fusionpacker 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/gem-push.yml +46 -0
- data/.gitignore +35 -0
- data/Gemfile +4 -0
- data/LICENSE +17 -0
- data/README.md +124 -0
- data/Rakefile +2 -0
- data/bin/fusionpacker +39 -0
- data/fusionpacker.gemspec +26 -0
- data/lib/RGSS/BasicCoder.rb +49 -0
- data/lib/RGSS/psych_mods.rb +129 -0
- data/lib/RGSS/serialize.rb +378 -0
- data/lib/RGSS.rb +336 -0
- data/lib/RPG.rb +57 -0
- data/lib/fusionpacker/version.rb +3 -0
- metadata +145 -0
@@ -0,0 +1,378 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (c) 2013 Howard Jeng
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
5
|
+
software and associated documentation files (the "Software"), to deal in the Software
|
6
|
+
without restriction, including without limitation the rights to use, copy, modify, merge,
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
8
|
+
to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or
|
11
|
+
substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
14
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
15
|
+
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
16
|
+
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
17
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
18
|
+
DEALINGS IN THE SOFTWARE.
|
19
|
+
=end
|
20
|
+
|
21
|
+
require 'RGSS/psych_mods'
|
22
|
+
require 'fileutils'
|
23
|
+
require 'zlib'
|
24
|
+
require 'pp'
|
25
|
+
require 'formatador'
|
26
|
+
module RGSS
|
27
|
+
def self.change_extension(file, new_ext)
|
28
|
+
return File.basename(file, '.*') + new_ext
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.sanitize_filename(filename)
|
32
|
+
return filename.gsub(/[^0-9A-Za-z]+/, '_')
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.files_with_extension(directory, extension)
|
36
|
+
return Dir.entries(directory).select { |file| File.extname(file) == extension }
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.inflate(str)
|
40
|
+
text = Zlib::Inflate.inflate(str)
|
41
|
+
return text.force_encoding("UTF-8")
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.deflate(str)
|
45
|
+
return Zlib::Deflate.deflate(str, Zlib::BEST_COMPRESSION)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.dump_data_file(file, data, time, options)
|
49
|
+
File.open(file, "wb") do |f|
|
50
|
+
Marshal.dump(data, f)
|
51
|
+
end
|
52
|
+
File.utime(time, time, file)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.dump_yaml_file(file, data, time, options)
|
56
|
+
File.open(file, "wb") do |f|
|
57
|
+
Psych::dump(data, f, options)
|
58
|
+
end
|
59
|
+
File.utime(time, time, file)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.dump_save(file, data, time, options)
|
63
|
+
File.open(file, "wb") do |f|
|
64
|
+
data.each do |chunk|
|
65
|
+
Marshal.dump(chunk, f)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
File.utime(time, time, file)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.dump_raw_file(file, data, time, options)
|
72
|
+
File.open(file, "wb") do |f|
|
73
|
+
f.write(data)
|
74
|
+
end
|
75
|
+
File.utime(time, time, file)
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.dump(dumper, file, data, time, options)
|
79
|
+
self.method(dumper).call(file, data, time, options)
|
80
|
+
rescue
|
81
|
+
warn "Exception dumping #{file}"
|
82
|
+
raise
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.load_data_file(file)
|
86
|
+
File.open(file, "rb") do |f|
|
87
|
+
return Marshal.load(f)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.load_yaml_file(file)
|
92
|
+
formatador = Formatador.new
|
93
|
+
obj = nil
|
94
|
+
File.open(file, "rb") do |f|
|
95
|
+
obj = Psych::load(f)
|
96
|
+
end
|
97
|
+
max = 0
|
98
|
+
return obj unless obj.kind_of?(Array)
|
99
|
+
seen = {}
|
100
|
+
idx =
|
101
|
+
obj.each do |elem|
|
102
|
+
next if elem.nil?
|
103
|
+
if elem.instance_variable_defined?("@id")
|
104
|
+
id = elem.instance_variable_get("@id")
|
105
|
+
else
|
106
|
+
id = nil
|
107
|
+
elem.instance_variable_set("@id", nil)
|
108
|
+
end
|
109
|
+
next if id.nil?
|
110
|
+
|
111
|
+
if seen.has_key?(id)
|
112
|
+
formatador.display_line("[red]#{file}: Duplicate ID #{id}[/]")
|
113
|
+
formatador.indent {
|
114
|
+
formatador.indent {
|
115
|
+
elem.pretty_inspect.split(/\n/).each do |line|
|
116
|
+
formatador.display_line("[red]#{line}[/]")
|
117
|
+
end
|
118
|
+
}
|
119
|
+
formatador.display_line
|
120
|
+
formatador.display_line("[red]Last seen at:\n[/]")
|
121
|
+
formatador.indent {
|
122
|
+
elem.pretty_inspect.split(/\n/).each do |line|
|
123
|
+
formatador.display_line("[red]#{line}[/]")
|
124
|
+
end
|
125
|
+
}
|
126
|
+
}
|
127
|
+
exit
|
128
|
+
end
|
129
|
+
seen[id] = elem
|
130
|
+
max = ((id + 1) unless id < max)
|
131
|
+
end
|
132
|
+
obj.each do |elem|
|
133
|
+
next if elem.nil?
|
134
|
+
id = elem.instance_variable_get("@id")
|
135
|
+
if id.nil?
|
136
|
+
elem.instance_variable_set("@id", max)
|
137
|
+
max += 1
|
138
|
+
end
|
139
|
+
end
|
140
|
+
return obj
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.load_raw_file(file)
|
144
|
+
File.open(file, "rb") do |f|
|
145
|
+
return f.read
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.load_save(file)
|
150
|
+
File.open(file, "rb") do |f|
|
151
|
+
data = []
|
152
|
+
while not f.eof?
|
153
|
+
o = Marshal.load(f)
|
154
|
+
data.push(o)
|
155
|
+
end
|
156
|
+
return data
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.load(loader, file)
|
161
|
+
return self.method(loader).call(file)
|
162
|
+
rescue
|
163
|
+
warn "Exception loading #{file}"
|
164
|
+
raise
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.scripts_to_text(dirs, src, dest, options)
|
168
|
+
formatador = Formatador.new
|
169
|
+
src_file = File.join(dirs[:data], src)
|
170
|
+
dest_file = File.join(dirs[:yaml], dest)
|
171
|
+
raise "Missing #{src}" unless File.exist?(src_file)
|
172
|
+
script_entries = load(:load_data_file, src_file)
|
173
|
+
check_time = !options[:force] && File.exist?(dest_file)
|
174
|
+
oldest_time = File.mtime(dest_file) if check_time
|
175
|
+
|
176
|
+
file_map, script_index, script_code = Hash.new(-1), [], {}
|
177
|
+
|
178
|
+
idx = 0
|
179
|
+
script_entries.each do |script|
|
180
|
+
idx += 1
|
181
|
+
magic_number, script_name, code = idx, script[1], inflate(script[2])
|
182
|
+
script_name.force_encoding("UTF-8")
|
183
|
+
|
184
|
+
if code.length > 0
|
185
|
+
filename = script_name.empty? ? 'blank' : sanitize_filename(script_name)
|
186
|
+
key = filename.upcase
|
187
|
+
value = (file_map[key] += 1)
|
188
|
+
actual_filename = filename + (value == 0 ? "" : ".#{value}") + RUBY_EXT
|
189
|
+
script_index.push([magic_number, script_name, actual_filename])
|
190
|
+
full_filename = File.join(dirs[:script], actual_filename)
|
191
|
+
script_code[full_filename] = code
|
192
|
+
check_time = false unless File.exist?(full_filename)
|
193
|
+
oldest_time = [File.mtime(full_filename), oldest_time].min if check_time
|
194
|
+
else
|
195
|
+
script_index.push([magic_number, script_name, nil])
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
src_time = File.mtime(src_file)
|
200
|
+
if check_time && (src_time - 1) < oldest_time
|
201
|
+
formatador.display_line("[yellow]Skipping scripts to text[/]") if $VERBOSE
|
202
|
+
else
|
203
|
+
formatador.display_line("[green]Converting scripts to text[/]") if $VERBOSE
|
204
|
+
dump(:dump_yaml_file, dest_file, script_index, src_time, options)
|
205
|
+
script_code.each { |file, code| dump(:dump_raw_file, file, code, src_time, options) }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.scripts_to_binary(dirs, src, dest, options)
|
210
|
+
formatador = Formatador.new
|
211
|
+
src_file = File.join(dirs[:yaml], src)
|
212
|
+
dest_file = File.join(dirs[:data], dest)
|
213
|
+
raise "Missing #{src}" unless File.exist?(src_file)
|
214
|
+
check_time = !options[:force] && File.exist?(dest_file)
|
215
|
+
newest_time = File.mtime(src_file) if check_time
|
216
|
+
|
217
|
+
index = load(:load_yaml_file, src_file)
|
218
|
+
script_entries = []
|
219
|
+
index.each do |entry|
|
220
|
+
magic_number, script_name, filename = entry
|
221
|
+
code = ''
|
222
|
+
if filename
|
223
|
+
full_filename = File.join(dirs[:script], filename)
|
224
|
+
raise "Missing script file #{filename}" unless File.exist?(full_filename)
|
225
|
+
newest_time = [File.mtime(full_filename), newest_time].max if check_time
|
226
|
+
code = load(:load_raw_file, full_filename)
|
227
|
+
end
|
228
|
+
script_entries.push([magic_number, script_name, deflate(code)])
|
229
|
+
end
|
230
|
+
if check_time && (newest_time - 1) < File.mtime(dest_file)
|
231
|
+
formatador.display_line("[yellow]Skipping scripts to binary[/]") if $VERBOSE
|
232
|
+
else
|
233
|
+
formatador.display_line("[green]Converting scripts to binary[/]") if $VERBOSE
|
234
|
+
dump(:dump_data_file, dest_file, script_entries, newest_time, options)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.process_file(file, src_file, dest_file, dest_ext, loader, dumper, options)
|
239
|
+
formatador = Formatador.new
|
240
|
+
fbase = File.basename(file, File.extname(file))
|
241
|
+
src_time = File.mtime(src_file)
|
242
|
+
begin
|
243
|
+
if !options[:force] && File.exist?(dest_file) && (src_time - 1) < File.mtime(dest_file)
|
244
|
+
formatador.display_line("[yellow]Skipping #{file}[/]") if $VERBOSE
|
245
|
+
else
|
246
|
+
formatador.display_line("[green]Converting #{file} to #{dest_ext}[/]") if $VERBOSE
|
247
|
+
data = load(loader, src_file)
|
248
|
+
dump(dumper, dest_file, data, src_time, options)
|
249
|
+
end
|
250
|
+
rescue ArgumentError
|
251
|
+
formatador.display_line("[_yellow_] Could not convert #{file} due to ArgumentError")
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.convert(src, dest, options)
|
256
|
+
f = Formatador.new
|
257
|
+
f.display_line("[green]#{options[:files].join('|')}")
|
258
|
+
files = options[:files]
|
259
|
+
files ||= Dir.entries(src[:directory])
|
260
|
+
files = files.select { |file| File.extname(file) == src[:ext] }
|
261
|
+
files -= src[:exclude]
|
262
|
+
files.each do |file|
|
263
|
+
src_file = File.join(src[:directory], file)
|
264
|
+
dest_file = File.join(dest[:directory], change_extension(file, dest[:ext]))
|
265
|
+
process_file(file, src_file, dest_file, dest[:ext], src[:load_file],
|
266
|
+
dest[:dump_file], options)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def self.convert_saves(base, src, dest, options)
|
271
|
+
save_files = files_with_extension(base, src[:ext])
|
272
|
+
save_files.each do |file|
|
273
|
+
src_file = File.join(base, file)
|
274
|
+
dest_file = File.join(base, change_extension(file, dest[:ext]))
|
275
|
+
|
276
|
+
process_file(file, src_file, dest_file, dest[:ext], src[:load_save],
|
277
|
+
dest[:dump_save], options)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# [version] one of :ace, :vx, :xp
|
282
|
+
# [direction] one of :data_bin_to_text, :data_text_to_bin, :save_bin_to_text,
|
283
|
+
# :save_text_to_bin, :scripts_bin_to_text, :scripts_text_to_bin,
|
284
|
+
# :all_text_to_bin, :all_bin_to_text
|
285
|
+
# [directory] directory that project file is in
|
286
|
+
# [options] :force - ignores file dates when converting (default false)
|
287
|
+
# :round_trip - create yaml data that matches original marshalled data skips
|
288
|
+
# data cleanup operations (default false)
|
289
|
+
# :line_width - line width form YAML files, -1 for no line width limit
|
290
|
+
# (default 130)
|
291
|
+
# :table_width - maximum number of entries per row for table data, -1 for no
|
292
|
+
# table row limit (default 20)
|
293
|
+
def self.serialize(version, direction, directory, options = {})
|
294
|
+
raise "#{directory} not found" unless File.exist?(directory)
|
295
|
+
|
296
|
+
setup_classes(version, options)
|
297
|
+
options = options.clone()
|
298
|
+
options[:sort] = true if [:vx, :xp].include?(version)
|
299
|
+
options[:flow_classes] = FLOW_CLASSES
|
300
|
+
options[:line_width] ||= 130
|
301
|
+
|
302
|
+
table_width = options[:table_width]
|
303
|
+
RGSS::reset_const(Table, :MAX_ROW_LENGTH, table_width ? table_width : 20)
|
304
|
+
|
305
|
+
base = File.realpath(directory)
|
306
|
+
|
307
|
+
dirs = {
|
308
|
+
:base => base,
|
309
|
+
:data => get_data_directory(base),
|
310
|
+
:yaml => get_yaml_directory(base),
|
311
|
+
:script => get_script_directory(base)
|
312
|
+
}
|
313
|
+
|
314
|
+
dirs.values.each do |d|
|
315
|
+
FileUtils.mkdir(d) unless File.exist?(d)
|
316
|
+
end
|
317
|
+
|
318
|
+
exts = {
|
319
|
+
:ace => ACE_DATA_EXT,
|
320
|
+
:vx => VX_DATA_EXT,
|
321
|
+
:xp => XP_DATA_EXT
|
322
|
+
}
|
323
|
+
|
324
|
+
yaml_scripts = SCRIPTS_BASE + YAML_EXT
|
325
|
+
yaml = {
|
326
|
+
:directory => dirs[:yaml],
|
327
|
+
:exclude => [yaml_scripts],
|
328
|
+
:ext => YAML_EXT,
|
329
|
+
:load_file => :load_yaml_file,
|
330
|
+
:dump_file => :dump_yaml_file,
|
331
|
+
:load_save => :load_yaml_file,
|
332
|
+
:dump_save => :dump_yaml_file
|
333
|
+
}
|
334
|
+
|
335
|
+
scripts = SCRIPTS_BASE + exts[version]
|
336
|
+
data = {
|
337
|
+
:directory => dirs[:data],
|
338
|
+
:exclude => [scripts],
|
339
|
+
:ext => exts[version],
|
340
|
+
:load_file => :load_data_file,
|
341
|
+
:dump_file => :dump_data_file,
|
342
|
+
:load_save => :load_save,
|
343
|
+
:dump_save => :dump_save
|
344
|
+
}
|
345
|
+
|
346
|
+
convert_saves = options[:database][:saves]
|
347
|
+
convert_scripts = options[:database][:scripts]
|
348
|
+
|
349
|
+
case direction
|
350
|
+
# below were not used in rv packer
|
351
|
+
when :data_bin_to_text
|
352
|
+
convert(data, yaml, options)
|
353
|
+
scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
|
354
|
+
when :data_text_to_bin
|
355
|
+
convert(yaml, data, options)
|
356
|
+
scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
|
357
|
+
when :save_bin_to_text
|
358
|
+
convert_saves(base, data, yaml, options) if convert_saves
|
359
|
+
when :save_text_to_bin
|
360
|
+
convert_saves(base, yaml, data, options) if convert_saves
|
361
|
+
when :scripts_bin_to_text
|
362
|
+
scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
|
363
|
+
when :scripts_text_to_bin
|
364
|
+
scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
|
365
|
+
# below are only options used by fusionpacker
|
366
|
+
when :all_bin_to_text
|
367
|
+
convert(data, yaml, options)
|
368
|
+
scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
|
369
|
+
convert_saves(base, data, yaml, options) if convert_saves
|
370
|
+
when :all_text_to_bin
|
371
|
+
convert(yaml, data, options)
|
372
|
+
scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
|
373
|
+
convert_saves(base, yaml, data, options) if convert_saves
|
374
|
+
else
|
375
|
+
raise "Unrecognized direction :#{direction}"
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
data/lib/RGSS.rb
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (c) 2013 Howard Jeng
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
5
|
+
software and associated documentation files (the "Software"), to deal in the Software
|
6
|
+
without restriction, including without limitation the rights to use, copy, modify, merge,
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
8
|
+
to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or
|
11
|
+
substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
14
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
15
|
+
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
16
|
+
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
17
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
18
|
+
DEALINGS IN THE SOFTWARE.
|
19
|
+
=end
|
20
|
+
|
21
|
+
require 'scanf'
|
22
|
+
|
23
|
+
class Table
|
24
|
+
def initialize(bytes)
|
25
|
+
@dim, @x, @y, @z, items, *@data = bytes.unpack('L5 S*')
|
26
|
+
raise "Size mismatch loading Table from data" unless items == @data.length
|
27
|
+
raise "Size mismatch loading Table from data" unless @x * @y * @z == items
|
28
|
+
end
|
29
|
+
|
30
|
+
MAX_ROW_LENGTH = 20
|
31
|
+
|
32
|
+
def encode_with(coder)
|
33
|
+
coder.style = Psych::Nodes::Mapping::BLOCK
|
34
|
+
|
35
|
+
coder['dim'] = @dim
|
36
|
+
coder['x'] = @x
|
37
|
+
coder['y'] = @y
|
38
|
+
coder['z'] = @z
|
39
|
+
|
40
|
+
if @x * @y * @z > 0
|
41
|
+
stride = @x < 2 ? (@y < 2 ? @z : @y) : @x
|
42
|
+
rows = @data.each_slice(stride).to_a
|
43
|
+
if MAX_ROW_LENGTH != -1 && stride > MAX_ROW_LENGTH
|
44
|
+
block_length = (stride + MAX_ROW_LENGTH - 1) / MAX_ROW_LENGTH
|
45
|
+
row_length = (stride + block_length - 1) / block_length
|
46
|
+
rows = rows.collect{|x| x.each_slice(row_length).to_a}.flatten(1)
|
47
|
+
end
|
48
|
+
rows = rows.collect{|x| x.collect{|y| "%04x" % y}.join(" ")}
|
49
|
+
coder['data'] = rows
|
50
|
+
else
|
51
|
+
coder['data'] = []
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def init_with(coder)
|
56
|
+
@dim = coder['dim']
|
57
|
+
@x = coder['x']
|
58
|
+
@y = coder['y']
|
59
|
+
@z = coder['z']
|
60
|
+
@data = coder['data'].collect{|x| x.split(" ").collect{|y| y.hex}}.flatten
|
61
|
+
items = @x * @y * @z
|
62
|
+
raise "Size mismatch loading Table from YAML" unless items == @data.length
|
63
|
+
end
|
64
|
+
|
65
|
+
def _dump(*ignored)
|
66
|
+
return [@dim, @x, @y, @z, @x * @y * @z, *@data].pack('L5 S*')
|
67
|
+
end
|
68
|
+
|
69
|
+
def self._load(bytes)
|
70
|
+
Table.new(bytes)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Color
|
75
|
+
def initialize(bytes, g = nil, b = nil)
|
76
|
+
# massive hack to support triple param color
|
77
|
+
if g && b
|
78
|
+
@r = bytes
|
79
|
+
@g = g
|
80
|
+
@b = b
|
81
|
+
@a = 255
|
82
|
+
else
|
83
|
+
@r, @g, @b, @a = *bytes.unpack('D4')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def _dump(*ignored)
|
88
|
+
return [@r, @g, @b, @a].pack('D4')
|
89
|
+
end
|
90
|
+
|
91
|
+
def self._load(bytes)
|
92
|
+
Color.new(bytes)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Tone
|
97
|
+
def initialize(bytes)
|
98
|
+
@r, @g, @b, @a = *bytes.unpack('D4')
|
99
|
+
end
|
100
|
+
|
101
|
+
def _dump(*ignored)
|
102
|
+
return [@r, @g, @b, @a].pack('D4')
|
103
|
+
end
|
104
|
+
|
105
|
+
def self._load(bytes)
|
106
|
+
Tone.new(bytes)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class Rect
|
111
|
+
def initialize(bytes)
|
112
|
+
@x, @y, @width, @height = *bytes.unpack('i4')
|
113
|
+
end
|
114
|
+
|
115
|
+
def _dump(*ignored)
|
116
|
+
return [@x, @y, @width, @height].pack('i4')
|
117
|
+
end
|
118
|
+
|
119
|
+
def self._load(bytes)
|
120
|
+
Rect.new(bytes)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
module RGSS
|
125
|
+
def self.remove_defined_method(scope, name)
|
126
|
+
scope.send(:remove_method, name) if scope.instance_methods(false).include?(name)
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.reset_method(scope, name, method)
|
130
|
+
remove_defined_method(scope, name)
|
131
|
+
scope.send(:define_method, name, method)
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.reset_const(scope, sym, value)
|
135
|
+
scope.send(:remove_const, sym) if scope.const_defined?(sym)
|
136
|
+
scope.send(:const_set, sym, value)
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.array_to_hash(arr, &block)
|
140
|
+
h = {}
|
141
|
+
arr.each_with_index do |val, index|
|
142
|
+
r = block_given? ? block.call(val) : val
|
143
|
+
h[index] = r unless r.nil?
|
144
|
+
end
|
145
|
+
if arr.length > 0
|
146
|
+
last = arr.length - 1
|
147
|
+
h[last] = nil unless h.has_key?(last)
|
148
|
+
end
|
149
|
+
return h
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.hash_to_array(hash)
|
153
|
+
arr = []
|
154
|
+
hash.each do |k, v|
|
155
|
+
arr[k] = v
|
156
|
+
end
|
157
|
+
return arr
|
158
|
+
end
|
159
|
+
|
160
|
+
require 'RGSS/BasicCoder'
|
161
|
+
require 'RPG'
|
162
|
+
|
163
|
+
# creates an empty class in a potentially nested scope
|
164
|
+
def self.process(root, name, *args)
|
165
|
+
if args.length > 0
|
166
|
+
process(root.const_get(name), *args)
|
167
|
+
else
|
168
|
+
root.const_set(name, Class.new) unless root.const_defined?(name, false)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# require 'infinitefusion/fusion'
|
173
|
+
|
174
|
+
# Object.const_set(:PokemonDataBox, PokemonDataBox) unless Object.const_defined?(name, false)
|
175
|
+
# other classes that don't need definitions
|
176
|
+
[ # RGSS data structures
|
177
|
+
[:RPG, :Actor], [:RPG, :Animation], [:RPG, :Animation, :Frame],
|
178
|
+
[:RPG, :Animation, :Timing], [:RPG, :Area], [:RPG, :Armor], [:RPG, :AudioFile],
|
179
|
+
[:RPG, :BaseItem], [:RPG, :BaseItem, :Feature], [:RPG, :BGM], [:RPG, :BGS],
|
180
|
+
[:RPG, :Class], [:RPG, :Class, :Learning], [:RPG, :CommonEvent], [:RPG, :Enemy],
|
181
|
+
[:RPG, :Enemy, :Action], [:RPG, :Enemy, :DropItem], [:RPG, :EquipItem],
|
182
|
+
[:RPG, :Event], [:RPG, :Event, :Page], [:RPG, :Event, :Page, :Condition],
|
183
|
+
[:RPG, :Event, :Page, :Graphic], [:RPG, :Item], [:RPG, :Map],
|
184
|
+
[:RPG, :Map, :Encounter], [:RPG, :MapInfo], [:RPG, :ME], [:RPG, :MoveCommand],
|
185
|
+
[:RPG, :MoveRoute], [:RPG, :SE], [:RPG, :Skill], [:RPG, :State],
|
186
|
+
[:RPG, :System, :Terms], [:RPG, :System, :TestBattler], [:RPG, :System, :Vehicle],
|
187
|
+
[:RPG, :System, :Words], [:RPG, :Tileset], [:RPG, :Troop], [:RPG, :Troop, :Member],
|
188
|
+
[:RPG, :Troop, :Page], [:RPG, :Troop, :Page, :Condition], [:RPG, :UsableItem],
|
189
|
+
[:RPG, :UsableItem, :Damage], [:RPG, :UsableItem, :Effect], [:RPG, :Weapon],
|
190
|
+
# Script classes serialized in save game files
|
191
|
+
[:Game_ActionResult], [:Game_Actor], [:Game_Actors], [:Game_BaseItem],
|
192
|
+
[:Game_BattleAction], [:Game_CommonEvent], [:Game_Enemy], [:Game_Event],
|
193
|
+
[:Game_Follower], [:Game_Followers], [:Game_Interpreter], [:Game_Map],
|
194
|
+
[:Game_Message], [:Game_Party], [:Game_Picture], [:Game_Pictures], [:Game_Player],
|
195
|
+
[:Game_System], [:Game_Timer], [:Game_Troop], [:Game_Screen], [:Game_Vehicle],
|
196
|
+
[:Interpreter],
|
197
|
+
[:PokemonDataCopy],
|
198
|
+
[:PBAnimations]
|
199
|
+
# coughing up classes for fusion
|
200
|
+
# [:PokemonDataBox, :Graphic]
|
201
|
+
].each {|x| process(Object, *x)}
|
202
|
+
|
203
|
+
|
204
|
+
def self.setup_system(version, options)
|
205
|
+
# convert variable and switch name arrays to a hash when serialized
|
206
|
+
# if round_trip isn't set change version_id to fixed number
|
207
|
+
if options[:round_trip]
|
208
|
+
iso = ->(val) { return val }
|
209
|
+
reset_method(RPG::System, :reduce_string, iso)
|
210
|
+
reset_method(RPG::System, :map_version, iso)
|
211
|
+
reset_method(Game_System, :map_version, iso)
|
212
|
+
else
|
213
|
+
reset_method(RPG::System, :reduce_string, ->(str) {
|
214
|
+
return nil if str.nil?
|
215
|
+
stripped = str.strip
|
216
|
+
return stripped.empty? ? nil : stripped
|
217
|
+
})
|
218
|
+
# These magic numbers should be different. If they are the same, the saved version
|
219
|
+
# of the map in save files will be used instead of any updated version of the map
|
220
|
+
reset_method(RPG::System, :map_version, ->(ignored) { return 12345678 })
|
221
|
+
reset_method(Game_System, :map_version, ->(ignored) { return 87654321 })
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.setup_interpreter(version)
|
226
|
+
# Game_Interpreter is marshalled differently in VX Ace
|
227
|
+
if version == :ace
|
228
|
+
reset_method(Game_Interpreter, :marshal_dump, ->{
|
229
|
+
return @data
|
230
|
+
})
|
231
|
+
reset_method(Game_Interpreter, :marshal_load, ->(obj) {
|
232
|
+
@data = obj
|
233
|
+
})
|
234
|
+
else
|
235
|
+
remove_defined_method(Game_Interpreter, :marshal_dump)
|
236
|
+
remove_defined_method(Game_Interpreter, :marshal_load)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.setup_event_command(version, options)
|
241
|
+
# format event commands to flow style for the event codes that aren't move commands
|
242
|
+
if options[:round_trip]
|
243
|
+
reset_method(RPG::EventCommand, :clean, ->{})
|
244
|
+
else
|
245
|
+
reset_method(RPG::EventCommand, :clean, ->{
|
246
|
+
@parameters[0].rstrip! if @code == 401
|
247
|
+
})
|
248
|
+
end
|
249
|
+
reset_const(RPG::EventCommand, :MOVE_LIST_CODE, version == :xp ? 209 : 205)
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.setup_classes(version, options)
|
253
|
+
setup_system(version, options)
|
254
|
+
setup_interpreter(version)
|
255
|
+
setup_event_command(version, options)
|
256
|
+
BasicCoder.set_ivars_methods(version)
|
257
|
+
end
|
258
|
+
|
259
|
+
FLOW_CLASSES = [Color, Tone, RPG::BGM, RPG::BGS, RPG::MoveCommand, RPG::SE]
|
260
|
+
|
261
|
+
SCRIPTS_BASE = 'Scripts'
|
262
|
+
|
263
|
+
ACE_DATA_EXT = '.rvdata2'
|
264
|
+
VX_DATA_EXT = '.rvdata'
|
265
|
+
XP_DATA_EXT = '.rxdata'
|
266
|
+
YAML_EXT = '.yaml'
|
267
|
+
RUBY_EXT = '.rb'
|
268
|
+
|
269
|
+
def self.get_data_directory(base)
|
270
|
+
return File.join(base, 'Data')
|
271
|
+
end
|
272
|
+
|
273
|
+
def self.get_yaml_directory(base)
|
274
|
+
return File.join(base, 'YAML')
|
275
|
+
end
|
276
|
+
|
277
|
+
def self.get_script_directory(base)
|
278
|
+
return File.join(base, 'Scripts')
|
279
|
+
end
|
280
|
+
|
281
|
+
class Game_Switches
|
282
|
+
include RGSS::BasicCoder
|
283
|
+
|
284
|
+
def encode(name, value)
|
285
|
+
return array_to_hash(value)
|
286
|
+
end
|
287
|
+
|
288
|
+
def decode(name, value)
|
289
|
+
return hash_to_array(value)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
class Game_Variables
|
294
|
+
include RGSS::BasicCoder
|
295
|
+
|
296
|
+
def encode(name, value)
|
297
|
+
return array_to_hash(value)
|
298
|
+
end
|
299
|
+
|
300
|
+
def decode(name, value)
|
301
|
+
return hash_to_array(value)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
class Game_SelfSwitches
|
306
|
+
include RGSS::BasicCoder
|
307
|
+
|
308
|
+
def encode(name, value)
|
309
|
+
return Hash[value.collect {|pair|
|
310
|
+
key, value = pair
|
311
|
+
next ["%03d %03d %s" % key, value]
|
312
|
+
}]
|
313
|
+
end
|
314
|
+
|
315
|
+
def decode(name, value)
|
316
|
+
return Hash[value.collect {|pair|
|
317
|
+
key, value = pair
|
318
|
+
next [key.scanf("%d %d %s"), value]
|
319
|
+
}]
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
class Game_System
|
324
|
+
include RGSS::BasicCoder
|
325
|
+
|
326
|
+
def encode(name, value)
|
327
|
+
if name == 'version_id'
|
328
|
+
return map_version(value)
|
329
|
+
else
|
330
|
+
return value
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
require 'RGSS/serialize'
|
336
|
+
end
|