rvpacker-txt 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be60b9ca7d30b5ba65f22b5c71b2051cddc357d0793e5040bddddb4b940f7aec
4
- data.tar.gz: a62fc90e814c94a9eeb0d56299c6a9211311263589d6d55b0ef5367a74a281ae
3
+ metadata.gz: 2b69421e26d7a2a85ce60f482712d941e23946887a3e9b8640752dd4441b4cea
4
+ data.tar.gz: 66fdd2d16bbd06ae1210105684805c9ec4a2beefef7f859568ae1faa37f6051e
5
5
  SHA512:
6
- metadata.gz: 58e86a866916217af89835096794ecdcaef8803eadc695f24347790179e5e3fda42510d5ce968a904e6d86e84c88602cbbd7fdf7411d2294b253c2a4d225b63b
7
- data.tar.gz: 4a6b9e66cc383defab76c9d6216afdfcc45f16de09530ebec498b2401faf4fac16dd3d1aa1843ecc6992585116f22f750f123a9df4d76dd1e1cacbede6ca2705
6
+ metadata.gz: '068699415ee5fe5faa764a20f66f60b6d359610503aff909dda3b62b42f78c83abfe7e5049cc8aab03b7c4e5f3ff3e77a879ff43f6a059ad021bb97beed0d74d'
7
+ data.tar.gz: e78c574fcf21bef8a4216f856ceeea67481281fe20047e78fd7a007c2b274e2e525a5a84a98c7bfcc1db8e4113c11b71837f6cd3f756002fde0c1ad4321811e3
data/Gemfile CHANGED
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
- # Specify your gem's dependencies in rvpacker-txt.gemspec
4
5
  gemspec
5
6
  gem 'rubocop', group: 'development', require: false
data/README.md CHANGED
@@ -5,9 +5,9 @@ port of Ruby 1.9.x's rvpacker to Ruby 3.x to use .txt files instead of YAML, and
5
5
 
6
6
  rvpacker consists of 3 parts:
7
7
 
8
- * RPG library (stub classes for serialization of RPGMaker game data)
9
- * RGSS library (some more classes for RPGMaker serialization)
10
- * rvpacker-txt (the script you call on the frontend)
8
+ * classes.rb library - all necessary classes to properly load and dump RPG Maker files
9
+ * read.rb library - all necessary functions for reading and parsing RPG Maker files to text
10
+ * write.rb library - all necessary functions for writing parsed RPG Maker files back to their initial form.
11
11
 
12
12
  # Installation
13
13
 
@@ -15,8 +15,9 @@ rvpacker consists of 3 parts:
15
15
  $ gem install rvpacker-txt
16
16
  ```
17
17
 
18
- Usage
19
- =====
18
+ # Usage
19
+
20
+ You can get a help message on usage using `rvpacker-txt -h`.
20
21
 
21
22
  ```
22
23
  $ rvpacker-txt -h
@@ -28,15 +29,17 @@ COMMANDS:
28
29
  read - Parses RPG Maker game files to .txt
29
30
  write - Writes parsed files back to their initial form
30
31
  OPTIONS:
