rvpacker 1.0.0 → 1.2.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.
File without changes
@@ -1,129 +1,129 @@
1
- =begin
2
- This file contains significant portions of Psych 2.0.0 to modify behavior and to fix
3
- bugs. The license follows:
4
-
5
- Copyright 2009 Aaron Patterson, et al.
6
-
7
- Permission is hereby granted, free of charge, to any person obtaining a copy of this
8
- software and associated documentation files (the 'Software'), to deal in the Software
9
- without restriction, including without limitation the rights to use, copy, modify, merge,
10
- publish, distribute, sublicense, and/or sell copies of the Software, and to permit
11
- persons to whom the Software is furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all copies or
14
- substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
17
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
18
- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
19
- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20
- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
- DEALINGS IN THE SOFTWARE.
22
- =end
23
-
24
- gem 'psych', '2.0.0'
25
- require 'psych'
26
-
27
- if Psych::VERSION == '2.0.0'
28
- # Psych bugs:
29
- #
30
- # 1) Psych has a bug where it stores an anchor to the YAML for an object, but indexes
31
- # the reference by object_id. This doesn't keep the object alive, so if it gets garbage
32
- # collected, Ruby might generate an object with the same object_id and try to generate a
33
- # reference to the stored anchor. This monkey-patches the Registrar to keep the object
34
- # alive so incorrect references aren't generated. The bug is also present in Psych 1.3.4
35
- # but there isn't a convenient way to patch that.
36
- #
37
- # 2) Psych also doesn't create references and anchors for classes that implement
38
- # encode_with. This modifies dump_coder to handle that situation.
39
- #
40
- # Added two options:
41
- # :sort - sort hashes and instance variables for objects
42
- # :flow_classes - array of class types that will automatically emit with flow style
43
- # rather than block style
44
- module Psych
45
- module Visitors
46
- class YAMLTree < Psych::Visitors::Visitor
47
- class Registrar
48
- old_initialize = self.instance_method(:initialize)
49
- define_method(:initialize) do
50
- old_initialize.bind(self).call
51
- @obj_to_obj = {}
52
- end
53
-
54
- old_register = self.instance_method(:register)
55
- define_method(:register) do |target, node|
56
- old_register.bind(self).call(target, node)
57
- @obj_to_obj[target.object_id] = target
58
- end
59
- end
60
-
61
- remove_method(:visit_Hash)
62
- def visit_Hash o
63
- tag = o.class == ::Hash ? nil : "!ruby/hash:#{o.class}"
64
- implicit = !tag
65
-
66
- register(o, @emitter.start_mapping(nil, tag, implicit, Nodes::Mapping::BLOCK))
67
-
68
- keys = o.keys
69
- keys = keys.sort if @options[:sort]
70
- keys.each do |k|
71
- accept k
72
- accept o[k]
73
- end
74
-
75
- @emitter.end_mapping
76
- end
77
-
78
- remove_method(:visit_Object)
79
- def visit_Object o
80
- tag = Psych.dump_tags[o.class]
81
- unless tag
82
- klass = o.class == Object ? nil : o.class.name
83
- tag = ['!ruby/object', klass].compact.join(':')
84
- end
85
-
86
- if @options[:flow_classes] && @options[:flow_classes].include?(o.class)
87
- style = Nodes::Mapping::FLOW
88
- else
89
- style = Nodes::Mapping::BLOCK
90
- end
91
-
92
- map = @emitter.start_mapping(nil, tag, false, style)
93
- register(o, map)
94
-
95
- dump_ivars o
96
- @emitter.end_mapping
97
- end
98
-
99
- remove_method(:dump_coder)
100
- def dump_coder o
101
- @coders << o
102
- tag = Psych.dump_tags[o.class]
103
- unless tag
104
- klass = o.class == Object ? nil : o.class.name
105
- tag = ['!ruby/object', klass].compact.join(':')
106
- end
107
-
108
- c = Psych::Coder.new(tag)
109
- o.encode_with(c)
110
- register o, emit_coder(c)
111
- end
112
-
113
- remove_method(:dump_ivars)
114
- def dump_ivars target
115
- ivars = find_ivars target
116
- ivars = ivars.sort() if @options[:sort]
117
-
118
- ivars.each do |iv|
119
- @emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY)
120
- accept target.instance_variable_get(iv)
121
- end
122
- end
123
-
124
- end
125
- end
126
- end
127
- else
128
- warn "Warning: Psych 2.0.0 not detected" if $VERBOSE
129
- end
1
+ =begin
2
+ This file contains significant portions of Psych 2.0.0 to modify behavior and to fix
3
+ bugs. The license follows:
4
+
5
+ Copyright 2009 Aaron Patterson, et al.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this
8
+ software and associated documentation files (the 'Software'), to deal in the Software
9
+ without restriction, including without limitation the rights to use, copy, modify, merge,
10
+ publish, distribute, sublicense, and/or sell copies of the Software, and to permit
11
+ persons to whom the Software is furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all copies or
14
+ substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
17
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
18
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
19
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ DEALINGS IN THE SOFTWARE.
22
+ =end
23
+
24
+ gem 'psych', '2.0.0'
25
+ require 'psych'
26
+
27
+ if Psych::VERSION == '2.0.0'
28
+ # Psych bugs:
29
+ #
30
+ # 1) Psych has a bug where it stores an anchor to the YAML for an object, but indexes
31
+ # the reference by object_id. This doesn't keep the object alive, so if it gets garbage
32
+ # collected, Ruby might generate an object with the same object_id and try to generate a
33
+ # reference to the stored anchor. This monkey-patches the Registrar to keep the object
34
+ # alive so incorrect references aren't generated. The bug is also present in Psych 1.3.4
35
+ # but there isn't a convenient way to patch that.
36
+ #
37
+ # 2) Psych also doesn't create references and anchors for classes that implement
38
+ # encode_with. This modifies dump_coder to handle that situation.
39
+ #
40
+ # Added two options:
41
+ # :sort - sort hashes and instance variables for objects
42
+ # :flow_classes - array of class types that will automatically emit with flow style
43
+ # rather than block style
44
+ module Psych
45
+ module Visitors
46
+ class YAMLTree < Psych::Visitors::Visitor
47
+ class Registrar
48
+ old_initialize = self.instance_method(:initialize)
49
+ define_method(:initialize) do
50
+ old_initialize.bind(self).call
51
+ @obj_to_obj = {}
52
+ end
53
+
54
+ old_register = self.instance_method(:register)
55
+ define_method(:register) do |target, node|
56
+ old_register.bind(self).call(target, node)
57
+ @obj_to_obj[target.object_id] = target
58
+ end
59
+ end
60
+
61
+ remove_method(:visit_Hash)
62
+ def visit_Hash o
63
+ tag = o.class == ::Hash ? nil : "!ruby/hash:#{o.class}"
64
+ implicit = !tag
65
+
66
+ register(o, @emitter.start_mapping(nil, tag, implicit, Nodes::Mapping::BLOCK))
67
+
68
+ keys = o.keys
69
+ keys = keys.sort if @options[:sort]
70
+ keys.each do |k|
71
+ accept k
72
+ accept o[k]
73
+ end
74
+
75
+ @emitter.end_mapping
76
+ end
77
+
78
+ remove_method(:visit_Object)
79
+ def visit_Object o
80
+ tag = Psych.dump_tags[o.class]
81
+ unless tag
82
+ klass = o.class == Object ? nil : o.class.name
83
+ tag = ['!ruby/object', klass].compact.join(':')
84
+ end
85
+
86
+ if @options[:flow_classes] && @options[:flow_classes].include?(o.class)
87
+ style = Nodes::Mapping::FLOW
88
+ else
89
+ style = Nodes::Mapping::BLOCK
90
+ end
91
+
92
+ map = @emitter.start_mapping(nil, tag, false, style)
93
+ register(o, map)
94
+
95
+ dump_ivars o
96
+ @emitter.end_mapping
97
+ end
98
+
99
+ remove_method(:dump_coder)
100
+ def dump_coder o
101
+ @coders << o
102
+ tag = Psych.dump_tags[o.class]
103
+ unless tag
104
+ klass = o.class == Object ? nil : o.class.name
105
+ tag = ['!ruby/object', klass].compact.join(':')
106
+ end
107
+
108
+ c = Psych::Coder.new(tag)
109
+ o.encode_with(c)
110
+ register o, emit_coder(c)
111
+ end
112
+
113
+ remove_method(:dump_ivars)
114
+ def dump_ivars target
115
+ ivars = find_ivars target
116
+ ivars = ivars.sort() if @options[:sort]
117
+
118
+ ivars.each do |iv|
119
+ @emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY)
120
+ accept target.instance_variable_get(iv)
121
+ end
122
+ end
123
+
124
+ end
125
+ end
126
+ end
127
+ else
128
+ warn "Warning: Psych 2.0.0 not detected" if $VERBOSE
129
+ end
@@ -1,371 +1,384 @@
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
-
27
- module RGSS
28
- def self.change_extension(file, new_ext)
29
- return File.basename(file, '.*') + new_ext
30
- end
31
-
32
- def self.sanitize_filename(filename)
33
- return filename.gsub(/[^0-9A-Za-z]+/, '_')
34
- end
35
-
36
- def self.files_with_extension(directory, extension)
37
- return Dir.entries(directory).select{|file| File.extname(file) == extension}
38
- end
39
-
40
- def self.inflate(str)
41
- text = Zlib::Inflate.inflate(str)
42
- return text.force_encoding("UTF-8")
43
- end
44
-
45
- def self.deflate(str)
46
- return Zlib::Deflate.deflate(str, Zlib::BEST_COMPRESSION)
47
- end
48
-
49
-
50
- def self.dump_data_file(file, data, time, options)
51
- File.open(file, "wb") do |f|
52
- Marshal.dump(data, f)
53
- end
54
- File.utime(time, time, file)
55
- end
56
-
57
- def self.dump_yaml_file(file, data, time, options)
58
- File.open(file, "wb") do |f|
59
- Psych::dump(data, f, options)
60
- end
61
- File.utime(time, time, file)
62
- end
63
-
64
- def self.dump_save(file, data, time, options)
65
- File.open(file, "wb") do |f|
66
- data.each do |chunk|
67
- Marshal.dump(chunk, f)
68
- end
69
- end
70
- File.utime(time, time, file)
71
- end
72
-
73
- def self.dump_raw_file(file, data, time, options)
74
- File.open(file, "wb") do |f|
75
- f.write(data)
76
- end
77
- File.utime(time, time, file)
78
- end
79
-
80
- def self.dump(dumper, file, data, time, options)
81
- self.method(dumper).call(file, data, time, options)
82
- rescue
83
- warn "Exception dumping #{file}"
84
- raise
85
- end
86
-
87
-
88
- def self.load_data_file(file)
89
- File.open(file, "rb") do |f|
90
- return Marshal.load(f)
91
- end
92
- end
93
-
94
- def self.load_yaml_file(file)
95
- formatador = Formatador.new
96
- obj = nil
97
- File.open(file, "rb") do |f|
98
- obj = Psych::load(f)
99
- end
100
- max = 0
101
- return obj unless obj.kind_of?(Array)
102
- seen = {}
103
- idx =
104
- obj.each do |elem|
105
- next if elem.nil?
106
- if elem.instance_variable_defined?("@id")
107
- id = elem.instance_variable_get("@id")
108
- else
109
- id = nil
110
- elem.instance_variable_set("@id", nil)
111
- end
112
- next if id.nil?
113
-
114
- if seen.has_key?(id)
115
- formatador.display_line("[red]#{file}: Duplicate ID #{id}[/]")
116
- formatador.indent {
117
- formatador.indent {
118
- elem.pretty_inspect.split(/\n/).each do |line|
119
- formatador.display_line("[red]#{line}[/]")
120
- end
121
- }
122
- formatador.display_line
123
- formatador.display_line("[red]Last seen at:\n[/]")
124
- formatador.indent {
125
- elem.pretty_inspect.split(/\n/).each do |line|
126
- formatador.display_line("[red]#{line}[/]")
127
- end
128
- }
129
- }
130
- exit
131
- end
132
- seen[id] = elem
133
- max = ((id + 1) unless id < max)
134
- end
135
- obj.each do |elem|
136
- next if elem.nil?
137
- id = elem.instance_variable_get("@id")
138
- if id.nil?
139
- elem.instance_variable_set("@id", max)
140
- max += 1
141
- end
142
- end
143
- return obj
144
- end
145
-
146
- def self.load_raw_file(file)
147
- File.open(file, "rb") do |f|
148
- return f.read
149
- end
150
- end
151
-
152
- def self.load_save(file)
153
- File.open(file, "rb") do |f|
154
- data = []
155
- while not f.eof?
156
- o = Marshal.load(f)
157
- data.push(o)
158
- end
159
- return data
160
- end
161
- end
162
-
163
- def self.load(loader, file)
164
- return self.method(loader).call(file)
165
- rescue
166
- warn "Exception loading #{file}"
167
- raise
168
- end
169
-
170
-
171
- def self.scripts_to_text(dirs, src, dest, options)
172
- formatador = Formatador.new
173
- src_file = File.join(dirs[:data], src)
174
- dest_file = File.join(dirs[:yaml], dest)
175
- raise "Missing #{src}" unless File.exists?(src_file)
176
-
177
- script_entries = load(:load_data_file, src_file)
178
- check_time = !options[:force] && File.exists?(dest_file)
179
- oldest_time = File.mtime(dest_file) if check_time
180
-
181
- file_map, script_index, script_code = Hash.new(-1), [], {}
182
-
183
- idx=0
184
- script_entries.each do |script|
185
- idx += 1
186
- magic_number, script_name, code = idx, script[1], inflate(script[2])
187
- script_name.force_encoding("UTF-8")
188
-
189
- if code.length > 0
190
- filename = script_name.empty? ? 'blank' : sanitize_filename(script_name)
191
- key = filename.upcase
192
- value = (file_map[key] += 1)
193
- actual_filename = filename + (value == 0 ? "" : ".#{value}") + RUBY_EXT
194
- script_index.push([magic_number, script_name, actual_filename])
195
- full_filename = File.join(dirs[:script], actual_filename)
196
- script_code[full_filename] = code
197
- check_time = false unless File.exists?(full_filename)
198
- oldest_time = [File.mtime(full_filename), oldest_time].min if check_time
199
- else
200
- script_index.push([magic_number, script_name, nil])
201
- end
202
- end
203
-
204
- src_time = File.mtime(src_file)
205
- if check_time && (src_time - 1) < oldest_time
206
- formatador.display_line("[yellow]Skipping scripts to text[/]") if $VERBOSE
207
- else
208
- formatador.display_line("[green]Converting scripts to text[/]") if $VERBOSE
209
- dump(:dump_yaml_file, dest_file, script_index, src_time, options)
210
- script_code.each {|file, code| dump(:dump_raw_file, file, code, src_time, options)}
211
- end
212
- end
213
-
214
- def self.scripts_to_binary(dirs, src, dest, options)
215
- formatador = Formatador.new
216
- src_file = File.join(dirs[:yaml], src)
217
- dest_file = File.join(dirs[:data], dest)
218
- raise "Missing #{src}" unless File.exists?(src_file)
219
- check_time = !options[:force] && File.exists?(dest_file)
220
- newest_time = File.mtime(src_file) if check_time
221
-
222
- index = load(:load_yaml_file, src_file)
223
- script_entries = []
224
- index.each do |entry|
225
- magic_number, script_name, filename = entry
226
- code = ''
227
- if filename
228
- full_filename = File.join(dirs[:script], filename)
229
- raise "Missing script file #{filename}" unless File.exists?(full_filename)
230
- newest_time = [File.mtime(full_filename), newest_time].max if check_time
231
- code = load(:load_raw_file, full_filename)
232
- end
233
- script_entries.push([magic_number, script_name, deflate(code)])
234
- end
235
- if check_time && (newest_time - 1) < File.mtime(dest_file)
236
- formatador.display_line("[yellow]Skipping scripts to binary[/]") if $VERBOSE
237
- else
238
- formatador.display_line("[green]Converting scripts to binary[/]") if $VERBOSE
239
- dump(:dump_data_file, dest_file, script_entries, newest_time, options)
240
- end
241
- end
242
-
243
- def self.process_file(file, src_file, dest_file, dest_ext, loader, dumper, options)
244
- formatador = Formatador.new
245
- src_time = File.mtime(src_file)
246
- if !options[:force] && File.exists?(dest_file) && (src_time - 1) < File.mtime(dest_file)
247
- formatador.display_line("[yellow]Skipping #{file}[/]") if $VERBOSE
248
- else
249
- formatador.display_line("[green]Converting #{file} to #{dest_ext}[/]") if $VERBOSE
250
- data = load(loader, src_file)
251
- dump(dumper, dest_file, data, src_time, options)
252
- end
253
- end
254
-
255
- def self.convert(src, dest, options)
256
- files = files_with_extension(src[:directory], src[:ext])
257
- files -= src[:exclude]
258
-
259
- files.each do |file|
260
- src_file = File.join(src[:directory], file)
261
- dest_file = File.join(dest[:directory], change_extension(file, dest[:ext]))
262
-
263
- process_file(file, src_file, dest_file, dest[:ext], src[:load_file],
264
- dest[:dump_file], options)
265
- end
266
- end
267
-
268
- def self.convert_saves(base, src, dest, options)
269
- save_files = files_with_extension(base, src[:ext])
270
- save_files.each do |file|
271
- src_file = File.join(base, file)
272
- dest_file = File.join(base, change_extension(file, dest[:ext]))
273
-
274
- process_file(file, src_file, dest_file, dest[:ext], src[:load_save],
275
- dest[:dump_save], options)
276
- end
277
- end
278
-
279
- # [version] one of :ace, :vx, :xp
280
- # [direction] one of :data_bin_to_text, :data_text_to_bin, :save_bin_to_text,
281
- # :save_text_to_bin, :scripts_bin_to_text, :scripts_text_to_bin,
282
- # :all_text_to_bin, :all_bin_to_text
283
- # [directory] directory that project file is in
284
- # [options] :force - ignores file dates when converting (default false)
285
- # :round_trip - create yaml data that matches original marshalled data skips
286
- # data cleanup operations (default false)
287
- # :line_width - line width form YAML files, -1 for no line width limit
288
- # (default 130)
289
- # :table_width - maximum number of entries per row for table data, -1 for no
290
- # table row limit (default 20)
291
- def self.serialize(version, direction, directory, options = {})
292
- raise "#{directory} not found" unless File.exist?(directory)
293
-
294
- setup_classes(version, options)
295
- options = options.clone()
296
- options[:sort] = true if [:vx, :xp].include?(version)
297
- options[:flow_classes] = FLOW_CLASSES
298
- options[:line_width] ||= 130
299
-
300
- table_width = options[:table_width]
301
- RGSS::reset_const(Table, :MAX_ROW_LENGTH, table_width ? table_width : 20)
302
-
303
- base = File.realpath(directory)
304
-
305
- dirs = {
306
- :base => base,
307
- :data => get_data_directory(base),
308
- :yaml => get_yaml_directory(base),
309
- :script => get_script_directory(base)
310
- }
311
-
312
- dirs.values.each do |d|
313
- FileUtils.mkdir(d) unless File.exists?(d)
314
- end
315
-
316
- exts = {
317
- :ace => ACE_DATA_EXT,
318
- :vx => VX_DATA_EXT,
319
- :xp => XP_DATA_EXT
320
- }
321
-
322
- yaml_scripts = SCRIPTS_BASE + YAML_EXT
323
- yaml = {
324
- :directory => dirs[:yaml],
325
- :exclude => [yaml_scripts],
326
- :ext => YAML_EXT,
327
- :load_file => :load_yaml_file,
328
- :dump_file => :dump_yaml_file,
329
- :load_save => :load_yaml_file,
330
- :dump_save => :dump_yaml_file
331
- }
332
-
333
- scripts = SCRIPTS_BASE + exts[version]
334
- data = {
335
- :directory => dirs[:data],
336
- :exclude => [scripts],
337
- :ext => exts[version],
338
- :load_file => :load_data_file,
339
- :dump_file => :dump_data_file,
340
- :load_save => :load_save,
341
- :dump_save => :dump_save
342
- }
343
-
344
- case direction
345
- when :data_bin_to_text
346
- convert(data, yaml, options)
347
- scripts_to_text(dirs, scripts, yaml_scripts, options)
348
- when :data_text_to_bin
349
- convert(yaml, data, options)
350
- scripts_to_binary(dirs, yaml_scripts, scripts, options)
351
- when :save_bin_to_text
352
- convert_saves(base, data, yaml, options)
353
- when :save_text_to_bin
354
- convert_saves(base, yaml, data, options)
355
- when :scripts_bin_to_text
356
- scripts_to_text(dirs, scripts, yaml_scripts, options)
357
- when :scripts_text_to_bin
358
- scripts_to_binary(dirs, yaml_scripts, scripts, options)
359
- when :all_bin_to_text
360
- convert(data, yaml, options)
361
- scripts_to_text(dirs, scripts, yaml_scripts, options)
362
- convert_saves(base, data, yaml, options)
363
- when :all_text_to_bin
364
- convert(yaml, data, options)
365
- scripts_to_binary(dirs, yaml_scripts, scripts, options)
366
- convert_saves(base, yaml, data, options)
367
- else
368
- raise "Unrecognized direction :#{direction}"
369
- end
370
- end
371
- end
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
+
27
+ module RGSS
28
+ def self.change_extension(file, new_ext)
29
+ return File.basename(file, '.*') + new_ext
30
+ end
31
+
32
+ def self.sanitize_filename(filename)
33
+ return filename.gsub(/[^0-9A-Za-z]+/, '_')
34
+ end
35
+
36
+ def self.files_with_extension(directory, extension)
37
+ return Dir.entries(directory).select{|file| File.extname(file) == extension}
38
+ end
39
+
40
+ def self.inflate(str)
41
+ text = Zlib::Inflate.inflate(str)
42
+ return text.force_encoding("UTF-8")
43
+ end
44
+
45
+ def self.deflate(str)
46
+ return Zlib::Deflate.deflate(str, Zlib::BEST_COMPRESSION)
47
+ end
48
+
49
+
50
+ def self.dump_data_file(file, data, time, options)
51
+ File.open(file, "wb") do |f|
52
+ Marshal.dump(data, f)
53
+ end
54
+ File.utime(time, time, file)
55
+ end
56
+
57
+ def self.dump_yaml_file(file, data, time, options)
58
+ File.open(file, "wb") do |f|
59
+ Psych::dump(data, f, options)
60
+ end
61
+ File.utime(time, time, file)
62
+ end
63
+
64
+ def self.dump_save(file, data, time, options)
65
+ File.open(file, "wb") do |f|
66
+ data.each do |chunk|
67
+ Marshal.dump(chunk, f)
68
+ end
69
+ end
70
+ File.utime(time, time, file)
71
+ end
72
+
73
+ def self.dump_raw_file(file, data, time, options)
74
+ File.open(file, "wb") do |f|
75
+ f.write(data)
76
+ end
77
+ File.utime(time, time, file)
78
+ end
79
+
80
+ def self.dump(dumper, file, data, time, options)
81
+ self.method(dumper).call(file, data, time, options)
82
+ rescue
83
+ warn "Exception dumping #{file}"
84
+ raise
85
+ end
86
+
87
+
88
+ def self.load_data_file(file)
89
+ File.open(file, "rb") do |f|
90
+ return Marshal.load(f)
91
+ end
92
+ end
93
+
94
+ def self.load_yaml_file(file)
95
+ formatador = Formatador.new
96
+ obj = nil
97
+ File.open(file, "rb") do |f|
98
+ obj = Psych::load(f)
99
+ end
100
+ max = 0
101
+ return obj unless obj.kind_of?(Array)
102
+ seen = {}
103
+ idx =
104
+ obj.each do |elem|
105
+ next if elem.nil?
106
+ if elem.instance_variable_defined?("@id")
107
+ id = elem.instance_variable_get("@id")
108
+ else
109
+ id = nil
110
+ elem.instance_variable_set("@id", nil)
111
+ end
112
+ next if id.nil?
113
+
114
+ if seen.has_key?(id)
115
+ formatador.display_line("[red]#{file}: Duplicate ID #{id}[/]")
116
+ formatador.indent {
117
+ formatador.indent {
118
+ elem.pretty_inspect.split(/\n/).each do |line|
119
+ formatador.display_line("[red]#{line}[/]")
120
+ end
121
+ }
122
+ formatador.display_line
123
+ formatador.display_line("[red]Last seen at:\n[/]")
124
+ formatador.indent {
125
+ elem.pretty_inspect.split(/\n/).each do |line|
126
+ formatador.display_line("[red]#{line}[/]")
127
+ end
128
+ }
129
+ }
130
+ exit
131
+ end
132
+ seen[id] = elem
133
+ max = ((id + 1) unless id < max)
134
+ end
135
+ obj.each do |elem|
136
+ next if elem.nil?
137
+ id = elem.instance_variable_get("@id")
138
+ if id.nil?
139
+ elem.instance_variable_set("@id", max)
140
+ max += 1
141
+ end
142
+ end
143
+ return obj
144
+ end
145
+
146
+ def self.load_raw_file(file)
147
+ File.open(file, "rb") do |f|
148
+ return f.read
149
+ end
150
+ end
151
+
152
+ def self.load_save(file)
153
+ File.open(file, "rb") do |f|
154
+ data = []
155
+ while not f.eof?
156
+ o = Marshal.load(f)
157
+ data.push(o)
158
+ end
159
+ return data
160
+ end
161
+ end
162
+
163
+ def self.load(loader, file)
164
+ return self.method(loader).call(file)
165
+ rescue
166
+ warn "Exception loading #{file}"
167
+ raise
168
+ end
169
+
170
+
171
+ def self.scripts_to_text(dirs, src, dest, options)
172
+ formatador = Formatador.new
173
+ src_file = File.join(dirs[:data], src)
174
+ dest_file = File.join(dirs[:yaml], dest)
175
+ raise "Missing #{src}" unless File.exists?(src_file)
176
+
177
+ script_entries = load(:load_data_file, src_file)
178
+ check_time = !options[:force] && File.exists?(dest_file)
179
+ oldest_time = File.mtime(dest_file) if check_time
180
+
181
+ file_map, script_index, script_code = Hash.new(-1), [], {}
182
+
183
+ idx=0
184
+ script_entries.each do |script|
185
+ idx += 1
186
+ magic_number, script_name, code = idx, script[1], inflate(script[2])
187
+ script_name.force_encoding("UTF-8")
188
+
189
+ if code.length > 0
190
+ filename = script_name.empty? ? 'blank' : sanitize_filename(script_name)
191
+ key = filename.upcase
192
+ value = (file_map[key] += 1)
193
+ actual_filename = filename + (value == 0 ? "" : ".#{value}") + RUBY_EXT
194
+ script_index.push([magic_number, script_name, actual_filename])
195
+ full_filename = File.join(dirs[:script], actual_filename)
196
+ script_code[full_filename] = code
197
+ check_time = false unless File.exists?(full_filename)
198
+ oldest_time = [File.mtime(full_filename), oldest_time].min if check_time
199
+ else
200
+ script_index.push([magic_number, script_name, nil])
201
+ end
202
+ end
203
+
204
+ src_time = File.mtime(src_file)
205
+ if check_time && (src_time - 1) < oldest_time
206
+ formatador.display_line("[yellow]Skipping scripts to text[/]") if $VERBOSE
207
+ else
208
+ formatador.display_line("[green]Converting scripts to text[/]") if $VERBOSE
209
+ dump(:dump_yaml_file, dest_file, script_index, src_time, options)
210
+ script_code.each {|file, code| dump(:dump_raw_file, file, code, src_time, options)}
211
+ end
212
+ end
213
+
214
+ def self.scripts_to_binary(dirs, src, dest, options)
215
+ formatador = Formatador.new
216
+ src_file = File.join(dirs[:yaml], src)
217
+ dest_file = File.join(dirs[:data], dest)
218
+ raise "Missing #{src}" unless File.exists?(src_file)
219
+ check_time = !options[:force] && File.exists?(dest_file)
220
+ newest_time = File.mtime(src_file) if check_time
221
+
222
+ index = load(:load_yaml_file, src_file)
223
+ script_entries = []
224
+ index.each do |entry|
225
+ magic_number, script_name, filename = entry
226
+ code = ''
227
+ if filename
228
+ full_filename = File.join(dirs[:script], filename)
229
+ raise "Missing script file #{filename}" unless File.exists?(full_filename)
230
+ newest_time = [File.mtime(full_filename), newest_time].max if check_time
231
+ code = load(:load_raw_file, full_filename)
232
+ end
233
+ script_entries.push([magic_number, script_name, deflate(code)])
234
+ end
235
+ if check_time && (newest_time - 1) < File.mtime(dest_file)
236
+ formatador.display_line("[yellow]Skipping scripts to binary[/]") if $VERBOSE
237
+ else
238
+ formatador.display_line("[green]Converting scripts to binary[/]") if $VERBOSE
239
+ dump(:dump_data_file, dest_file, script_entries, newest_time, options)
240
+ end
241
+ end
242
+
243
+ def self.process_file(file, src_file, dest_file, dest_ext, loader, dumper, options)
244
+ formatador = Formatador.new
245
+ fbase = File.basename(file, File.extname(file))
246
+ return if (! options[:database].nil? ) and (options[:database].downcase != fbase.downcase)
247
+ src_time = File.mtime(src_file)
248
+ if !options[:force] && File.exists?(dest_file) && (src_time - 1) < File.mtime(dest_file)
249
+ formatador.display_line("[yellow]Skipping #{file}[/]") if $VERBOSE
250
+ else
251
+ formatador.display_line("[green]Converting #{file} to #{dest_ext}[/]") if $VERBOSE
252
+ data = load(loader, src_file)
253
+ dump(dumper, dest_file, data, src_time, options)
254
+ end
255
+ end
256
+
257
+ def self.convert(src, dest, options)
258
+ files = files_with_extension(src[:directory], src[:ext])
259
+ files -= src[:exclude]
260
+
261
+ files.each do |file|
262
+ src_file = File.join(src[:directory], file)
263
+ dest_file = File.join(dest[:directory], change_extension(file, dest[:ext]))
264
+
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.exists?(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
+ if options[:database].nil? or options[:database].downcase == 'scripts'
347
+ convert_scripts = true
348
+ else
349
+ convert_scripts = false
350
+ end
351
+ if options[:database].nil? or options[:database].downcase == 'saves'
352
+ convert_saves = true
353
+ else
354
+ convert_saves = false
355
+ end
356
+
357
+ case direction
358
+ when :data_bin_to_text
359
+ convert(data, yaml, options)
360
+ scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
361
+ when :data_text_to_bin
362
+ convert(yaml, data, options)
363
+ scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
364
+ when :save_bin_to_text
365
+ convert_saves(base, data, yaml, options) if convert_saves
366
+ when :save_text_to_bin
367
+ convert_saves(base, yaml, data, options) if convert_saves
368
+ when :scripts_bin_to_text
369
+ scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
370
+ when :scripts_text_to_bin
371
+ scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
372
+ when :all_bin_to_text
373
+ convert(data, yaml, options)
374
+ scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
375
+ convert_saves(base, data, yaml, options) if convert_saves
376
+ when :all_text_to_bin
377
+ convert(yaml, data, options)
378
+ scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
379
+ convert_saves(base, yaml, data, options) if convert_saves
380
+ else
381
+ raise "Unrecognized direction :#{direction}"
382
+ end
383
+ end
384
+ end