fusionpacker 0.1.1
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 +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
|