bin/rvpacker-txt in rvpacker-txt-1.3.1 vs bin/rvpacker-txt in rvpacker-txt-1.4.0

- old
+ new

@@ -1,88 +1,130 @@ #!/usr/bin/env ruby -require 'classes' +# frozen_string_literal: true + require 'optparse' +require 'fileutils' -$logging = false -$shuffle = 0 -$no = [false, false, false, false] # 0 is whether to NOT process maps, 1 is other, 2 is system, 3 is scripts -$disable_custom_parsing = false +require 'classes' +require 'read' +require 'write' -options = {} -OptionParser.new do |command| - command.banner = "This tool allows to parse RPG Maker project to .txt files and back.\n\nUsage: rvpacker-txt COMMAND [options]\n\nCOMMANDS:\n read - Parses RPG Maker game files to .txt\n write - Writes parsed files back to their initial form\nOPTIONS:\n" +def self.parse_options + options = { disable_processing: Array.new(4, false) } - command.on('-d', - '--input-dir DIRECTORY', - 'Input directory of RPG Maker project.', - 'Must contain "Data" or "original" folder to read,', - 'and additionally "translation" with "maps" and "other" subdirectories to write.') { |dir| options[:input_dir] = dir } + OptionParser.new do |cmd| + cmd.banner = "This tool allows to parse RPG Maker project to .txt files and back.\n\nUsage: rvpacker-txt COMMAND [options]\n\nCOMMANDS:\n read - Parses RPG Maker game files to .txt\n write - Writes parsed files back to their initial form\nOPTIONS:\n" - command.on('--no', - "Don't process specified files.", - 'Takes multiple values separated by a comma.', - 'Allowed values: maps, other, system, plugins') do |files| - actual_files = files.split(',') + cmd.on('-i', '--input-dir DIR', String, 'Input directory of RPG Maker project') do |dir| + options[:input_dir] = File.exist?(dir) ? File.realpath(dir) : (raise "#{dir} not found") + options[:output_dir] = options[:input_dir] + end - actual_files.each do |file| - case file - when "maps" - $no[0] = true - when "other" - $no[1] = true - when "system" - $no[2] = true - when "scripts" - $no[3] = true - else - puts "Wrong value for no argument: #{file}.\nAllowed values: maps, other, system, plugins" - exit + cmd.on('-o', '--output-dir DIR', String, 'Output directory of parsed/written files') do |dir| + options[:output_dir] = File.exist?(dir) ? File.realpath(dir) : (raise "#{dir} not found") + end + + cmd.on('--disable-processing FILES', Array, 'Don\'t process specified files (maps, other, system, plugins)') do |files| + files.each do |file| + index = %w[maps other system scripts].index(file) + options[:disable_processing][index] = true if index end end - end - command.on('-s', - '--shuffle NUMBER', - 'At value 1: Shuffles all lines in strings, at value 2: shuffles all lines and words in strings.') { |number| $shuffle = number } + cmd.on('-s', '--shuffle NUM', Integer, 'Shuffle level (1: lines, 2: lines and words)') do |num| + options[:shuffle_level] = num + end - command.on('--disable-custom-parsing', - 'Disables built-in custom parsing for some games, which may improperly parse/write some games.') + cmd.on('--disable-custom-parsing', 'Disables built-in custom parsing for some games') do + options[:disable_custom_parsing] = true + end - command.on('-l', - '--log', - 'Log information while processing.') { $logging = true } + cmd.on('-l', '--log', 'Log information while processing') do + options[:logging] = true + end - command.on_tail('-h', - '--help', - 'Show help message.') { puts command; exit } -end.parse!(ARGV) + cmd.on('-h', '--help', 'Show help message') do + puts cmd + exit + end + end.parse! -if ARGV.empty? - puts 'COMMAND argument is required. Use rvpacker-txt -h for help.' - exit + options[:action] = ARGV.shift + raise 'COMMAND argument is required. Use rvpacker-txt -h for help.' if options[:action].nil? + raise 'Invalid command. Allowed commands are: read, write.' unless %w[read write].include?(options[:action]) + + options end -options[:action] = ARGV.shift +def self.get_game_type(system_file_path, disable_custom_parsing) + return nil if disable_custom_parsing -unless %w[read write].include?(options[:action]) - puts 'Invalid command. Allowed commands are: read, write.' - exit + object = Marshal.load(File.binread(system_file_path)) + game_title = object.instance_variable_get(:@game_title).to_s.downcase + game_title.include?('lisa') ? 'lisa' : nil end -directory = options[:input_dir] -raise "#{directory} not found" unless File.exist?(directory) -directory = File.realpath(directory) +start_time = Time.now -original_directory = Dir.foreach(directory).find { |dirname| dirname.downcase == 'original' || dirname.downcase == 'data' } -raise '"Data" or "original" directory not found within input directory.' if original_directory.nil? +options = parse_options +input_dir = options[:input_dir] +output_dir = options[:output_dir] +disable_custom_parsing = options[:disable_custom_parsing] +shuffle_level = options[:shuffle_level] +logging = options[:logging] +disable_processing = options[:disable_processing] -engine = if File.exist?(File.join(directory, original_directory, 'System.rxdata')) - :xp - elsif File.exist?(File.join(directory, original_directory, 'System.rvdata')) - :vx - elsif File.exist?(File.join(directory, original_directory, 'System.rvdata2')) - :ace - else - raise "Couldn't determine project engine." - end +extensions = { xp: '.rxdata', vx: 'rvdata', ace: 'rvdata2' } -RGSS.serialize(engine, options[:action], directory, original_directory,) +original_directory = Dir.glob(File.join(input_dir, '{data,original}'), File::FNM_CASEFOLD).first +raise '"Data" or "original" directory not found within input directory.' unless original_directory + +paths = { + original_path: original_directory, + translation_path: File.join(input_dir, 'translation'), + maps_path: File.join(input_dir, 'translation', 'maps'), + other_path: File.join(input_dir, 'translation', 'other'), + output_path: File.join(output_dir, 'output') +} + +paths.each_value { |path| FileUtils.mkdir_p(path) } + +engine = extensions.find do |symbol, extension| + symbol if File.exist?(File.join(paths[:original_path], "System.#{extension}")) +end || (raise "Couldn't determine project engine.") + +files = Dir.glob("#{paths[:original_path]}/*#{extensions[engine]}") + +maps_files = [] +other_files = [] +system_file = nil +scripts_file = nil + +files.each do |file| + basename = File.basename(file) + + if basename.start_with?(/Map[0-9]/) + maps_files.push(file) + elsif !basename.start_with?(/Map|Tilesets|Animations|System|Scripts|Areas/) + other_files.push(file) + elsif basename.start_with?('System') + system_file = file + elsif basename.start_with?('Scripts') + scripts_file = file + end +end + +game_type = get_game_type(system_file, disable_custom_parsing) + +if options[:action] == 'read' + read_map(maps_files, paths[:maps_path], logging, game_type) unless disable_processing[0] + read_other(other_files, paths[:other_path], logging, game_type) unless disable_processing[1] + read_system(system_file, paths[:other_path], logging) unless disable_processing[2] + read_scripts(scripts_file, paths[:other_path], logging) unless disable_processing[3] +else + write_map(maps_files, paths[:maps_path], paths[:output_path], shuffle_level, logging, game_type) unless disable_processing[0] + write_other(other_files, paths[:other_path], paths[:output_path], shuffle_level, logging, game_type) unless disable_processing[1] + write_system(system_file, paths[:other_path], paths[:output_path], shuffle_level, logging) unless disable_processing[2] + write_scripts(scripts_file, paths[:other_path], paths[:output_path], logging) unless disable_processing[3] +end + +puts "Done in #{Time.now - start_time}" \ No newline at end of file