rvpacker 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +34 -34
- data/Gemfile +4 -4
- data/LICENSE +17 -17
- data/README.md +78 -29
- data/Rakefile +2 -2
- data/bin/rvpacker +3 -1
- data/lib/RGSS.rb +320 -320
- data/lib/RGSS/BasicCoder.rb +0 -0
- data/lib/RGSS/psych_mods.rb +129 -129
- data/lib/RGSS/serialize.rb +384 -371
- data/lib/RPG.rb +0 -0
- data/lib/rvpacker/version.rb +3 -3
- data/rvpacker.gemspec +25 -25
- metadata +3 -3
data/lib/RGSS/BasicCoder.rb
CHANGED
File without changes
|
data/lib/RGSS/psych_mods.rb
CHANGED
@@ -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
|
data/lib/RGSS/serialize.rb
CHANGED
@@ -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
|
-
|
246
|
-
if !options[:
|
247
|
-
|
248
|
-
|
249
|
-
formatador.display_line("[
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
files
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
#
|
282
|
-
#
|
283
|
-
#
|
284
|
-
#
|
285
|
-
#
|
286
|
-
#
|
287
|
-
# :
|
288
|
-
# (default
|
289
|
-
# :
|
290
|
-
#
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
options
|
298
|
-
options[:
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
:
|
309
|
-
:
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
:
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
:
|
327
|
-
:
|
328
|
-
:
|
329
|
-
:
|
330
|
-
:
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
:
|
338
|
-
:
|
339
|
-
:
|
340
|
-
:
|
341
|
-
:
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
convert_saves
|
353
|
-
|
354
|
-
convert_saves
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
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
|