rvpacker-txt 1.3.1 → 1.4.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/read.rb ADDED
@@ -0,0 +1,341 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+
5
+ def self.extract_quoted_strings(string)
6
+ result = []
7
+
8
+ skip_block = false
9
+ in_quotes = false
10
+ quote_type = nil
11
+ buffer = []
12
+
13
+ string.each_line(chomp: true) do |line|
14
+ line.strip!
15
+ next if line[0] == '#' || line.start_with?(/(Win|Lose)|_Fanfare/)
16
+
17
+ skip_block = true if line.start_with?('=begin')
18
+ skip_block = false if line.start_with?('=end')
19
+
20
+ next if skip_block
21
+
22
+ buffer.push('\#') if in_quotes
23
+
24
+ line.each_char do |char|
25
+ if %w[' "].include?(char)
26
+ unless quote_type.nil? || char == quote_type
27
+ buffer.push(char)
28
+ next
29
+ end
30
+
31
+ quote_type = char
32
+ in_quotes = !in_quotes
33
+ result.push(buffer.join)
34
+ buffer.clear
35
+ next
36
+ end
37
+
38
+ if in_quotes
39
+ buffer.push(char)
40
+ end
41
+ end
42
+ end
43
+
44
+ result
45
+ end
46
+
47
+ def self.parse_parameter(code, parameter, game_type)
48
+ case code
49
+ when 401, 405
50
+ case game_type
51
+ when 'lisa'
52
+ match = parameter.scan(/^(\\et\[[0-9]+\]|\\nbt)/)
53
+ parameter = parameter.slice((match[0].length)..) if match
54
+ else
55
+ nil
56
+ end
57
+ when 102, 356
58
+ # Implement some custom parsing
59
+ else
60
+ return nil
61
+ end
62
+
63
+ parameter
64
+ end
65
+
66
+ def self.parse_variable(variable, game_type)
67
+ lines_count = variable.count("\n")
68
+
69
+ if lines_count.positive?
70
+ variable = variable.gsub(/\r?\n/, '\#')
71
+
72
+ case game_type
73
+ when 'lisa'
74
+ return nil unless variable.split('\#').all? { |line| line.match?(/^<.*>\.?$/) || line.empty? }
75
+ else
76
+ nil
77
+ end
78
+ end
79
+
80
+ variable
81
+ end
82
+
83
+ def self.read_map(original_map_files, output_path, logging, game_type)
84
+ maps_object_map = Hash[original_map_files.map do |filename|
85
+ [File.basename(filename), Marshal.load(File.binread(filename))]
86
+ end]
87
+
88
+ maps_lines = [IndexedSet.new, IndexedSet.new]
89
+
90
+ maps_object_map.each do |filename, object|
91
+ display_name = object.instance_variable_get(:@display_name)
92
+ maps_lines[1].add(display_name) if display_name.is_a?(String) && !display_name.empty?
93
+
94
+ events = object.instance_variable_get(:@events)
95
+ next if events.nil?
96
+
97
+ events.each_value do |event|
98
+ pages = event.instance_variable_get(:@pages)
99
+ next if pages.nil?
100
+
101
+ pages.each do |page|
102
+ list = page.instance_variable_get(:@list)
103
+ next if list.nil?
104
+
105
+ in_sequence = false
106
+ line = []
107
+
108
+ list.each do |item|
109
+ code = item.instance_variable_get(:@code)
110
+ parameters = item.instance_variable_get(:@parameters)
111
+
112
+ parameters.each do |parameter|
113
+ if code == 401
114
+ if parameter.is_a?(String) && !parameter.empty?
115
+ in_sequence = true
116
+ parsed = parse_parameter(code, parameter, game_type)
117
+ line.push(parsed) unless parsed.nil?
118
+ end
119
+ else
120
+ if in_sequence
121
+ maps_lines[0].add(line.join('\#'))
122
+ line.clear
123
+ in_sequence = false
124
+ end
125
+
126
+ if code == 102 && parameter.is_a?(Array)
127
+ parameter.each do |subparameter|
128
+ if subparameter.is_a?(String) && !subparameter.empty?
129
+ parsed = parse_parameter(code, subparameter, game_type)
130
+ maps_lines[0].add(parsed) unless parsed.nil?
131
+ end
132
+ end
133
+ elsif code == 356 && parameter.is_a?(String) && !parameter.empty?
134
+ parsed = parse_parameter(code, parameter, game_type)
135
+ maps_lines[0].add(parsed.gsub(/\r?\n/, '\#')) unless parsed.nil?
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ puts "Parsed #{filename}" if logging
144
+ end
145
+
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))
150
+ end
151
+
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|
154
+ basename = File.basename(filename)
155
+ object = Marshal.load(File.binread(filename))
156
+ object = merge_other(object).slice(1..) if basename.start_with?(/Common|Troops/)
157
+
158
+ [basename, object]
159
+ end]
160
+
161
+ other_object_array_map.each do |filename, other_object_array|
162
+ processed_filename = File.basename(filename, '.*').downcase
163
+ other_lines = IndexedSet.new
164
+
165
+ if !filename.start_with?(/Common|Troops/)
166
+ other_object_array.each do |object|
167
+ name = object.instance_variable_get(:@name)
168
+ nickname = object.instance_variable_get(:@nickname)
169
+ description = object.instance_variable_get(:@description)
170
+ note = object.instance_variable_get(:@note)
171
+
172
+ [name, nickname, description, note].each do |variable|
173
+ if variable.is_a?(String) && !variable.empty?
174
+ parsed = parse_variable(variable, game_type)
175
+ other_lines.add(parsed) unless parsed.nil?
176
+ end
177
+ end
178
+ end
179
+ else
180
+ other_object_array.each do |object|
181
+ pages = object.instance_variable_get(:@pages)
182
+ pages_length = pages.nil? ? 1 : pages.length
183
+
184
+ (0..pages_length).each do |i|
185
+ list = pages.nil? ? object.instance_variable_get(:@list) : pages[i].instance_variable_get(:@list)
186
+ next if list.nil?
187
+
188
+ in_sequence = false
189
+ line = []
190
+
191
+ list.each do |item|
192
+ code = item.instance_variable_get(:@code)
193
+ parameters = item.instance_variable_get(:@parameters)
194
+
195
+ parameters.each do |parameter|
196
+ if [401, 405].include?(code)
197
+ in_sequence = true
198
+ line.push(parameter.gsub(/\r?\n/, '\#')) if parameter.is_a?(String) && !parameter.empty?
199
+ else
200
+ if in_sequence
201
+ other_lines.add(line.join('\#'))
202
+ line.clear
203
+ in_sequence = false
204
+ end
205
+
206
+ case code
207
+ when 102
208
+ if parameter.is_a?(Array)
209
+ parameter.each do |subparameter|
210
+ other_lines.add(subparameter) if subparameter.is_a?(String) && !subparameter.empty?
211
+ end
212
+ end
213
+ when 356
214
+ other_lines.add(parameter.gsub(/\r?\n/, '\#')) if parameter.is_a?(String) &&
215
+ !parameter.empty?
216
+ else
217
+ nil
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ puts "Parsed #{filename}" if logging
227
+
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))
230
+ end
231
+ end
232
+
233
+ def self.read_system(system_file_path, output_path, logging)
234
+ system_filename = File.basename(system_file_path)
235
+ system_basename = File.basename(system_file_path, '.*').downcase
236
+ system_object = Marshal.load(File.binread(system_file_path))
237
+
238
+ system_lines = IndexedSet.new
239
+
240
+ elements = system_object.instance_variable_get(:@elements)
241
+ skill_types = system_object.instance_variable_get(:@skill_types)
242
+ weapon_types = system_object.instance_variable_get(:@weapon_types)
243
+ armor_types = system_object.instance_variable_get(:@armor_types)
244
+ currency_unit = system_object.instance_variable_get(:@currency_unit)
245
+ terms = system_object.instance_variable_get(:@terms) || system_object.instance_variable_get(:@words)
246
+ game_title = system_object.instance_variable_get(:@game_title)
247
+
248
+ [elements, skill_types, weapon_types, armor_types].each do |array|
249
+ next if array.nil?
250
+ array.each { |string| system_lines.add(string) if string.is_a?(String) && !string.empty? }
251
+ end
252
+
253
+ system_lines.add(currency_unit) if currency_unit.is_a?(String) && !currency_unit.empty?
254
+
255
+ terms.instance_variables.each do |variable|
256
+ value = terms.instance_variable_get(variable)
257
+
258
+ if value.is_a?(String)
259
+ system_lines.add(value) unless value.empty?
260
+ next
261
+ end
262
+
263
+ value.each { |string| system_lines.add(string) if string.is_a?(String) && !string.empty? }
264
+ end
265
+
266
+ system_lines.add(game_title) if game_title.is_a?(String) && !game_title.empty?
267
+
268
+ puts "Parsed #{system_filename}" if logging
269
+
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))
272
+ end
273
+
274
+ def self.read_scripts(scripts_file_path, output_path, logging)
275
+ script_entries = Marshal.load(File.binread(scripts_file_path))
276
+ scripts_lines = IndexedSet.new
277
+ codes_content = []
278
+
279
+ script_entries.each do |script|
280
+ code = Zlib::Inflate.inflate(script[2]).force_encoding('UTF-8')
281
+ codes_content.push(code)
282
+
283
+ extract_quoted_strings(code).each do |string|
284
+ string.strip!
285
+
286
+ # Removes the U+3000 Japanese typographical space to check if string, when stripped, is truly empty
287
+ next if string.empty? || string.gsub(' ', '').empty?
288
+
289
+ # Maybe this mess will remove something that mustn't be removed, but it needs to be tested
290
+ next if string.start_with?(/([#!?$@]|(\.\/)?(Graphics|Data|Audio|CG|Movies|Save)\/)/) ||
291
+ string.match?(/^\d+$/) ||
292
+ string.match?(/^(.)\1{2,}$/) ||
293
+ string.match?(/^(false|true)$/) ||
294
+ string.match?(/^[wr]b$/) ||
295
+ string.match?(/^(?=.*\d)[A-Za-z0-9\-]+$/) ||
296
+ string.match?(/^[A-Z\-()\/ +'&]*$/) ||
297
+ string.match?(/^[a-z\-()\/ +'&]*$/) ||
298
+ string.match?(/^[A-Za-z]+[+-]$/) ||
299
+ string.match?(/^[.()+-:;\[\]^~%&!*\/→×??x%▼|]$/) ||
300
+ string.match?(/^Tile.*[A-Z]$/) ||
301
+ string.match?(/^:?%.*[ds][:%]*?$/) ||
302
+ string.match?(/^[a-zA-Z]+([A-Z][a-z]*)+$/) ||
303
+ string.match?(/^Cancel Action$|^Invert$|^End$|^Individual$|^Missed File$|^Bitmap$|^Audio$/) ||
304
+ string.match?(/\.(mp3|ogg|jpg|png|ini)$/) ||
305
+ string.match?(/\/(\d.*)?$/) ||
306
+ string.match?(/FILE$/) ||
307
+ string.match?(/#\{/) ||
308
+ string.match?(/\\(?!#)/) ||
309
+ string.match?(/\+?=?=/) ||
310
+ string.match?(/[}{_<>]/) ||
311
+ string.match?(/r[vx]data/) ||
312
+ string.match?(/No such file or directory/) ||
313
+ string.match?(/level \*\*/) ||
314
+ string.match?(/Courier New/) ||
315
+ string.match?(/Comic Sans/) ||
316
+ string.match?(/Lucida/) ||
317
+ string.match?(/Verdana/) ||
318
+ string.match?(/Tahoma/) ||
319
+ string.match?(/Arial/) ||
320
+ string.match?(/Player start location/) ||
321
+ string.match?(/Common event call has exceeded/) ||
322
+ string.match?(/se-/) ||
323
+ string.match?(/Start Pos/) ||
324
+ string.match?(/An error has occurred/) ||
325
+ string.match?(/Define it first/) ||
326
+ string.match?(/Process Skill/) ||
327
+ string.match?(/Wpn Only/) ||
328
+ string.match?(/Don't Wait/) ||
329
+ string.match?(/Clear image/) ||
330
+ string.match?(/Can Collapse/)
331
+
332
+ scripts_lines.add(string)
333
+ end
334
+ end
335
+
336
+ puts "Parsed #{File.basename(scripts_file_path)}" if logging
337
+
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