31
- -d, --input-dir DIRECTORY Input directory of RPG Maker project.
32
- Must contain "Data" or "original" folder to read,
33
- and additionally "translation" with "maps" and "other" subdirectories to write.
34
- --no Don't process specified files.
35
- Takes multiple values separated by a comma.
36
- Allowed values: maps, other, system, plugins
37
- -s, --shuffle NUMBER At value 1: Shuffles all lines in strings, at value 2: shuffles all lines and words in strings.
38
- -l, --log Log information while processing.
39
- -h, --help Show help message.
32
+ -i, --input-dir DIR Input directory of RPG Maker project
33
+ -o, --output-dir DIR Output directory of parsed/written files
34
+ --disable-processing FILES Don't process specified files (maps, other, system, plugins)
35
+ -s, --shuffle NUM Shuffle level (1: lines, 2: lines and words)
36
+ --disable_custom_processing Disables built-in custom parsing/writing for some games
37
+ -l, --log Log information while processing
38
+ -f, --force Force rewrite all files. Cannot be used with --append.
39
+ USE WITH CAUTION!
40
+ -a, --append When you update the rvpacker-txt, you probably should re-read your files with append, as some new text might be added to parser.
41
+ Cannot be used with --force
42
+ -h, --help Show help message
40
43
  ```
41
44
 
42
45
  For example, to read a RPG Maker VX Ace project in E:/Documents/RPGMakerGame to .txt files:
@@ -49,7 +52,8 @@ Program determines game engine automatically.
49
52
 
50
53
  This will parse all text from Data/* files into translation/maps and translation/other directories as files without
51
54
  _trans postfix that contain original text and files with _trans postfix that contain empty lines for translation.
52
- Lines from Scripts file will be parsed into translation/other/scripts.txt file as plain text.
55
+ Lines from Scripts file will be parsed into translation/other/scripts.txt file as plain text, and
56
+ also into a scripts_plain.txt file that contains scripts contents as a whole - that's just for convenience.
53
57
 
54
58
  To write previously parsed project back to its initial form:
55
59
 
@@ -80,4 +84,4 @@ https://github.com/ymaxkrapzv/rvpacker
80
84
 
81
85
  Darkness9724 forked rvpacker to rvpacker-ng, ported it to Ruby 3.x and updated dependencies.
82
86
 
83
- https://gitlab.com/Darkness9724/rvpacker-ng
87
+ https://gitlab.com/Darkness9724/rvpacker-ng
data/bin/rvpacker-txt CHANGED
@@ -1,88 +1,159 @@
1
1
  #!/usr/bin/env ruby
2
- require 'classes'
2
+ # frozen_string_literal: true
3
+
3
4
  require 'optparse'
5
+ require 'fileutils'
6
+
7
+ require 'classes'
8
+ require 'read'
9
+ require 'write'
10
+
11
+ def self.parse_options
12
+ options = { disable_processing: Array.new(4, false) }
13
+
14
+ OptionParser.new do |cmd|
15
+ 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"
16
+
17
+ cmd.on('-i', '--input-dir DIR', String, 'Input directory of RPG Maker project') do |dir|
18
+ options[:input_dir] = File.exist?(dir) ? File.realpath(dir) : (raise "#{dir} not found")
19
+ options[:output_dir] = options[:input_dir]
20
+ end
21
+
22
+ cmd.on('-o', '--output-dir DIR', String, 'Output directory of parsed/written files') do |dir|
23
+ options[:output_dir] = File.exist?(dir) ? File.realpath(dir) : (raise "#{dir} not found")
24
+ end
4
25
 
5
- $logging = false
6
- $shuffle = 0
7
- $no = [false, false, false, false] # 0 is whether to NOT process maps, 1 is other, 2 is system, 3 is scripts
8
- $disable_custom_parsing = false
9
-
10
- options = {}
11
- OptionParser.new do |command|
12
- 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"
13
-
14
- command.on('-d',
15
- '--input-dir DIRECTORY',
16
- 'Input directory of RPG Maker project.',
17
- 'Must contain "Data" or "original" folder to read,',
18
- 'and additionally "translation" with "maps" and "other" subdirectories to write.') { |dir| options[:input_dir] = dir }
19
-
20
- command.on('--no',
21
- "Don't process specified files.",
22
- 'Takes multiple values separated by a comma.',
23
- 'Allowed values: maps, other, system, plugins') do |files|
24
- actual_files = files.split(',')
25
-
26
- actual_files.each do |file|
27
- case file
28
- when "maps"
29
- $no[0] = true
30
- when "other"
31
- $no[1] = true
32
- when "system"
33
- $no[2] = true
34
- when "scripts"
35
- $no[3] = true
36
- else
37
- puts "Wrong value for no argument: #{file}.\nAllowed values: maps, other, system, plugins"
38
- exit
26
+ cmd.on('--disable-processing FILES', Array, 'Don\'t process specified files (maps, other, system, plugins)') do |files|
27
+ files.each do |file|
28
+ index = %w[maps other system scripts].index(file)
29
+ options[:disable_processing][index] = true if index
39
30
  end
40
31
  end
41
- end
42
32
 
43
- command.on('-s',
44
- '--shuffle NUMBER',
45
- 'At value 1: Shuffles all lines in strings, at value 2: shuffles all lines and words in strings.') { |number| $shuffle = number }
33
+ cmd.on('-s', '--shuffle NUM', Integer, 'Shuffle level (1: lines, 2: lines and words)') do |num|
34
+ options[:shuffle_level] = num
35
+ end
36
+
37
+ cmd.on('--disable_custom_processing', 'Disables built-in custom parsing/writing for some games') do
38
+ options[:disable_custom_processing] = true
39
+ end
40
+
41
+ cmd.on('-l', '--log', 'Log information while processing') do
42
+ options[:logging] = true
43
+ end
46
44
 
47
- command.on('--disable-custom-parsing',
48
- 'Disables built-in custom parsing for some games, which may improperly parse/write some games.')
45
+ cmd.on('-f', '--force', 'Force rewrite all files. Cannot be used with --append.', 'USE WITH CAUTION!') do
46
+ options[:force] = true
47
+ end
49
48
 
50
- command.on('-l',
51
- '--log',
52
- 'Log information while processing.') { $logging = true }
49
+ cmd.on('-a', '--append', 'When you update the rvpacker-txt, you probably should re-read your files with append, as some new text might be added to parser.', 'Cannot be used with --force') do
50
+ raise '--append cannot be used beside --force.' if options[:force]
51
+ options[:append] = true
52
+ end
53
53
 
54
- command.on_tail('-h',
55
- '--help',
56
- 'Show help message.') { puts command; exit }
57
- end.parse!(ARGV)
54
+ cmd.on('-h', '--help', 'Show help message') do
55
+ puts cmd
56
+ exit
57
+ end
58
+ end.parse!
59
+
60
+ options[:action] = ARGV.shift
61
+ raise 'COMMAND argument is required. Use rvpacker-txt -h for help.' if options[:action].nil?
62
+ raise 'Invalid command. Allowed commands are: read, write.' unless %w[read write].include?(options[:action])
58
63
 
59
- if ARGV.empty?
60
- puts 'COMMAND argument is required. Use rvpacker-txt -h for help.'
61
- exit
64
+ options
62
65
  end
63
66
 
64
- options[:action] = ARGV.shift
67
+ def self.get_game_type(system_file_path)
68
+ object = Marshal.load(File.binread(system_file_path))
69
+ game_title = object.instance_variable_get(:@game_title).to_s.downcase
70
+ game_title.include?('lisa') ? 'lisa' : nil
71
+ end
65
72
 
66
- unless %w[read write].include?(options[:action])
67
- puts 'Invalid command. Allowed commands are: read, write.'
68
- exit
73
+ start_time = Time.now
74
+
75
+ options = parse_options
76
+ input_dir = options[:input_dir]
77
+ output_dir = options[:output_dir]
78
+ disable_custom_processing = options[:disable_custom_processing]
79
+ shuffle_level = options[:shuffle_level]
80
+ logging = options[:logging]
81
+ disable_processing = options[:disable_processing]
82
+ force = options[:force]
83
+ append = options[:append]
84
+
85
+ extensions = { xp: '.rxdata', vx: 'rvdata', ace: 'rvdata2' }
86
+
87
+ original_directory = Dir.glob(File.join(input_dir, '{data,original}'), File::FNM_CASEFOLD).first
88
+ raise '"Data" or "original" directory not found within input directory.' unless original_directory
89
+
90
+ paths = {
91
+ original_path: original_directory,
92
+ translation_path: File.join(input_dir, 'translation'),
93
+ maps_path: File.join(input_dir, 'translation', 'maps'),
94
+ other_path: File.join(input_dir, 'translation', 'other'),
95
+ output_path: File.join(output_dir, 'output')
96
+ }
97
+
98
+ paths.each_value { |path| FileUtils.mkdir_p(path) }
99
+
100
+ engine = extensions.find do |symbol, extension|
101
+ symbol if File.exist?(File.join(paths[:original_path], "System.#{extension}"))
102
+ end || (raise "Couldn't determine project engine.")
103
+
104
+ files = Dir.glob("#{paths[:original_path]}/*#{extensions[engine]}")
105
+
106
+ maps_files_paths = []
107
+ other_files_paths = []
108
+ system_file_path = nil
109
+ scripts_file_path = nil
110
+
111
+ files.each do |file|
112
+ basename = File.basename(file)
113
+
114
+ if basename.start_with?(/Map[0-9]/)
115
+ maps_files_paths.push(file)
116
+ elsif !basename.start_with?(/Map|Tilesets|Animations|System|Scripts|Areas/)
117
+ other_files_paths.push(file)
118
+ elsif basename.start_with?('System')
119
+ system_file_path = file
120
+ elsif basename.start_with?('Scripts')
121
+ scripts_file_path = file
122
+ end
69
123
  end
70
124
 
71
- directory = options[:input_dir]
72
- raise "#{directory} not found" unless File.exist?(directory)
73
- directory = File.realpath(directory)
125
+ ini_file_path = File.join(input_dir, "Game.ini")
126
+
127
+ game_type = disable_custom_processing ? nil : get_game_type(system_file_path)
128
+
129
+ wait_time = 0
130
+ processing_type = if force
131
+ wait_time_start = Time.now
132
+
133
+ puts "WARNING! You\'re about to forcefully rewrite all your translation files, including _trans files.\nIf you really want to do it, make sure you've made a backup of your _trans files, if you made some changes in them already.\nInput 'Y' to continue."
134
+ exit unless gets.chomp == 'Y'
74
135
 
75
- original_directory = Dir.foreach(directory).find { |dirname| dirname.downcase == 'original' || dirname.downcase == 'data' }
76
- raise '"Data" or "original" directory not found within input directory.' if original_directory.nil?
136
+ wait_time = Time.now - wait_time_start
137
+ 'force'
138
+ else
139
+ append ? 'append' : 'default'
140
+ end
141
+
142
+ puts 'Custom processing for this game is enabled. Use --disable-custom-processing to disable it.' unless game_type.nil?
143
+
144
+ if options[:action] == 'read'
145
+ read_map(maps_files_paths, paths[:maps_path], logging, game_type, processing_type) unless disable_processing[0]
146
+ read_other(other_files_paths, paths[:other_path], logging, game_type, processing_type) unless disable_processing[1]
147
+ read_system(system_file_path, ini_file_path, paths[:other_path], logging, processing_type) unless disable_processing[2]
148
+ read_scripts(scripts_file_path, paths[:other_path], logging, processing_type) unless disable_processing[3]
149
+ else
150
+ write_map(maps_files_paths, paths[:maps_path], paths[:output_path], shuffle_level, logging, game_type, processing_type) unless disable_processing[0]
151
+ write_other(other_files_paths, paths[:other_path], paths[:output_path], shuffle_level, logging, game_type, processing_type) unless disable_processing[1]
152
+ write_system(system_file_path, ini_file_path, paths[:other_path], paths[:output_path], shuffle_level, logging, processing_type) unless disable_processing[2]
153
+ write_scripts(scripts_file_path, paths[:other_path], paths[:output_path], logging, processing_type) unless disable_processing[3]
154
+ end
77
155
 
78
- engine = if File.exist?(File.join(directory, original_directory, 'System.rxdata'))
79
- :xp
80
- elsif File.exist?(File.join(directory, original_directory, 'System.rvdata'))
81
- :vx
82
- elsif File.exist?(File.join(directory, original_directory, 'System.rvdata2'))
83
- :ace
84
- else
85
- raise "Couldn't determine project engine."
86
- end
156
+ $wait_time = 0 if $wait_time.nil?
157
+ end_time = Time.now - start_time - wait_time - $wait_time
87
158
 
88
- RGSS.serialize(engine, options[:action], directory, original_directory,)
159
+ puts "Done in #{end_time}"
data/lib/classes.rb CHANGED
@@ -22,9 +22,7 @@ class Table
22
22
  def initialize(bytes)
23
23
  @dim, @x, @y, @z, items, *@data = bytes.unpack('L5 S*')
24
24
 
25
- unless items == @data.length && @x * @y * @z == items
26
- raise 'Size mismatch loading Table from data'
27
- end
25
+ raise 'Size mismatch loading Table from data' unless items == @data.length && @x * @y * @z == items
28
26
  end
29
27
 
30
28
  def _dump(*_ignored)
@@ -32,7 +30,7 @@ class Table
32
30
  end
33
31
 
34
32
  def self._load(bytes)
35
- Table.new(bytes)
33
+ new(bytes)
36
34
  end
37
35
  end
38
36
 
@@ -46,7 +44,7 @@ class Color
46
44
  end
47
45
 
48
46
  def self._load(bytes)
49
- Color.new(bytes)
47
+ new(bytes)
50
48
  end
51
49
  end
52
50
 
@@ -60,7 +58,7 @@ class Tone
60
58
  end
61
59
 
62
60
  def self._load(bytes)
63
- Tone.new(bytes)
61
+ new(bytes)
64
62
  end
65
63
  end
66
64
 
@@ -74,7 +72,44 @@ class Rect
74
72
  end
75
73
 
76
74
  def self._load(bytes)
77
- Rect.new(bytes)
75
+ new(bytes)
76
+ end
77
+ end
78
+
79
+ # Fuck using an array with set, that's just straight dumb and not efficient
80
+ class IndexSet
81
+ def initialize
82
+ @hash = Hash.new
83
+ end
84
+
85
+ def add(item)
86
+ return if @hash.include?(item)
87
+ @hash[item] = hash.size
88
+ @hash
89
+ end
90
+
91
+ def include?(item)
92
+ @hash.include?(item)
93
+ end
94
+
95
+ def each(&block)
96
+ @hash.each_key(&block)
97
+ end
98
+
99
+ def to_a
100
+ @hash.dup
101
+ end
102
+
103
+ def join(delimiter = '')
104
+ @hash.keys.join(delimiter)
105
+ end
106
+
107
+ def length
108
+ @hash.size
109
+ end
110
+
111
+ def empty?
112
+ @hash.empty?
78
113
  end
79
114
  end
80
115
 
@@ -146,5 +181,3 @@ module RGSS
146
181
  process(Object, *symbol_array)
147
182
  end
148
183
  end
149
-
150
- require 'serialize'