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.
- 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
|