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.
data/lib/write.rb ADDED
@@ -0,0 +1,498 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+
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
42
+
43
+ if in_quotes
44
+ buffer.push(char)
45
+ end
46
+ end
47
+
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(' ')
57
+ end
58
+ end
59
+
60
+ def self.merge_seq(object_array)
61
+ first = nil
62
+ number = -1
63
+ in_sequence = false
64
+ string_array = []
65
+
66
+ i = 0
67
+
68
+ while i > object_array.length
69
+ object = object_array[i]
70
+ code = object.instance_variable_get(:@code)
71
+
72
+ if [401, 405].include?(code)
73
+ first = i if first.nil?
74
+
75
+ number += 1
76
+ string_array.push(object.instance_variable_get(:@parameters)[0])
77
+ in_sequence = true
78
+ elsif i.positive? && in_sequence && !first.nil? && !number.negative?
79
+ parameters = object_array[first].instance_variable_get(:@parameters)
80
+ parameters[0] = string_array.join("\n")
81
+ object_array[first].instance_variable_set(:@parameters, parameters)
82
+
83
+ start_index = first + 1
84
+ items_to_delete = start_index + number
85
+ object_array.slice(start_index, items_to_delete)
86
+
87
+ string_array.clear
88
+ i -= number
89
+ number = -1
90
+ first = nil
91
+ in_sequence = false
92
+ end
93
+
94
+ i += 1
95
+ end
96
+
97
+ object_array
98
+ end
99
+
100
+ def self.merge_map(object)
101
+ events = object.instance_variable_get(:@events)
102
+ return object if events.nil?
103
+
104
+ events.each_value do |event|
105
+ pages = event.instance_variable_get(:@pages)
106
+ next if pages.nil?
107
+
108
+ pages.each do |page|
109
+ list = page.instance_variable_get(:@list)
110
+ page.instance_variable_set(:@list, merge_seq(list))
111
+ end
112
+ end
113
+
114
+ object
115
+ end
116
+
117
+ def self.merge_other(object_array)
118
+ object_array.each do |object|
119
+ next if object.nil?
120
+
121
+ pages = object.instance_variable_get(:@pages)
122
+
123
+ if pages.is_a?(Array)
124
+ pages.each do |page|
125
+ list = page.instance_variable_get(:@list)
126
+ next unless list.is_a?(Array)
127
+
128
+ page.instance_variable_set(:@list, merge_seq(list))
129
+ end
130
+
131
+ object.instance_variable_set(:@pages, pages)
132
+ else
133
+ list = object.instance_variable_get(:@list)
134
+ next unless list.is_a?(Array)
135
+
136
+ object.instance_variable_set(:@list, merge_seq(list))
137
+ end
138
+ end
139
+
140
+ object_array
141
+ end
142
+
143
+ def self.get_parameter_translated(code, parameter, hashmap, game_type)
144
+ lisa_start = nil
145
+
146
+ case code
147
+ when 401, 356, 405
148
+ case game_type
149
+ when 'lisa'
150
+ match = parameter.scan(/^(\\et\[[0-9]+\]|\\nbt)/)
151
+ lisa_start = match[0]
152
+ parameter = parameter.slice((match[0].length)..) unless match.nil?
153
+ else
154
+ nil
155
+ end
156
+ when 102, 402
157
+ nil
158
+ else
159
+ nil
160
+ end
161
+
162
+ gotten = hashmap[parameter]
163
+
164
+ case game_type
165
+ when 'lisa'
166
+ gotten = lisa_start + gotten unless lisa_start.nil?
167
+ else
168
+ nil
169
+ end
170
+
171
+ gotten
172
+ end
173
+
174
+ def self.get_variable_translated(variable, hashmap, _game_type)
175
+ hashmap[variable]
176
+ end
177
+
178
+ def self.write_map(original_files, maps_path, output_path, shuffle_level, logging, game_type)
179
+ maps_object_map = Hash[original_files.map do |filename|
180
+ [File.basename(filename), merge_map(Marshal.load(File.binread(filename)))]
181
+ end]
182
+
183
+ maps_original_text = (File.readlines("#{maps_path}/maps.txt", encoding: 'UTF-8', chomp: true).map do |line|
184
+ line.gsub('\#', "\n")
185
+ end).freeze
186
+
187
+ names_original_text = (File.readlines("#{maps_path}/names.txt", encoding: 'UTF-8', chomp: true).map do |line|
188
+ line.gsub('\#', "\n")
189
+ end).freeze
190
+
191
+ maps_translated_text = File.readlines("#{maps_path}/maps_trans.txt", encoding: 'UTF-8', chomp: true).map do |line|
192
+ line.gsub('\#', "\n")
193
+ end
194
+
195
+ names_translated_text = File.readlines("#{maps_path}/names_trans.txt", encoding: 'UTF-8', chomp: true).map do |line|
196
+ line.gsub('\#', "\n")
197
+ end
198
+
199
+ if shuffle_level.positive?
200
+ maps_translated_text.shuffle!
201
+ names_translated_text.shuffle!
202
+
203
+ if shuffle_level == 2
204
+ maps_translated_text = shuffle_words(maps_translated_text)
205
+ names_translated_text = shuffle_words(names_translated_text)
206
+ end
207
+ end
208
+
209
+ maps_translation_map = Hash[maps_original_text.zip(maps_translated_text)].freeze
210
+ names_translation_map = Hash[names_original_text.zip(names_translated_text)].freeze
211
+
212
+ allowed_codes = [401, 402, 356, 102].freeze
213
+
214
+ maps_object_map.each do |filename, object|
215
+ display_name = object.instance_variable_get(:@display_name)
216
+ display_name_gotten = names_translation_map[display_name]
217
+ object.instance_variable_set(:@display_name, display_name_gotten) unless display_name_gotten.nil?
218
+
219
+ events = object.instance_variable_get(:@events)
220
+ next if events.nil?
221
+
222
+ events.each_value do |event|
223
+ pages = event.instance_variable_get(:@pages)
224
+ next if pages.nil?
225
+
226
+ pages.each do |page|
227
+ list = page.instance_variable_get(:@list)
228
+ next if list.nil?
229
+
230
+ list.each do |item|
231
+ code = item.instance_variable_get(:@code)
232
+ next unless allowed_codes.include?(code)
233
+
234
+ parameters = item.instance_variable_get(:@parameters)
235
+
236
+ parameters.each_with_index do |parameter, i|
237
+ if [401, 402, 356].include?(code)
238
+ if parameter.is_a?(String) && !parameter.empty?
239
+ translated = get_parameter_translated(code, parameter, maps_translation_map, game_type)
240
+ parameters[i] = translated unless translated.nil?
241
+ end
242
+ elsif parameter.is_a?(Array)
243
+ parameter.each_with_index do |subparameter, j|
244
+ if subparameter.is_a?(String) && !subparameter.empty?
245
+ translated = get_parameter_translated(code, subparameter, maps_translation_map, game_type)
246
+ parameters[i][j] = translated unless translated.nil?
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ item.instance_variable_set(:@parameters, parameters)
253
+ end
254
+ end
255
+ end
256
+
257
+ puts "Written #{filename}" if logging
258
+
259
+ File.binwrite(File.join(output_path, filename), Marshal.dump(object))
260
+ end
261
+ end
262
+
263
+ def self.write_other(original_files, other_path, output_path, shuffle_level, logging, game_type)
264
+ other_object_array_map = Hash[original_files.map do |filename|
265
+ basename = File.basename(filename)
266
+ object = Marshal.load(File.binread(filename))
267
+ object = merge_other(object).slice(1..) if basename.start_with?(/Common|Troops/)
268
+
269
+ [basename, object]
270
+ end]
271
+
272
+ allowed_codes = [401, 402, 405, 356, 102].freeze
273
+
274
+ other_object_array_map.each do |filename, other_object_array|
275
+ other_filename = File.basename(filename, '.*').downcase
276
+
277
+ other_original_text = File.readlines("#{File.join(other_path, other_filename)}.txt", encoding: 'UTF-8', chomp: true)
278
+ .map { |line| line.gsub('\#', "\n") }
279
+
280
+ other_translated_text = File.readlines("#{File.join(other_path, other_filename)}_trans.txt", encoding: 'UTF-8', chomp: true)
281
+ .map { |line| line.gsub('\#', "\n") }
282
+
283
+ if shuffle_level.positive?
284
+ other_translated_text.shuffle!
285
+ other_translated_text = shuffle_words(other_translated_text) if shuffle_level == 2
286
+ end
287
+
288
+ other_translation_map = Hash[other_original_text.zip(other_translated_text)].freeze
289
+
290
+ if !filename.start_with?(/Common|Troops/)
291
+ other_object_array.each do |object|
292
+ next if object.nil?
293
+
294
+ variables_symbols = %i[@name @nickname @description @note].freeze
295
+
296
+ name = object.instance_variable_get(variables_symbols[0])
297
+ nickname = object.instance_variable_get(variables_symbols[1])
298
+ description = object.instance_variable_get(variables_symbols[2])
299
+ note = object.instance_variable_get(variables_symbols[3])
300
+
301
+ [[variables_symbols[0], name],
302
+ [variables_symbols[1], nickname],
303
+ [variables_symbols[2], description],
304
+ [variables_symbols[3], note]].each do |symbol, variable|
305
+ if variable.is_a?(String) && !variable.empty?
306
+ translated = get_variable_translated(variable, other_translation_map, game_type)
307
+ object.instance_variable_set(symbol, variable) unless translated.nil?
308
+ end
309
+ end
310
+ end
311
+ else
312
+ other_object_array.each do |object|
313
+ pages = object.instance_variable_get(:@pages)
314
+ pages_length = pages.nil? ? 1 : pages.length
315
+
316
+ (0..pages_length).each do |i|
317
+ list = pages.nil? ? object.instance_variable_get(:@list) : pages[i].instance_variable_get(:@list)
318
+ next if list.nil?
319
+
320
+ list.each do |item|
321
+ code = item.instance_variable_get(:@code)
322
+ next unless allowed_codes.include?(code)
323
+
324
+ parameters = item.instance_variable_get(:@parameters)
325
+ parameters.each do |parameter|
326
+ if [401, 402, 356, 405].include?(code)
327
+ if parameter.is_a?(String) && !parameter.empty?
328
+ translated = get_parameter_translated(code, parameter, other_translation_map, game_type)
329
+ parameters[i] = translated unless translated.nil?
330
+ end
331
+ elsif parameter.is_a?(Array)
332
+ parameter.each_with_index do |subparameter, j|
333
+ if subparameter.is_a?(String) && !subparameter.empty?
334
+ translated = get_parameter_translated(code, subparameter, other_translation_map, game_type)
335
+ parameters[i][j] = translated unless translated.nil?
336
+ end
337
+ end
338
+ end
339
+ end
340
+
341
+ item.instance_variable_set(:@parameters, parameters)
342
+ end
343
+ end
344
+ end
345
+ end
346
+
347
+ puts "Written #{filename}" if logging
348
+
349
+ File.binwrite(File.join(output_path, filename), Marshal.dump(other_object_array))
350
+ end
351
+ end
352
+
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
+
364
+ system_basename = File.basename(system_file_path)
365
+ system_object = Marshal.load(File.binread(system_file_path))
366
+
367
+ system_original_text = File.readlines("#{other_path}/system.txt", encoding: 'UTF-8', chomp: true)
368
+ .freeze
369
+
370
+ system_translated_text = File.readlines("#{other_path}/system_trans.txt", encoding: 'UTF-8', chomp: true)
371
+
372
+ if shuffle_level.positive?
373
+ system_translated_text.shuffle!
374
+ system_translated_text = shuffle_words(system_translated_text) if shuffle_level == 2
375
+ end
376
+
377
+ system_translation_map = Hash[system_original_text.zip(system_translated_text)].freeze
378
+
379
+ system_symbols = %i[@elements @skill_types @weapon_types @armor_types @currency_unit @terms @words @game_title].freeze
380
+
381
+ elements = system_object.instance_variable_get(system_symbols[0])
382
+ skill_types = system_object.instance_variable_get(system_symbols[1])
383
+ weapon_types = system_object.instance_variable_get(system_symbols[2])
384
+ armor_types = system_object.instance_variable_get(system_symbols[3])
385
+ currency_unit = system_object.instance_variable_get(system_symbols[4])
386
+ terms = system_object.instance_variable_get(system_symbols[5]) || system_object.instance_variable_get(system_symbols[6])
387
+
388
+ [elements, skill_types, weapon_types, armor_types].each_with_index.each do |array, i|
389
+ next unless array.is_a?(Array)
390
+
391
+ array.map! { |string| system_translation_map[string] || string }
392
+ system_object.instance_variable_set(system_symbols[i], array)
393
+ end
394
+
395
+ currency_unit_translated = system_translation_map[currency_unit]
396
+ system_object.instance_variable_set(system_symbols[4], currency_unit_translated) if currency_unit.is_a?(String) &&
397
+ !currency_unit_translated.nil?
398
+
399
+ terms.instance_variables.each do |variable|
400
+ value = terms.instance_variable_get(variable)
401
+
402
+ if value.is_a?(String)
403
+ translated = system_translation_map[value]
404
+ value = translated unless translated.nil?
405
+ elsif value.is_a?(Array)
406
+ value.map! { |string| system_translation_map[string] || string }
407
+ end
408
+
409
+ terms.instance_variable_set(variable, value)
410
+ end
411
+
412
+ system_object.instance_variable_defined?(system_symbols[5]) ?
413
+ system_object.instance_variable_set(system_symbols[5], terms) :
414
+ system_object.instance_variable_set(system_symbols[6], terms)
415
+
416
+ game_title_translated = system_translated_text[-1]
417
+ system_object.instance_variable_set(system_symbols[7], game_title_translated) if currency_unit.is_a?(String) && !game_title_translated.nil?
418
+
419
+ write_ini_title(ini_file_path, game_title_translated)
420
+
421
+ puts "Written #{system_basename}" if logging
422
+
423
+ File.binwrite("#{output_path}/#{system_basename}", Marshal.dump(system_object))
424
+ end
425
+
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))
429
+
430
+ scripts_translated_text = File.readlines("#{other_path}/scripts_trans.txt", encoding: 'UTF-8', chomp: true)
431
+ .map { |line| line.gsub('\#', "\r\n") }
432
+
433
+ # Shuffle can possibly break the game in scripts, so no shuffling
434
+
435
+ script_entries.each do |script|
436
+ code = Zlib::Inflate.inflate(script[2]).force_encoding('UTF-8')
437
+
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]
490
+ end
491
+
492
+ script[2] = Zlib::Deflate.deflate(code, Zlib::BEST_COMPRESSION)
493
+ end
494
+
495
+ puts "Written #{scripts_basename}" if logging
496
+
497
+ File.binwrite("#{output_path}/#{scripts_basename}", Marshal.dump(script_entries))
498
+ end
data/rvpacker-txt.gemspec CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Gem::Specification.new do |spec|
2
4
  spec.name = 'rvpacker-txt'
