rvpacker-txt 1.4.0 → 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: 90b4214a5f31ca357f08bb57ab08a8fdfeeed37aff4674d73dc2c866bdb3f30e
4
- data.tar.gz: d35c5da8a2755b486ef5b98f9c9091f682b8d3143f3dde0f2adafa4348fc71e7
3
+ metadata.gz: 2b69421e26d7a2a85ce60f482712d941e23946887a3e9b8640752dd4441b4cea
4
+ data.tar.gz: 66fdd2d16bbd06ae1210105684805c9ec4a2beefef7f859568ae1faa37f6051e
5
5
  SHA512:
6
- metadata.gz: db1067185a2c07d44c96393d9d4b1c63bd27d424d946558e23a9bd4c6f8f6a9f4961cbddba5f8ce32ac5d1074d15aa2cc6cf64a45d3a10400ac753cc8380b26d
7
- data.tar.gz: 8a696a3a4cb22ac06425207218f8aedebbe1bc730fc36a474d589a6dd0d1b9c8320d270beb67235f7d75666caf35edc5fd7a83f5245ffe798aa76f50b29fa099
6
+ metadata.gz: '068699415ee5fe5faa764a20f66f60b6d359610503aff909dda3b62b42f78c83abfe7e5049cc8aab03b7c4e5f3ff3e77a879ff43f6a059ad021bb97beed0d74d'
7
+ data.tar.gz: e78c574fcf21bef8a4216f856ceeea67481281fe20047e78fd7a007c2b274e2e525a5a84a98c7bfcc1db8e4113c11b71837f6cd3f756002fde0c1ad4321811e3
data/README.md CHANGED
@@ -29,11 +29,16 @@ COMMANDS:
29
29
  read - Parses RPG Maker game files to .txt
30
30
  write - Writes parsed files back to their initial form
31
31
  OPTIONS:
32
- -d, --input-dir DIR Input directory of RPG Maker project
32
+ -i, --input-dir DIR Input directory of RPG Maker project
33
+ -o, --output-dir DIR Output directory of parsed/written files
33
34
  --disable-processing FILES Don't process specified files (maps, other, system, plugins)
34
35
  -s, --shuffle NUM Shuffle level (1: lines, 2: lines and words)
35
- --disable-custom-parsing Disables built-in custom parsing for some games
36
+ --disable_custom_processing Disables built-in custom parsing/writing for some games
36
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
37
42
  -h, --help Show help message
