fusionpacker 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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