# File ../lib/roller.rb, line 124
    def self.process
      options = OpenStruct.new

      processing = false
      exporting = false

      option_parser = OptionParser.new do |opts|
        opts.banner = '-' * 80 +
          "\n#{APP_NAME} #{APP_VERSION}\n" +
          "by #{AUTHOR_NAME} (#{AUTHOR_CONTACT})\n" +
          '-' * 80 +
          "\n\nUsage:\n  #{File.basename(RUBYSCRIPT2EXE.executable)} WRPFILE [options]"

        opts.separator ''
        opts.separator 'Replacing objects and textures:'

        options.replace_forests = []
        desc = 'Replace OFP forest block objects with individual trees and bushes, according to comma-separated list of YAML files (occurs after object replacement)'
        opts.on('-F', '--replace-forests A,B,C', Array, *desc.wrap(DESC_WIDTH)) do |o|
          options.replace_forests = o
          processing = true
        end

        options.replace_objects = []
        desc = 'Replace objects according to comma-separated list of YAML files (occurs before forest replacement)'
        opts.on('-o', '--replace-objects A,B,C', Array, *desc.wrap(DESC_WIDTH)) do |o|
          options.replace_objects = o
          processing = true
        end

        options.replace_textures = []
        desc = 'Replace textures according to comma-separated list of YAML files'
        opts.on('-t', '--replace-textures A,B,C', Array, *desc.wrap(DESC_WIDTH)) do |o|
          options.replace_textures = o
          processing = true
        end

        opts.separator ''
        opts.separator 'Altering the terrain:'

        options.terrain_cell_size = nil
        desc = 'Set new size of terrain cells (metres). This must give a legal grid size of 16x16 to 4096x4096 cells (e.g. for an island imported from OFP, which has 50m cells, you could resize to 800, 400, 200, 100, 25, 12.5, 6.25 or 3.125).'
        opts.on('-c', '--terrain-cell-size N', Float, *desc.wrap(DESC_WIDTH)) do |o|
          options.terrain_cell_size = o
          processing = true
        end

        options.terrain_bumpiness = nil
        desc = 'Maximum amount of vertical distortion of new grid points added by terrain-cell-size (metres). Applied after terrain cell resizing, but before other changes.'
        opts.on('-b', '--terrain-bumpiness N', Float, *desc.wrap(DESC_WIDTH)) do |o|
          options.terrain_bumpiness = o
          processing = true
        end

        options.raise_sea_level = nil
        desc = 'Move sea level up by this distance (move sea level down if negative). Done before edge-height is set.'
        opts.on('-r', '--raise-sea-level N', Float, *desc.wrap(DESC_WIDTH)) do |o|
          options.raise_sea_level = o
          processing = true
        end

        options.edge_height = nil
        desc = 'Sets the height of all terrain points around the edge to a specific value. This is applied after cell, bumpiness and sea-level changes.'
        opts.on('-e', '--edge-height N', Float, *desc.wrap(DESC_WIDTH)) do |o|
          options.edge_height = o
          processing = true
        end

        opts.separator ''
        opts.separator 'Generating images:'

        options.satellite_mask = nil
        desc = "Export a satellite mask PNG image (FILE defaults to 'WRPFILE#{DEFAULT_EXPORT_MASK}')"
        opts.on('-m', '--satellite-mask [FILE]', *desc.wrap(DESC_WIDTH)) do |o|
          options.satellite_mask = if o.nil?
            DEFAULT_EXPORT_MASK
          else
            o
          end
          exporting = true
        end

        options.satellite_cell_size = DEFAULT_SATELLITE_CELL_SIZE
        desc = "Size of satellite cells, i.e. pixels, in metres (defaults to #{DEFAULT_SATELLITE_CELL_SIZE}m if this option is omitted')"
        opts.on('-C', '--satellite-cell-size N', Float, *desc.wrap(DESC_WIDTH)) do |o|
          options.satellite_cell_size = o
        end
        
        opts.separator ''
        opts.separator 'Exporting files:'

        options.export_objects = nil
        desc = "Export BIS objects file (FILE defaults to 'WRPFILE#{DEFAULT_EXPORT_OBJECTS}')"
        opts.on('-O' ,'--objects [FILE]', *desc.wrap(DESC_WIDTH)) do |o|
          options.export_objects = if o.nil?
            DEFAULT_EXPORT_OBJECTS
          else
            o
          end
          exporting = true
        end

        options.export_unique_objects = nil
        desc = "Export a list of objects used in the WRP (FILE defaults to 'WRPFILE#{DEFAULT_EXPORT_UNIQUE_OBJECTS}')"
        opts.on('-u' ,'--unique-objects [FILE]', *desc.wrap(DESC_WIDTH)) do |o|
          options.export_unique_objects = if o.nil?
            DEFAULT_EXPORT_UNIQUE_OBJECTS
          else
            o
          end
          exporting = true
        end

        options.export_wrp = nil
        desc = "Export WRP file in 8WVR format, regardless of which format it was imported as (FILE defaults to 'WRPFILE#{DEFAULT_EXPORT_WRP}')"
        opts.on('-w', '--wrp [FILE]', *desc.wrap(DESC_WIDTH)) do |o|
          options.export_wrp = if o.nil?
            DEFAULT_EXPORT_WRP
          else
            o
          end
          exporting = true
        end

        options.export_xyz = nil
        desc = "Export XYZ file (FILE defaults to 'WRPFILE#{DEFAULT_EXPORT_XYZ}')"
        opts.on('-x', '--xyz [FILE]', *desc.wrap(DESC_WIDTH)) do |o|
          options.export_xyz = if o.nil?
            DEFAULT_EXPORT_XYZ
          else
            o
          end
          exporting = true
        end

        opts.separator ''
        opts.separator 'Common options:'

        options.show_progress = false
        desc = 'Show the graphical progress bar (which will slow down all import, export and processing slightly).'
        opts.on('-p', '--show-progress', *desc.wrap(DESC_WIDTH)) do
          options.show_progress = true
        end
        
        options.force_overwrite = false
        desc = 'Forces overwriting of existing output files (defaults to requesting user confirmation of overwrites)'
        opts.on('-f', '--force-overwrite', *desc.wrap(DESC_WIDTH)) do |o|
          options.force_overwrite = o
        end

        opts.on_tail('-?', '--help', 'Display this message') do |o|
          puts opts.help
          return true
        end
      end

      begin
        option_parser.parse!
      rescue => error
        puts error.message
        option_parser.help
        return false
      end
      
      if ARGV.size == 0
        puts "Need to specify a WRP file to import"
        puts option_parser.help
        return false
      elsif ARGV.size > 1
        puts "Too many arguments"
        puts option_parser.help
        return false
      end

      filename_in = ARGV.first

      # Add WRP extension if missing.
      base_name = filename_in.gsub(/#{File.extname(filename_in)}$/,'')
      filename_in += EXT_WRP if File.extname(filename_in).empty?

      start = Time.now
 
      puts '--- Importing ---'

      # Load WRP
      file8wvr = File.timed_open(filename_in, "rb") do |file|
        File8WVR.new(file, options.show_progress)
      end

      puts
      puts file8wvr.info
      puts
      
      # Process WRP
      if exporting
        puts '--- Processing ---'
      end
      
      # Resize the terrain grid size.
      unless options.terrain_cell_size.nil?       
        print "Resizing terrain cell size from #{file8wvr.terrain_cell_size}m to #{options.terrain_cell_size}m..."
        timed_block do
          file8wvr.resize_terrain_grid(options.terrain_cell_size)
        end
      end

      unless options.terrain_bumpiness.nil?
        print "Applying up to #{options.terrain_bumpiness}m bumps..."
        timed_block do
          file8wvr.apply_bumpiness(options.terrain_bumpiness)
        end
      end

      # Set height along all four edges.
      unless options.raise_sea_level.nil?
        print "Altering sea level by #{if options.raise_sea_level >= 0 then '+' end}#{options.raise_sea_level}m..."
        timed_block do
          file8wvr.raise_sea_level(options.raise_sea_level)
        end
      end

      # Set height along all four edges.
      unless options.edge_height.nil?
        print "Setting terrain height along all edges to #{options.edge_height}m..."
        timed_block do
          file8wvr.set_edge_height(options.edge_height)
        end
      end

      # Replace objects.
      options.replace_objects.each do |filename|
        filename = find_config_file(filename)
        return false if filename.nil?
        print 'Replacing objects: '
        File.timed_open(filename, "r") do |file|
          num_replacements = file8wvr.replace_objects(YAML::load(file))
          print "replaced #{num_replacements} objects..."
        end
      end

      # Replace textures.
      options.replace_textures.each do |filename|
        filename = find_config_file(filename)
        return false if filename.nil?
        print 'Replacing textures: '
        File.timed_open(filename, "r") do |file|
          num_replacements = file8wvr.replace_textures(YAML::load(file))
          print "replaced #{num_replacements} textures..."
        end
      end

      # Replace blocks of trees.
      options.replace_forests.each do |filename|
        filename = find_config_file(filename)
        return false if filename.nil?
        print 'Replacing forests: '
        File.timed_open(filename, "r") do |file|
          num_blocks, num_models = file8wvr.replace_forests(YAML::load(file))
          print "replaced #{num_blocks} forest blocks with #{num_models} trees and bushes..."
        end
      end

      # Output the updated island info.
      if processing
        puts
        puts file8wvr.info
        puts
      end

      # EXPORT

      if exporting
        puts '--- Exporting ---'
      end

      # Export WRP file.
      unless options.export_wrp.nil?
        file_name = export_file_name(options.export_wrp, base_name, DEFAULT_EXPORT_WRP)

        if options.force_overwrite or confirm_overwrite(file_name)

          print "WRP export: "
          if file_name == filename_in
            puts "Not overwriting #{filename_in} to export WRP!"
            return false
          end

          File.timed_open(file_name, "wb") do |file|
            file8wvr.write_wrp(file)
          end

          if FileUtils.compare_file(filename_in, file_name)
            puts "Input and output WRP files are identical"
          end
        end
      end

      # Export XYZ data.
      unless options.export_xyz.nil?
        file_name = export_file_name(options.export_xyz, base_name, DEFAULT_EXPORT_XYZ)

        if options.force_overwrite or confirm_overwrite(file_name)
          print "XYZ export: "
          File.timed_open(file_name, "w") do |file|
            num_points = file8wvr.write_xyz(file)
            print "#{num_points} positions..."
          end
        end
      end

      # Export object template.
      unless options.export_objects.nil?
        file_name = export_file_name(options.export_objects, base_name, DEFAULT_EXPORT_OBJECTS)

        if options.force_overwrite or confirm_overwrite(file_name)
          print "Object export: "
          File.timed_open(file_name, "w") do |file|
            num_objects = file8wvr.write_object_template(file)
            print "#{num_objects} objects..."
          end
        end
      end

      # Export list of unique objects.
      unless options.export_unique_objects.nil?
        file_name = export_file_name(options.export_unique_objects, base_name, DEFAULT_EXPORT_UNIQUE_OBJECTS)

        if options.force_overwrite or confirm_overwrite(file_name)
          print "Unique objects export: "
          File.timed_open(file_name, "w") do |file|
            object_names = file8wvr.object_names
            object_names.each do |object|
              file.puts object.downcase
            end
            print "#{object_names.size} objects..."
          end
        end
      end

      # Export satellite mask.
      unless options.satellite_mask.nil?
        image_file_name = export_file_name(options.satellite_mask, base_name, DEFAULT_EXPORT_MASK)

        if options.force_overwrite or confirm_overwrite(image_file_name)
          print 'Satellite-mask export...'

          timed_block do
            # TODO: Specify this file, rather than always use same one..
            config_file_name = find_config_file('mask.yaml')
            return false if config_file_name.nil?
            config = File.open(config_file_name, 'r') do |file|
              YAML::load(file)
            end

            file8wvr.export_satellite_mask(image_file_name, options.satellite_cell_size, config)
          end
        end
      end

      puts
      puts "#{APP_NAME} completed in #{format_time(Time.now - start)}"

      true
    end