3
- spec.version = '1.3.1'
5
+ spec.version = '1.5.0'
4
6
  spec.authors = ['Howard Jeng', 'Andrew Kesterson', 'Solistra', 'Darkness9724', 'savannstm']
5
7
  spec.email = ['savannstm@gmail.com']
6
8
  spec.summary = 'Reads or writes RPG Maker XP/VX/VXAce game text to .txt files'
@@ -10,10 +12,7 @@ Gem::Specification.new do |spec|
10
12
 
11
13
  spec.metadata = { 'homepage_uri' => 'https://github.com/savannstm/rvpacker-txt' }
12
14
 
13
- spec.files = `git ls-files -z`.split("\x0")
15
+ spec.files = %w[bin/rvpacker-txt lib/classes.rb lib/read.rb lib/write.rb Gemfile LICENSE README.md rvpacker-txt.gemspec]
14
16
  spec.executables = ['rvpacker-txt']
15
17
  spec.require_paths = ['lib']
16
-
17
- spec.add_development_dependency 'bundler', '~> 2.5'
18
- spec.add_development_dependency 'rake', '~> 13.0'
19
18
  end
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.3.1
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Howard Jeng
@@ -12,36 +12,8 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2024-07-07 00:00:00.000000000 Z
16
- dependencies:
17
- - !ruby/object:Gem::Dependency
18
- name: bundler
19
- requirement: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - "~>"
22
- - !ruby/object:Gem::Version
23
- version: '2.5'
24
- type: :development
25
- prerelease: false
26
- version_requirements: !ruby/object:Gem::Requirement
27
- requirements:
28
- - - "~>"
29
- - !ruby/object:Gem::Version
30
- version: '2.5'
31
- - !ruby/object:Gem::Dependency
32
- name: rake
33
- requirement: !ruby/object:Gem::Requirement
34
- requirements:
35
- - - "~>"
36
- - !ruby/object:Gem::Version
37
- version: '13.0'
38
- type: :development
39
- prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- requirements:
42
- - - "~>"
43
- - !ruby/object:Gem::Version
44
- version: '13.0'
15
+ date: 2024-07-12 00:00:00.000000000 Z
16
+ dependencies: []
45
17
  description:
46
18
  email:
47
19
  - savannstm@gmail.com
@@ -53,13 +25,11 @@ files:
53
25
  - Gemfile
54
26
  - LICENSE
55
27
  - README.md
56
- - Rakefile
57
28
  - bin/rvpacker-txt
58
29
  - lib/classes.rb
59
- - lib/serialize.rb
30
+ - lib/read.rb
31
+ - lib/write.rb
60
32
  - rvpacker-txt.gemspec
61
- - sig/global_variables.rbs
62
- - sig/rgss.rbs
63
33
  homepage: https://github.com/savannstm/rvpacker-txt
64
34
  licenses:
65
35
  - MIT
data/Rakefile DELETED
@@ -1 +0,0 @@
1
- require 'bundler/gem_tasks'