38
43
  ```
39
44
 
data/bin/rvpacker-txt CHANGED
@@ -34,14 +34,23 @@ def self.parse_options
34
34
  options[:shuffle_level] = num
35
35
  end
36
36
 
37
- cmd.on('--disable-custom-parsing', 'Disables built-in custom parsing for some games') do
38
- options[:disable_custom_parsing] = true
37
+ cmd.on('--disable_custom_processing', 'Disables built-in custom parsing/writing for some games') do
38
+ options[:disable_custom_processing] = true
39
39
  end
40
40
 
41
41
  cmd.on('-l', '--log', 'Log information while processing') do
42
42
  options[:logging] = true
43
43
  end
44
44
 
45
+ cmd.on('-f', '--force', 'Force rewrite all files. Cannot be used with --append.', 'USE WITH CAUTION!') do
46
+ options[:force] = true
47
+ end
48
+
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
+
45
54
  cmd.on('-h', '--help', 'Show help message') do
46
55
  puts cmd
47
56
  exit
@@ -55,9 +64,7 @@ def self.parse_options
55
64
  options
56
65
  end
57
66
 
58
- def self.get_game_type(system_file_path, disable_custom_parsing)
59
- return nil if disable_custom_parsing
60
-
67
+ def self.get_game_type(system_file_path)
61
68
  object = Marshal.load(File.binread(system_file_path))
62
69
  game_title = object.instance_variable_get(:@game_title).to_s.downcase
63
70
  game_title.include?('lisa') ? 'lisa' : nil
@@ -68,10 +75,12 @@ start_time = Time.now
68
75
  options = parse_options
69
76
  input_dir = options[:input_dir]
70
77
  output_dir = options[:output_dir]
71
- disable_custom_parsing = options[:disable_custom_parsing]
78
+ disable_custom_processing = options[:disable_custom_processing]
72
79
  shuffle_level = options[:shuffle_level]
73
80
  logging = options[:logging]
74
81
  disable_processing = options[:disable_processing]
82
+ force = options[:force]
83
+ append = options[:append]
75
84
 
76
85
  extensions = { xp: '.rxdata', vx: 'rvdata', ace: 'rvdata2' }
77
86
 
@@ -94,37 +103,57 @@ end || (raise "Couldn't determine project engine.")
94
103
 
95
104
  files = Dir.glob("#{paths[:original_path]}/*#{extensions[engine]}")
96
105
 
97
- maps_files = []
98
- other_files = []
99
- system_file = nil
100
- scripts_file = nil
106
+ maps_files_paths = []
107
+ other_files_paths = []
108
+ system_file_path = nil
109
+ scripts_file_path = nil
101
110
 
102
111
  files.each do |file|
103
112
  basename = File.basename(file)
104
113
 
105
114
  if basename.start_with?(/Map[0-9]/)
106
- maps_files.push(file)
115
+ maps_files_paths.push(file)
107
116
  elsif !basename.start_with?(/Map|Tilesets|Animations|System|Scripts|Areas/)
108
- other_files.push(file)
117
+ other_files_paths.push(file)
109
118
  elsif basename.start_with?('System')
110
- system_file = file
119
+ system_file_path = file
111
120
  elsif basename.start_with?('Scripts')
112
- scripts_file = file
121
+ scripts_file_path = file
113
122
  end
114
123
  end
115
124
 
116
- game_type = get_game_type(system_file, disable_custom_parsing)
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'
135
+
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?
117
143
 
118
144
  if options[:action] == 'read'
119
- read_map(maps_files, paths[:maps_path], logging, game_type) unless disable_processing[0]
120
- read_other(other_files, paths[:other_path], logging, game_type) unless disable_processing[1]
121
- read_system(system_file, paths[:other_path], logging) unless disable_processing[2]
122
- read_scripts(scripts_file, paths[:other_path], logging) unless disable_processing[3]
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]
123
149
  else
124
- write_map(maps_files, paths[:maps_path], paths[:output_path], shuffle_level, logging, game_type) unless disable_processing[0]
125
- write_other(other_files, paths[:other_path], paths[:output_path], shuffle_level, logging, game_type) unless disable_processing[1]
126
- write_system(system_file, paths[:other_path], paths[:output_path], shuffle_level, logging) unless disable_processing[2]
127
- write_scripts(scripts_file, paths[:other_path], paths[:output_path], logging) unless disable_processing[3]
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]
128
154
  end
129
155
 
130
- puts "Done in #{Time.now - start_time}"
156
+ $wait_time = 0 if $wait_time.nil?
157
+ end_time = Time.now - start_time - wait_time - $wait_time
158
+
159
+ puts "Done in #{end_time}"
data/lib/classes.rb CHANGED
@@ -77,7 +77,7 @@ class Rect
77
77
  end
78
78
 
79
79
  # Fuck using an array with set, that's just straight dumb and not efficient
80
- class IndexedSet
80
+ class IndexSet
81
81
  def initialize
82
82
  @hash = Hash.new
83
83
  end
data/lib/read.rb CHANGED
@@ -2,26 +2,40 @@
2
2
 
3
3
  require 'zlib'
4
4
 
5
+ def self.insert_at_index(hash, index, key, value)
6
+ return hash[key] = value if index >= hash.size
7
+
8
+ temp_hash = hash.to_a
9
+ temp_hash.insert(index, [key, value])
10
+ hash.clear
11
+ hash.merge!(temp_hash.to_h)
12
+ end
13
+
5
14
  def self.extract_quoted_strings(string)
6
- result = []
15
+ # Hash of string-index key-value pairs
16
+ result = {}
7
17
 
8
18
  skip_block = false
9
19
  in_quotes = false
10
20
  quote_type = nil
11
21
  buffer = []
12
22
 
13
- string.each_line(chomp: true) do |line|
14
- line.strip!
15
- next if line[0] == '#' || line.start_with?(/(Win|Lose)|_Fanfare/)
23
+ current_string_index = 0
24
+ string.each_line do |line|
25
+ stripped = line.strip
16
26
 
17
- skip_block = true if line.start_with?('=begin')
18
- skip_block = false if line.start_with?('=end')
27
+ if stripped[0] == '#' || stripped.start_with?(/(Win|Lose)|_Fanfare/)
28
+ next
29
+ end
30
+
31
+ skip_block = true if stripped.start_with?('=begin')
32
+ skip_block = false if stripped.start_with?('=end')
19
33
 
20
34
  next if skip_block
21
35
 
22
36
  buffer.push('\#') if in_quotes
23
37
 
24
- line.each_char do |char|
38
+ line.each_char.each_with_index do |char, index|
25
39
  if %w[' "].include?(char)
26
40
  unless quote_type.nil? || char == quote_type
27
41
  buffer.push(char)
@@ -30,7 +44,7 @@ def self.extract_quoted_strings(string)
30
44
 
31
45
  quote_type = char
32
46
  in_quotes = !in_quotes
33
- result.push(buffer.join)
47
+ result[buffer.join] = current_string_index + index
34
48
  buffer.clear
35
49
  next
36
50
  end
@@ -39,6 +53,8 @@ def self.extract_quoted_strings(string)
39
53
  buffer.push(char)
40
54
  end
41
55
  end
56
+
57
+ current_string_index += line.length
42
58
  end
43
59
 
44
60
  result
@@ -80,16 +96,54 @@ def self.parse_variable(variable, game_type)
80
96
  variable
81
97
  end
82
98
 
83
- def self.read_map(original_map_files, output_path, logging, game_type)
84
- maps_object_map = Hash[original_map_files.map do |filename|
99
+ def self.read_map(maps_files, output_path, logging, game_type, processing_type)
100
+ maps_output_path = File.join(output_path, 'maps.txt')
101
+ names_output_path = File.join(output_path, 'names.txt')
102
+ maps_trans_output_path = File.join(output_path, 'maps_trans.txt')
103
+ names_trans_output_path = File.join(output_path, 'names_trans.txt')
104
+
105
+ if processing_type == 'default' && (File.exist?(maps_trans_output_path) || File.exist?(names_trans_output_path))
106
+ puts 'maps.txt or maps_trans.txt file already exists. If you want to forcefully re-read all files, use --force flag, or --append if you want append new text to already existing files.'
107
+ return
108
+ end
109
+
110
+ maps_object_map = Hash[maps_files.map do |filename|
85
111
  [File.basename(filename), Marshal.load(File.binread(filename))]
86
112
  end]
87
113
 
88
- maps_lines = [IndexedSet.new, IndexedSet.new]
114
+ maps_lines = nil
115
+
116
+ maps_trans_map = nil
117
+ names_trans_map = nil
118
+
119
+ if processing_type == 'append'
120
+ if File.exist?(maps_trans_output_path)
121
+ maps_trans_map = Hash[File.readlines(maps_output_path, chomp: true).zip(File.readlines(maps_trans_output_path, chomp: true))]
122
+ names_trans_map = Hash[File.readlines(names_output_path, chomp: true).zip(File.readlines(names_trans_output_path, chomp: true))]
123
+ else
124
+ puts 'Files aren\'t already parsed. Continuing as if --append flag was omitted.'
125
+ processing_type = 'default'
126
+ maps_lines = [IndexSet.new, IndexSet.new]
127
+ end
128
+ else
129
+ maps_lines = [IndexSet.new, IndexSet.new]
130
+ end
131
+
132
+ hash_index = 0
133
+ names_hash_index = 0
89
134
 
90
135
  maps_object_map.each do |filename, object|
91
136
  display_name = object.instance_variable_get(:@display_name)
92
- maps_lines[1].add(display_name) if display_name.is_a?(String) && !display_name.empty?
137
+
138
+ if display_name.is_a?(String) && !display_name.empty?
139
+ if processing_type == 'append'
140
+ insert_at_index(names_trans_map, names_hash_index, display_name, '') unless names_trans_map.include?(display_name)
141
+ else
142
+ maps_lines[1].add(display_name)
143
+ end
144
+
145
+ names_hash_index += 1
146
+ end
93
147
 
94
148
  events = object.instance_variable_get(:@events)
95
149
  next if events.nil?
@@ -113,26 +167,56 @@ def self.read_map(original_map_files, output_path, logging, game_type)
113
167
  if code == 401
114
168
  if parameter.is_a?(String) && !parameter.empty?
115
169
  in_sequence = true
170
+
116
171
  parsed = parse_parameter(code, parameter, game_type)
117
172
  line.push(parsed) unless parsed.nil?
118
173
  end
119
174
  else
120
175
  if in_sequence
121
- maps_lines[0].add(line.join('\#'))
176
+ joined = line.join('\#')
177
+
178
+ if processing_type == 'append'
179
+ insert_at_index(maps_trans_map, hash_index, joined, '') unless maps_trans_map.include?(joined)
180
+ else
181
+ maps_lines[0].add(joined)
182
+ end
183
+
122
184
  line.clear
123
185
  in_sequence = false
186
+
187
+ hash_index += 1
124
188
  end
125
189
 
126
190
  if code == 102 && parameter.is_a?(Array)
127
191
  parameter.each do |subparameter|
128
192
  if subparameter.is_a?(String) && !subparameter.empty?
129
193
  parsed = parse_parameter(code, subparameter, game_type)
130
- maps_lines[0].add(parsed) unless parsed.nil?
194
+
195
+ unless parsed.nil?
196
+ if processing_type == 'append'
197
+ insert_at_index(maps_trans_map, hash_index, parsed, '') unless maps_trans_map.include?(parsed)
198
+ else
199
+ maps_lines[0].add(parsed)
200
+ end
201
+
202
+ hash_index += 1
203
+ end
131
204
  end
132
205
  end
133
206
  elsif code == 356 && parameter.is_a?(String) && !parameter.empty?
134
207
  parsed = parse_parameter(code, parameter, game_type)
135
- maps_lines[0].add(parsed.gsub(/\r?\n/, '\#')) unless parsed.nil?
208
+
209
+ unless parsed.nil?
210
+ subbed = parsed.gsub(/\r?\n/, '\#')
211
+
212
+ if processing_type == 'append'
213
+ insert_at_index(maps_trans_map, hash_index, parsed, '') unless maps_trans_map.include?(parsed)
214
+ else
215
+ maps_lines[0].add(subbed)
216
+ end
217
+
218
+ hash_index += 1
219
+ end
136
220
  end
137
221
  end
138
222
  end
@@ -143,14 +227,21 @@ def self.read_map(original_map_files, output_path, logging, game_type)
143
227
  puts "Parsed #{filename}" if logging
144
228
  end
145
229
 
146
- File.binwrite("#{output_path}/maps.txt", maps_lines[0].join("\n"))
147
- File.binwrite("#{output_path}/maps_trans.txt", "\n" * (maps_lines[0].empty? ? 0 : maps_lines[0].length - 1))
148
- File.binwrite("#{output_path}/names.txt", maps_lines[1].join("\n"))
149
- File.binwrite("#{output_path}/names_trans.txt", "\n" * (maps_lines[1].empty? ? 0 : maps_lines[1].length - 1))
230
+ if processing_type == 'append'
231
+ File.binwrite(maps_output_path, maps_trans_map.keys.join("\n"))
232
+ File.binwrite(maps_trans_output_path, maps_trans_map.values.join("\n"))
233
+ File.binwrite(names_output_path, names_trans_map.keys.join("\n"))
234
+ File.binwrite(names_trans_output_path, names_trans_map.values.join("\n"))
235
+ else
236
+ File.binwrite(maps_output_path, maps_lines[0].join("\n"))
237
+ File.binwrite(maps_trans_output_path, "\n" * (maps_lines[0].empty? ? 0 : maps_lines[0].length - 1))
238
+ File.binwrite(names_output_path, maps_lines[1].join("\n"))
239
+ File.binwrite(names_trans_output_path, "\n" * (maps_lines[1].empty? ? 0 : maps_lines[1].length - 1))
240
+ end
150
241
  end
151
242
 
152
- def self.read_other(original_other_files, output_path, logging, game_type)
153
- other_object_array_map = Hash[original_other_files.map do |filename|
243
+ def self.read_other(other_files, output_path, logging, game_type, processing_type)
244
+ other_object_array_map = Hash[other_files.map do |filename|
154
245
  basename = File.basename(filename)
155
246
  object = Marshal.load(File.binread(filename))
156
247
  object = merge_other(object).slice(1..) if basename.start_with?(/Common|Troops/)
@@ -158,11 +249,37 @@ def self.read_other(original_other_files, output_path, logging, game_type)
158
249
  [basename, object]
159
250
  end]
160
251
 
252
+ internal_processing_type = processing_type
253
+
161
254
  other_object_array_map.each do |filename, other_object_array|
162
255
  processed_filename = File.basename(filename, '.*').downcase
163
- other_lines = IndexedSet.new
256
+
257
+ other_output_path = File.join(output_path, "#{processed_filename}.txt")
258
+ other_trans_output_path = File.join(output_path, "#{processed_filename}_trans.txt")
259
+
260
+ if processing_type == 'default' && File.exist?(other_trans_output_path)
261
+ puts "#{processed_filename}_trans.txt file already exists. If you want to forcefully re-read all files, use --force flag, or --append if you want append new text to already existing files."
262
+ next
263
+ end
264
+
265
+ other_lines = nil
266
+ other_trans_map = nil
267
+
268
+ if processing_type == 'append'
269
+ if File.exist?(other_trans_output_path)
270
+ internal_processing_type == 'append'
271
+ other_trans_map = Hash[File.readlines(other_output_path, chomp: true).zip(File.readlines(other_trans_output_path, chomp: true))]
272
+ else
273
+ puts 'Files aren\'t already parsed. Continuing as if --append flag was omitted.'
274
+ internal_processing_type = 'default'
275
+ other_lines = IndexSet.new
276
+ end
277
+ else
278
+ other_lines = IndexSet.new
279
+ end
164
280
 
165
281
  if !filename.start_with?(/Common|Troops/)
282
+ hash_index = 0
166
283
  other_object_array.each do |object|
167
284
  name = object.instance_variable_get(:@name)
168
285
  nickname = object.instance_variable_get(:@nickname)
@@ -172,11 +289,21 @@ def self.read_other(original_other_files, output_path, logging, game_type)
172
289
  [name, nickname, description, note].each do |variable|
173
290
  if variable.is_a?(String) && !variable.empty?
174
291
  parsed = parse_variable(variable, game_type)
175
- other_lines.add(parsed) unless parsed.nil?
292
+
293
+ unless parsed.nil?
294
+ if internal_processing_type == 'append'
295
+ insert_at_index(other_trans_map, hash_index, parsed, '') unless other_trans_map.include?(parsed)
296
+ else
297
+ other_lines.add(parsed)
298
+ end
299
+
300
+ hash_index += 1
301
+ end
176
302
  end
177
303
  end
178
304
  end
179
305
  else
306
+ hash_index = 0
180
307
  other_object_array.each do |object|
181
308
  pages = object.instance_variable_get(:@pages)
182
309
  pages_length = pages.nil? ? 1 : pages.length
@@ -198,21 +325,47 @@ def self.read_other(original_other_files, output_path, logging, game_type)
198
325
  line.push(parameter.gsub(/\r?\n/, '\#')) if parameter.is_a?(String) && !parameter.empty?
199
326
  else
200
327
  if in_sequence
201
- other_lines.add(line.join('\#'))
328
+ joined = line.join('\#')
329
+
330
+ if internal_processing_type == 'append'
331
+ insert_at_index(other_trans_map, hash_index, joined, '') unless other_trans_map.include?(joined)
332
+ else
333
+ other_lines.add(joined)
334
+ end
335
+
202
336
  line.clear
203
337
  in_sequence = false
338
+
339
+ hash_index += 1
204
340
  end
205
341
 
206
342
  case code
207
343
  when 102
208
344
  if parameter.is_a?(Array)
209
345
  parameter.each do |subparameter|
210
- other_lines.add(subparameter) if subparameter.is_a?(String) && !subparameter.empty?
346
+ if subparameter.is_a?(String) && !subparameter.empty?
347
+ if internal_processing_type == 'append'
348
+ insert_at_index(other_trans_map, hash_index, subparameter, '') unless other_trans_map.include?(subparameter)
349
+ else
350
+ other_lines.add(subparameter)
351
+ end
352
+
353
+ hash_index += 1
354
+ end
211
355
  end
212
356
  end
213
357
  when 356
214
- other_lines.add(parameter.gsub(/\r?\n/, '\#')) if parameter.is_a?(String) &&
215
- !parameter.empty?
358
+ if parameter.is_a?(String) && !parameter.empty?
359
+ subbed = parameter.gsub(/\r?\n/, '\#')
360
+
361
+ if internal_processing_type == 'append'
362
+ insert_at_index(other_trans_map, hash_index, subbed, '')
363
+ else
364
+ other_lines.add(subbed)
365
+ end
366
+
367
+ hash_index += 1
368
+ end
216
369
  else
217
370
  nil
218
371
  end
@@ -225,17 +378,54 @@ def self.read_other(original_other_files, output_path, logging, game_type)
225
378
 
226
379
  puts "Parsed #{filename}" if logging
227
380
 
228
- File.binwrite("#{output_path}/#{processed_filename}.txt", other_lines.join("\n"))
229
- File.binwrite("#{output_path}/#{processed_filename}_trans.txt", "\n" * (other_lines.empty? ? 0 : other_lines.length - 1))
381
+ if processing_type == 'append'
382
+ File.binwrite(other_output_path, other_trans_map.keys.join("\n"))
383
+ File.binwrite(other_trans_output_path, other_trans_map.values.join("\n"))
384
+ else
385
+ File.binwrite(other_output_path, other_lines.join("\n"))
386
+ File.binwrite(other_trans_output_path, "\n" * (other_lines.empty? ? 0 : other_lines.length - 1))
387
+ end
230
388
  end
231
389
  end
232
390
 
233
- def self.read_system(system_file_path, output_path, logging)
391
+ def self.read_system(system_file_path, ini_file_path, output_path, logging, processing_type)
392
+ def self.read_ini_title(ini_file_path)
393
+ file_lines = File.readlines(ini_file_path, chomp: true)
394
+ file_lines.each do |line|
395
+ if line.start_with?('title')
396
+ parts = line.partition('=')
397
+ break parts[2].strip
398
+ end
399
+ end
400
+ end
401
+
234
402
  system_filename = File.basename(system_file_path)
235
403
  system_basename = File.basename(system_file_path, '.*').downcase
404
+
405
+ system_output_path = File.join(output_path, "#{system_basename}.txt")
406
+ system_trans_output_path = File.join(output_path, "#{system_basename}_trans.txt")
407
+
408
+ if processing_type == 'default' && File.exist?(system_trans_output_path)
409
+ puts "system_trans.txt file already exists. If you want to forcefully re-read all files, use --force flag, or --append if you want append new text to already existing files."
410
+ return
411
+ end
412
+
236
413
  system_object = Marshal.load(File.binread(system_file_path))
237
414
 
238
- system_lines = IndexedSet.new
415
+ system_lines = nil
416
+ system_trans_map = nil
417
+
418
+ if processing_type == 'append'
419
+ if File.exist?(system_trans_output_path)
420
+ system_trans_map = Hash[File.readlines(system_output_path, chomp: true).zip(File.readlines(system_trans_output_path, chomp: true))]
421
+ else
422
+ puts 'Files aren\'t already parsed. Continuing as if --append flag was omitted.'
423
+ system_lines = IndexSet.new
424
+ processing_type = 'default'
425
+ end
426
+ else
427
+ system_lines = IndexSet.new
428
+ end
239
429
 
240
430
  elements = system_object.instance_variable_get(:@elements)
241
431
  skill_types = system_object.instance_variable_get(:@skill_types)
@@ -245,42 +435,151 @@ def self.read_system(system_file_path, output_path, logging)
245
435
  terms = system_object.instance_variable_get(:@terms) || system_object.instance_variable_get(:@words)
246
436
  game_title = system_object.instance_variable_get(:@game_title)
247
437
 
438
+ hash_index = 0
439
+
248
440
  [elements, skill_types, weapon_types, armor_types].each do |array|
249
441
  next if array.nil?
250
- array.each { |string| system_lines.add(string) if string.is_a?(String) && !string.empty? }
442
+
443
+ array.each do |string|
444
+ if string.is_a?(String) && !string.empty?
445
+ if processing_type == 'append'
446
+ insert_at_index(system_trans_map, hash_index, string, '') unless system_trans_map.include?(string)
447
+ else
448
+ system_lines.add(string)
449
+ end
450
+
451
+ hash_index += 1
452
+ end
453
+ end
251
454
  end
252
455
 
253
- system_lines.add(currency_unit) if currency_unit.is_a?(String) && !currency_unit.empty?
456
+ if currency_unit.is_a?(String) && !currency_unit.empty?
457
+ if processing_type == 'append'
458
+ insert_at_index(system_trans_map, hash_index, currency_unit, '') unless system_trans_map.include?(currency_unit)
459
+ else
460
+ system_lines.add(currency_unit)
461
+ end
462
+
463
+ hash_index += 1
464
+ end
254
465
 
255
466
  terms.instance_variables.each do |variable|
256
467
  value = terms.instance_variable_get(variable)
257
468
 
258
469
  if value.is_a?(String)
259
- system_lines.add(value) unless value.empty?
470
+ unless value.empty?
471
+ if processing_type == 'append'
472
+ insert_at_index(system_trans_map, hash_index, value, '') unless system_trans_map.include?(value)
473
+ else
474
+ system_lines.add(value)
475
+ end
476
+
477
+ hash_index += 1
478
+ end
479
+
260
480
  next
261
481
  end
262
482
 
263
- value.each { |string| system_lines.add(string) if string.is_a?(String) && !string.empty? }
483
+ value.each do |string|
484
+ if string.is_a?(String) && !string.empty?
485
+ if processing_type == 'append'
486
+ insert_at_index(system_trans_map, hash_index, string, '') unless system_trans_map.include?(string)
487
+ else
488
+ system_lines.add(string)
489
+ end
490
+
491
+ hash_index += 1
492
+ end
493
+ end
264
494
  end
265
495
 
266
- system_lines.add(game_title) if game_title.is_a?(String) && !game_title.empty?
496
+ ini_game_title = read_ini_title(ini_file_path)
497
+
498
+ $wait_time = 0
499
+
500
+ if ini_game_title != game_title
501
+ if game_title.is_a?(String) && !game_title.empty?
502
+ wait_time_start = Time.now
503
+
504
+ puts "Game title from the Game.ini file and game title from the System file are different.\nWhich game title would you like to parse?\n(That doesn't affect anything major, just when you'll write the game back, translated game title will be applied both to the .ini and System file.)\n0, System title - #{game_title}\n1, Game.ini title - #{ini_game_title}"
505
+ choice = gets.chomp.to_i
506
+
507
+ $wait_time = Time.now - wait_time_start
508
+
509
+ if choice == 0
510
+ if processing_type == 'append'
511
+ insert_at_index(system_trans_map, hash_index, game_title, '') unless system_trans_map.include?(game_title)
512
+ else
513
+ system_lines.add(game_title)
514
+ end
515
+ else
516
+ if processing_type == 'append'
517
+ insert_at_index(system_trans_map, hash_index, ini_game_title, '') unless system_trans_map.include?(ini_game_title)
518
+ else
519
+ system_lines.add(ini_game_title)
520
+ end
521
+ end
522
+ else
523
+ if processing_type == 'append'
524
+ insert_at_index(system_trans_map, hash_index, ini_game_title, '') unless system_trans_map.include?(ini_game_title)
525
+ else
526
+ system_lines.add(ini_game_title)
527
+ end
528
+ end
529
+
530
+ hash_index += 1
531
+ end
267
532
 
268
533
  puts "Parsed #{system_filename}" if logging
269
534
 
270
- File.binwrite("#{output_path}/#{system_basename}.txt", system_lines.join("\n"))
271
- File.binwrite("#{output_path}/#{system_basename}_trans.txt", "\n" * (system_lines.empty? ? 0 : system_lines.length - 1))
535
+ if processing_type == 'append'
536
+ File.binwrite(system_output_path, system_trans_map.keys.join("\n"))
537
+ File.binwrite(system_trans_output_path, system_trans_map.values.join("\n"))
538
+ else
539
+ File.binwrite(system_output_path, system_lines.join("\n"))
540
+ File.binwrite(system_trans_output_path, "\n" * (system_lines.empty? ? 0 : system_lines.length - 1))
541
+ end
542
+
272
543
  end
273
544
 
274
- def self.read_scripts(scripts_file_path, output_path, logging)
545
+ def self.read_scripts(scripts_file_path, output_path, logging, processing_type)
546
+ scripts_filename = File.basename(scripts_file_path)
547
+ scripts_basename = File.basename(scripts_file_path, '.*').downcase
548
+
549
+ scripts_plain_output_path = File.join(output_path, "#{scripts_basename}_plain.txt")
550
+ scripts_output_path = File.join(output_path, "#{scripts_basename}.txt")
551
+ scripts_trans_output_path = File.join(output_path, "#{scripts_basename}_trans.txt")
552
+
553
+ if processing_type == 'default' && File.exist?(scripts_trans_output_path)
554
+ puts "scripts_trans.txt file already exists. If you want to forcefully re-read all files, use --force flag, or --append if you want append new text to already existing files."
555
+ return
556
+ end
557
+
275
558
  script_entries = Marshal.load(File.binread(scripts_file_path))
276
- scripts_lines = IndexedSet.new
559
+
560
+ scripts_lines = nil
561
+ scripts_trans_map = nil
562
+
563
+ if processing_type == 'append'
564
+ if File.exist?(scripts_trans_output_path)
565
+ scripts_trans_map = Hash[File.readlines(scripts_output_path, chomp: true).zip(File.readlines(scripts_trans_output_path, chomp: true))]
566
+ else
567
+ puts 'Files aren\'t already parsed. Continuing as if --append flag was omitted.'
568
+ processing_type = 'default'
569
+ scripts_lines = IndexSet.new
570
+ end
571
+ else
572
+ scripts_lines = IndexSet.new
573
+ end
574
+
277
575
  codes_content = []
576
+ hash_index = 0
278
577
 
279
578
  script_entries.each do |script|
280
579
  code = Zlib::Inflate.inflate(script[2]).force_encoding('UTF-8')
281
580
  codes_content.push(code)
282
581
 
283
- extract_quoted_strings(code).each do |string|
582
+ extract_quoted_strings(code).keys.each do |string|
284
583
  string.strip!
285
584
 
286
585
  # Removes the U+3000 Japanese typographical space to check if string, when stripped, is truly empty
@@ -329,13 +628,25 @@ def self.read_scripts(scripts_file_path, output_path, logging)
329
628
  string.match?(/Clear image/) ||
330
629
  string.match?(/Can Collapse/)
331
630
 
332
- scripts_lines.add(string)
631
+ if processing_type == 'append'
632
+ insert_at_index(scripts_trans_map, hash_index, string, '') unless scripts_trans_map.include?(string)
633
+ else
634
+ scripts_lines.add(string)
635
+ end
636
+
637
+ hash_index += 1
333
638
  end
334
639
  end
335
640
 
336
- puts "Parsed #{File.basename(scripts_file_path)}" if logging
641
+ puts "Parsed #{scripts_filename}" if logging
642
+
643
+ File.binwrite(scripts_plain_output_path, codes_content.join("\n"))
337
644
 
338
- File.binwrite("#{output_path}/scripts_plain.txt", codes_content.join("\n"))
339
- File.binwrite("#{output_path}/scripts.txt", scripts_lines.join("\n"))
340
- File.binwrite("#{output_path}/scripts_trans.txt", "\n" * (scripts_lines.empty? ? 0 : scripts_lines.length - 1))
341
- end
645
+ if processing_type == 'append'
646
+ File.binwrite(scripts_output_path, scripts_trans_map.keys.join("\n"))
647
+ File.binwrite(scripts_trans_output_path, scripts_trans_map.values.join("\n"))
648
+ else
649
+ File.binwrite(scripts_output_path, scripts_lines.join("\n"))
650
+ File.binwrite(scripts_trans_output_path, "\n" * (scripts_lines.empty? ? 0 : scripts_lines.length - 1))
651
+ end
652
+ end
data/lib/write.rb CHANGED
@@ -2,17 +2,58 @@
2
2
 
3
3
  require 'zlib'
4
4
 
5
- def self.shuffle_words(array)
6
- array.map do |string|
7
- re = /\S+/
8
- words = string.scan(re)
9
- shuffled = words.shuffle
5
+ def self.extract_quoted_strings(string)
6
+ # Hash of string-index key-value pairs
7
+ result = {}
8
+
9
+ skip_block = false
10
+ in_quotes = false
11
+ quote_type = nil
12
+ buffer = []
13
+
14
+ current_string_index = 0
15
+ string.each_line do |line|
16
+ stripped = line.strip
17
+
18
+ if stripped[0] == '#' || stripped.start_with?(/(Win|Lose)|_Fanfare/)
19
+ next
20
+ end
21
+
22
+ skip_block = true if stripped.start_with?('=begin')
23
+ skip_block = false if stripped.start_with?('=end')
24
+
25
+ next if skip_block
26
+
27
+ buffer.push('\#') if in_quotes
28
+
29
+ line.each_char.each_with_index do |char, index|
30
+ if %w[' "].include?(char)
31
+ unless quote_type.nil? || char == quote_type
32
+ buffer.push(char)
33
+ next
34
+ end
35
+
36
+ quote_type = char
37
+ in_quotes = !in_quotes
38
+ result[buffer.join] = current_string_index + index
39
+ buffer.clear
40
+ next
41
+ end
10
42
 
11
- (0..(words.length)).each do |i|
12
- string.sub!(words[i], shuffled[i])
43
+ if in_quotes
44
+ buffer.push(char)
45
+ end
13
46
  end
14
47
 
15
- string
48
+ current_string_index += line.length
49
+ end
50
+
51
+ result
52
+ end
53
+
54
+ def shuffle_words_in_array(array)
55
+ array.map do |string|
56
+ string.split.shuffle.join(' ')
16
57
  end
17
58
  end
18
59
 
@@ -309,7 +350,17 @@ def self.write_other(original_files, other_path, output_path, shuffle_level, log
309
350
  end
310
351
  end
311
352
 
312
- def self.write_system(system_file_path, other_path, output_path, shuffle_level, logging)
353
+ def self.write_system(system_file_path, ini_file_path, other_path, output_path, shuffle_level, logging)
354
+ def self.write_ini_title(ini_file_path, translated)
355
+ file_lines = File.readlines(ini_file_path, chomp: true)
356
+ title_line_index = file_lines.each_with_index do |line, i|
357
+ break i if line.start_with?('title')
358
+ end
359
+
360
+ file_lines[title_line_index] = translated
361
+ File.binwrite(ini_file_path, file_lines.join)
362
+ end
363
+
313
364
  system_basename = File.basename(system_file_path)
314
365
  system_object = Marshal.load(File.binread(system_file_path))
315
366
 
@@ -333,7 +384,6 @@ def self.write_system(system_file_path, other_path, output_path, shuffle_level,
333
384
  armor_types = system_object.instance_variable_get(system_symbols[3])
334
385
  currency_unit = system_object.instance_variable_get(system_symbols[4])
335
386
  terms = system_object.instance_variable_get(system_symbols[5]) || system_object.instance_variable_get(system_symbols[6])
336
- game_title = system_object.instance_variable_get(system_symbols[7])
337
387
 
338
388
  [elements, skill_types, weapon_types, armor_types].each_with_index.each do |array, i|
339
389
  next unless array.is_a?(Array)
@@ -363,20 +413,19 @@ def self.write_system(system_file_path, other_path, output_path, shuffle_level,
363
413
  system_object.instance_variable_set(system_symbols[5], terms) :
364
414
  system_object.instance_variable_set(system_symbols[6], terms)
365
415
 
366
- game_title_translated = system_translation_map[game_title]
416
+ game_title_translated = system_translated_text[-1]
367
417
  system_object.instance_variable_set(system_symbols[7], game_title_translated) if currency_unit.is_a?(String) && !game_title_translated.nil?
368
418
 
419
+ write_ini_title(ini_file_path, game_title_translated)
420
+
369
421
  puts "Written #{system_basename}" if logging
370
422
 
371
423
  File.binwrite("#{output_path}/#{system_basename}", Marshal.dump(system_object))
372
424
  end
373
425
 
374
- def self.write_scripts(scripts_file, other_path, output_path, logging)
375
- scripts_basename = File.basename(scripts_file)
376
- script_entries = Marshal.load(File.binread(scripts_file))
377
-
378
- scripts_original_text = File.readlines("#{other_path}/scripts.txt", encoding: 'UTF-8', chomp: true)
379
- .map { |line| line.gsub('\#', "\r\n") }
426
+ def self.write_scripts(scripts_file_path, other_path, output_path, logging)
427
+ scripts_basename = File.basename(scripts_file_path)
428
+ script_entries = Marshal.load(File.binread(scripts_file_path))
380
429
 
381
430
  scripts_translated_text = File.readlines("#{other_path}/scripts_trans.txt", encoding: 'UTF-8', chomp: true)
382
431
  .map { |line| line.gsub('\#', "\r\n") }
@@ -386,10 +435,58 @@ def self.write_scripts(scripts_file, other_path, output_path, logging)
386
435
  script_entries.each do |script|
387
436
  code = Zlib::Inflate.inflate(script[2]).force_encoding('UTF-8')
388
437
 
389
- scripts_original_text.zip(scripts_translated_text).each do |original, translated|
390
- # That may possibly break something, but until it does, who cares
391
- # Honestly, it needs to be changed to find quoted strings like in `extract_quoted_strings` method
392
- code.gsub!(original, translated) unless translated.nil?
438
+ (extract_quoted_strings(code)).each_with_index do |string_data, i|
439
+ string, string_index = string_data
440
+
441
+ string.strip!
442
+
443
+ # Removes the U+3000 Japanese typographical space to check if string, when stripped, is truly empty
444
+ next if string.empty? || string.gsub(' ', '').empty?
445
+
446
+ # Maybe this mess will remove something that mustn't be removed, but it needs to be tested
447
+ next if string.start_with?(/([#!?$@]|(\.\/)?(Graphics|Data|Audio|CG|Movies|Save)\/)/) ||
448
+ string.match?(/^\d+$/) ||
449
+ string.match?(/^(.)\1{2,}$/) ||
450
+ string.match?(/^(false|true)$/) ||
451
+ string.match?(/^[wr]b$/) ||
452
+ string.match?(/^(?=.*\d)[A-Za-z0-9\-]+$/) ||
453
+ string.match?(/^[A-Z\-()\/ +'&]*$/) ||
454
+ string.match?(/^[a-z\-()\/ +'&]*$/) ||
455
+ string.match?(/^[A-Za-z]+[+-]$/) ||
456
+ string.match?(/^[.()+-:;\[\]^~%&!*\/→×??x%▼|]$/) ||
457
+ string.match?(/^Tile.*[A-Z]$/) ||
458
+ string.match?(/^:?%.*[ds][:%]*?$/) ||
459
+ string.match?(/^[a-zA-Z]+([A-Z][a-z]*)+$/) ||
460
+ string.match?(/^Cancel Action$|^Invert$|^End$|^Individual$|^Missed File$|^Bitmap$|^Audio$/) ||
461
+ string.match?(/\.(mp3|ogg|jpg|png|ini)$/) ||
462
+ string.match?(/\/(\d.*)?$/) ||
463
+ string.match?(/FILE$/) ||
464
+ string.match?(/#\{/) ||
465
+ string.match?(/\\(?!#)/) ||
466
+ string.match?(/\+?=?=/) ||
467
+ string.match?(/[}{_<>]/) ||
468
+ string.match?(/r[vx]data/) ||
469
+ string.match?(/No such file or directory/) ||
470
+ string.match?(/level \*\*/) ||
471
+ string.match?(/Courier New/) ||
472
+ string.match?(/Comic Sans/) ||
473
+ string.match?(/Lucida/) ||
474
+ string.match?(/Verdana/) ||
475
+ string.match?(/Tahoma/) ||
476
+ string.match?(/Arial/) ||
477
+ string.match?(/Player start location/) ||
478
+ string.match?(/Common event call has exceeded/) ||
479
+ string.match?(/se-/) ||
480
+ string.match?(/Start Pos/) ||
481
+ string.match?(/An error has occurred/) ||
482
+ string.match?(/Define it first/) ||
483
+ string.match?(/Process Skill/) ||
484
+ string.match?(/Wpn Only/) ||
485
+ string.match?(/Don't Wait/) ||
486
+ string.match?(/Clear image/) ||
487
+ string.match?(/Can Collapse/)
488
+
489
+ code[string_index, string.length] = scripts_translated_text[i]
393
490
  end
394
491
 
395
492
  script[2] = Zlib::Deflate.deflate(code, Zlib::BEST_COMPRESSION)
data/rvpacker-txt.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'rvpacker-txt'
5
- spec.version = '1.4.0'
5
+ spec.version = '1.5.0'
6
6
  spec.authors = ['Howard Jeng', 'Andrew Kesterson', 'Solistra', 'Darkness9724', 'savannstm']
7
7
  spec.email = ['savannstm@gmail.com']
8
8
  spec.summary = 'Reads or writes RPG Maker XP/VX/VXAce game text to .txt files'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rvpacker-txt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Howard Jeng
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2024-07-09 00:00:00.000000000 Z
15
+ date: 2024-07-12 00:00:00.000000000 Z
16
16
  dependencies: []
17
17
  description:
18
18
  email: