rvpacker 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rvpacker.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Copyright (c) 2013 Howard Jeng
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
+ software and associated documentation files (the "Software"), to deal in the Software
5
+ without restriction, including without limitation the rights to use, copy, modify, merge,
6
+ publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
+ to whom the Software is furnished to do so, subject to the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be included in all copies or
10
+ substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Rvpacker
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rvpacker'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rvpacker
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/rvpacker/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'trollop'
5
+ require 'RGSS'
6
+
7
+ opts = Trollop::options do
8
+ opt :action, "Action to perform on project (unpack|pack)", :short => "a", :type => String
9
+ opt :project, "RPG Maker Project directory", :short => "d", :type => String
10
+ opt :force, "Update target even when source is older than target", :short => "f"
11
+ opt :project_type, "Project type (vx|ace|xp)", :short => "t", :type => String
12
+ opt :verbose, "Print verbose information while processing", :short => "V"
13
+ end
14
+
15
+ directions = {
16
+ "unpack" => :all_bin_to_text,
17
+ "pack" => :all_text_to_bin
18
+ }
19
+ projecttypes = {
20
+ "vx" => :vx,
21
+ "ace" => :ace,
22
+ "xp" => :xp
23
+ }
24
+ $VERBOSE=opts[:verbose]
25
+
26
+ RGSS.serialize(projecttypes[opts[:project_type]],
27
+ directions[opts[:action]],
28
+ opts[:project],
29
+ { :force => (opts[:force] ? true : false),
30
+ :line_width => -1,
31
+ :table_width => -1
32
+ }
33
+ )
34
+
@@ -0,0 +1,320 @@
1
+ =begin
2
+ Copyright (c) 2013 Howard Jeng
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this
5
+ software and associated documentation files (the "Software"), to deal in the Software
6
+ without restriction, including without limitation the rights to use, copy, modify, merge,
7
+ publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
8
+ to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or
11
+ substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
15
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
16
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
+ DEALINGS IN THE SOFTWARE.
19
+ =end
20
+
21
+ require 'scanf'
22
+
23
+ class Table
24
+ def initialize(bytes)
25
+ @dim, @x, @y, @z, items, *@data = bytes.unpack('L5 S*')
26
+ raise "Size mismatch loading Table from data" unless items == @data.length
27
+ raise "Size mismatch loading Table from data" unless @x * @y * @z == items
28
+ end
29
+
30
+ MAX_ROW_LENGTH = 20
31
+
32
+ def encode_with(coder)
33
+ coder.style = Psych::Nodes::Mapping::BLOCK
34
+
35
+ coder['dim'] = @dim
36
+ coder['x'] = @x
37
+ coder['y'] = @y
38
+ coder['z'] = @z
39
+
40
+ if @x * @y * @z > 0
41
+ stride = @x < 2 ? (@y < 2 ? @z : @y) : @x
42
+ rows = @data.each_slice(stride).to_a
43
+ if MAX_ROW_LENGTH != -1 && stride > MAX_ROW_LENGTH
44
+ block_length = (stride + MAX_ROW_LENGTH - 1) / MAX_ROW_LENGTH
45
+ row_length = (stride + block_length - 1) / block_length
46
+ rows = rows.collect{|x| x.each_slice(row_length).to_a}.flatten(1)
47
+ end
48
+ rows = rows.collect{|x| x.collect{|y| "%04x" % y}.join(" ")}
49
+ coder['data'] = rows
50
+ else
51
+ coder['data'] = []
52
+ end
53
+ end
54
+
55
+ def init_with(coder)
56
+ @dim = coder['dim']
57
+ @x = coder['x']
58
+ @y = coder['y']
59
+ @z = coder['z']
60
+ @data = coder['data'].collect{|x| x.split(" ").collect{|y| y.hex}}.flatten
61
+ items = @x * @y * @z
62
+ raise "Size mismatch loading Table from YAML" unless items == @data.length
63
+ end
64
+
65
+ def _dump(*ignored)
66
+ return [@dim, @x, @y, @z, @x * @y * @z, *@data].pack('L5 S*')
67
+ end
68
+
69
+ def self._load(bytes)
70
+ Table.new(bytes)
71
+ end
72
+ end
73
+
74
+ class Color
75
+ def initialize(bytes)
76
+ @r, @g, @b, @a = *bytes.unpack('D4')
77
+ end
78
+
79
+ def _dump(*ignored)
80
+ return [@r, @g, @b, @a].pack('D4')
81
+ end
82
+
83
+ def self._load(bytes)
84
+ Color.new(bytes)
85
+ end
86
+ end
87
+
88
+ class Tone
89
+ def initialize(bytes)
90
+ @r, @g, @b, @a = *bytes.unpack('D4')
91
+ end
92
+
93
+ def _dump(*ignored)
94
+ return [@r, @g, @b, @a].pack('D4')
95
+ end
96
+
97
+ def self._load(bytes)
98
+ Tone.new(bytes)
99
+ end
100
+ end
101
+
102
+ class Rect
103
+ def initialize(bytes)
104
+ @x, @y, @width, @height = *bytes.unpack('i4')
105
+ end
106
+
107
+ def _dump(*ignored)
108
+ return [@x, @y, @width, @height].pack('i4')
109
+ end
110
+
111
+ def self._load(bytes)
112
+ Rect.new(bytes)
113
+ end
114
+ end
115
+
116
+ module RGSS
117
+ def self.remove_defined_method(scope, name)
118
+ scope.send(:remove_method, name) if scope.instance_methods(false).include?(name)
119
+ end
120
+
121
+ def self.reset_method(scope, name, method)
122
+ remove_defined_method(scope, name)
123
+ scope.send(:define_method, name, method)
124
+ end
125
+
126
+ def self.reset_const(scope, sym, value)
127
+ scope.send(:remove_const, sym) if scope.const_defined?(sym)
128
+ scope.send(:const_set, sym, value)
129
+ end
130
+
131
+ def self.array_to_hash(arr, &block)
132
+ h = {}
133
+ arr.each_with_index do |val, index|
134
+ r = block_given? ? block.call(val) : val
135
+ h[index] = r unless r.nil?
136
+ end
137
+ if arr.length > 0
138
+ last = arr.length - 1
139
+ h[last] = nil unless h.has_key?(last)
140
+ end
141
+ return h
142
+ end
143
+
144
+ def self.hash_to_array(hash)
145
+ arr = []
146
+ hash.each do |k, v|
147
+ arr[k] = v
148
+ end
149
+ return arr
150
+ end
151
+
152
+ require 'RGSS/BasicCoder'
153
+ require 'RPG'
154
+
155
+ # creates an empty class in a potentially nested scope
156
+ def self.process(root, name, *args)
157
+ if args.length > 0
158
+ process(root.const_get(name), *args)
159
+ else
160
+ root.const_set(name, Class.new) unless root.const_defined?(name, false)
161
+ end
162
+ end
163
+
164
+ # other classes that don't need definitions
165
+ [ # RGSS data structures
166
+ [:RPG, :Actor], [:RPG, :Animation], [:RPG, :Animation, :Frame],
167
+ [:RPG, :Animation, :Timing], [:RPG, :Area], [:RPG, :Armor], [:RPG, :AudioFile],
168
+ [:RPG, :BaseItem], [:RPG, :BaseItem, :Feature], [:RPG, :BGM], [:RPG, :BGS],
169
+ [:RPG, :Class], [:RPG, :Class, :Learning], [:RPG, :CommonEvent], [:RPG, :Enemy],
170
+ [:RPG, :Enemy, :Action], [:RPG, :Enemy, :DropItem], [:RPG, :EquipItem],
171
+ [:RPG, :Event], [:RPG, :Event, :Page], [:RPG, :Event, :Page, :Condition],
172
+ [:RPG, :Event, :Page, :Graphic], [:RPG, :Item], [:RPG, :Map],
173
+ [:RPG, :Map, :Encounter], [:RPG, :MapInfo], [:RPG, :ME], [:RPG, :MoveCommand],
174
+ [:RPG, :MoveRoute], [:RPG, :SE], [:RPG, :Skill], [:RPG, :State],
175
+ [:RPG, :System, :Terms], [:RPG, :System, :TestBattler], [:RPG, :System, :Vehicle],
176
+ [:RPG, :System, :Words], [:RPG, :Tileset], [:RPG, :Troop], [:RPG, :Troop, :Member],
177
+ [:RPG, :Troop, :Page], [:RPG, :Troop, :Page, :Condition], [:RPG, :UsableItem],
178
+ [:RPG, :UsableItem, :Damage], [:RPG, :UsableItem, :Effect], [:RPG, :Weapon],
179
+ # Script classes serialized in save game files
180
+ [:Game_ActionResult], [:Game_Actor], [:Game_Actors], [:Game_BaseItem],
181
+ [:Game_BattleAction], [:Game_CommonEvent], [:Game_Enemy], [:Game_Event],
182
+ [:Game_Follower], [:Game_Followers], [:Game_Interpreter], [:Game_Map],
183
+ [:Game_Message], [:Game_Party], [:Game_Picture], [:Game_Pictures], [:Game_Player],
184
+ [:Game_System], [:Game_Timer], [:Game_Troop], [:Game_Screen], [:Game_Vehicle],
185
+ [:Interpreter]
186
+ ].each {|x| process(Object, *x)}
187
+
188
+ def self.setup_system(version, options)
189
+ # convert variable and switch name arrays to a hash when serialized
190
+ # if round_trip isn't set change version_id to fixed number
191
+ if options[:round_trip]
192
+ iso = ->(val) { return val }
193
+ reset_method(RPG::System, :reduce_string, iso)
194
+ reset_method(RPG::System, :map_version, iso)
195
+ reset_method(Game_System, :map_version, iso)
196
+ else
197
+ reset_method(RPG::System, :reduce_string, ->(str) {
198
+ return nil if str.nil?
199
+ stripped = str.strip
200
+ return stripped.empty? ? nil : stripped
201
+ })
202
+ # These magic numbers should be different. If they are the same, the saved version
203
+ # of the map in save files will be used instead of any updated version of the map
204
+ reset_method(RPG::System, :map_version, ->(ignored) { return 12345678 })
205
+ reset_method(Game_System, :map_version, ->(ignored) { return 87654321 })
206
+ end
207
+ end
208
+
209
+ def self.setup_interpreter(version)
210
+ # Game_Interpreter is marshalled differently in VX Ace
211
+ if version == :ace
212
+ reset_method(Game_Interpreter, :marshal_dump, ->{
213
+ return @data
214
+ })
215
+ reset_method(Game_Interpreter, :marshal_load, ->(obj) {
216
+ @data = obj
217
+ })
218
+ else
219
+ remove_defined_method(Game_Interpreter, :marshal_dump)
220
+ remove_defined_method(Game_Interpreter, :marshal_load)
221
+ end
222
+ end
223
+
224
+ def self.setup_event_command(version, options)
225
+ # format event commands to flow style for the event codes that aren't move commands
226
+ if options[:round_trip]
227
+ reset_method(RPG::EventCommand, :clean, ->{})
228
+ else
229
+ reset_method(RPG::EventCommand, :clean, ->{
230
+ @parameters[0].rstrip! if @code == 401
231
+ })
232
+ end
233
+ reset_const(RPG::EventCommand, :MOVE_LIST_CODE, version == :xp ? 209 : 205)
234
+ end
235
+
236
+ def self.setup_classes(version, options)
237
+ setup_system(version, options)
238
+ setup_interpreter(version)
239
+ setup_event_command(version, options)
240
+ BasicCoder.set_ivars_methods(version)
241
+ end
242
+
243
+ FLOW_CLASSES = [Color, Tone, RPG::BGM, RPG::BGS, RPG::MoveCommand, RPG::SE]
244
+
245
+ SCRIPTS_BASE = 'Scripts'
246
+
247
+ ACE_DATA_EXT = '.rvdata2'
248
+ VX_DATA_EXT = '.rvdata'
249
+ XP_DATA_EXT = '.rxdata'
250
+ YAML_EXT = '.yaml'
251
+ RUBY_EXT = '.rb'
252
+
253
+ def self.get_data_directory(base)
254
+ return File.join(base, 'Data')
255
+ end
256
+
257
+ def self.get_yaml_directory(base)
258
+ return File.join(base, 'YAML')
259
+ end
260
+
261
+ def self.get_script_directory(base)
262
+ return File.join(base, 'Scripts')
263
+ end
264
+
265
+ class Game_Switches
266
+ include RGSS::BasicCoder
267
+
268
+ def encode(name, value)
269
+ return array_to_hash(value)
270
+ end
271
+
272
+ def decode(name, value)
273
+ return hash_to_array(value)
274
+ end
275
+ end
276
+
277
+ class Game_Variables
278
+ include RGSS::BasicCoder
279
+
280
+ def encode(name, value)
281
+ return array_to_hash(value)
282
+ end
283
+
284
+ def decode(name, value)
285
+ return hash_to_array(value)
286
+ end
287
+ end
288
+
289
+ class Game_SelfSwitches
290
+ include RGSS::BasicCoder
291
+
292
+ def encode(name, value)
293
+ return Hash[value.collect {|pair|
294
+ key, value = pair
295
+ next ["%03d %03d %s" % key, value]
296
+ }]
297
+ end
298
+
299
+ def decode(name, value)
300
+ return Hash[value.collect {|pair|
301
+ key, value = pair
302
+ next [key.scanf("%d %d %s"), value]
303
+ }]
304
+ end
305
+ end
306
+
307
+ class Game_System
308
+ include RGSS::BasicCoder
309
+
310
+ def encode(name, value)
311
+ if name == 'version_id'
312
+ return map_version(value)
313
+ else
314
+ return value
315
+ end
316
+ end
317
+ end
318
+
319
+ require 'RGSS/serialize'
320
+ end
@@ -0,0 +1,49 @@
1
+ module RGSS
2
+ module BasicCoder
3
+ def encode_with(coder)
4
+ ivars.each do |var|
5
+ name = var.to_s.sub(/^@/, '')
6
+ value = instance_variable_get(var)
7
+ coder[name] = encode(name, value)
8
+ end
9
+ end
10
+
11
+ def encode(name, value)
12
+ return value
13
+ end
14
+
15
+ def init_with(coder)
16
+ coder.map.each do |key, value|
17
+ sym = "@#{key}".to_sym
18
+ instance_variable_set(sym, decode(key, value))
19
+ end
20
+ end
21
+
22
+ def decode(name, value)
23
+ return value
24
+ end
25
+
26
+ def ivars
27
+ return instance_variables
28
+ end
29
+
30
+ INCLUDED_CLASSES = []
31
+ def self.included(mod)
32
+ INCLUDED_CLASSES.push(mod)
33
+ end
34
+
35
+ def self.set_ivars_methods(version)
36
+ INCLUDED_CLASSES.each do |c|
37
+ if version == :ace
38
+ RGSS::reset_method(c, :ivars, ->{
39
+ return instance_variables
40
+ })
41
+ else
42
+ RGSS::reset_method(c, :ivars, ->{
43
+ return instance_variables.sort
44
+ })
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +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
@@ -0,0 +1,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
+ 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
@@ -0,0 +1,57 @@
1
+ require 'RGSS'
2
+ module RPG
3
+ class System
4
+ include RGSS::BasicCoder
5
+ HASHED_VARS = ['variables', 'switches']
6
+ end
7
+
8
+ def self.array_to_hash(arr, &block)
9
+ h = {}
10
+ arr.each_with_index do |val, index|
11
+ r = block_given? ? block.call(val) : val
12
+ h[index] = r unless r.nil?
13
+ end
14
+ if arr.length > 0
15
+ last = arr.length - 1
16
+ h[last] = nil unless h.has_key?(last)
17
+ end
18
+ return h
19
+ end
20
+
21
+ def encode(name, value)
22
+ if HASHED_VARS.include?(name)
23
+ return array_to_hash(value) {|val| reduce_string(val)}
24
+ elsif name == 'version_id'
25
+ return map_version(value)
26
+ else
27
+ return value
28
+ end
29
+ end
30
+
31
+ def decode(name, value)
32
+ if HASHED_VARS.include?(name)
33
+ return hash_to_array(value)
34
+ else
35
+ return value
36
+ end
37
+ end
38
+
39
+ class EventCommand
40
+ def encode_with(coder)
41
+ raise 'Unexpected number of instance variables' if instance_variables.length != 3
42
+ clean
43
+
44
+ case @code
45
+ when MOVE_LIST_CODE # move list
46
+ coder.style = Psych::Nodes::Mapping::BLOCK
47
+ else
48
+ coder.style = Psych::Nodes::Mapping::FLOW
49
+ end
50
+ coder['i'], coder['c'], coder['p'] = @indent, @code, @parameters
51
+ end
52
+
53
+ def init_with(coder)
54
+ @indent, @code, @parameters = coder['i'], coder['c'], coder['p']
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,3 @@
1
+ module Rvpacker
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rvpacker/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rvpacker"
8
+ spec.version = Rvpacker::VERSION
9
+ spec.authors = ["Howard Jeng", "Andrew Kesterson"]
10
+ spec.email = ["andrew@aklabs.net"]
11
+ spec.summary = %q{Pack and unpack RPG Maker data files}
12
+ spec.homepage = "https://github.com/akesterson/rvpacker"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_dependency "trollop"
23
+ spec.add_dependency "psych", "2.0.0"
24
+ spec.add_dependency "formatador"
25
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rvpacker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Howard Jeng
9
+ - Andrew Kesterson
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2014-04-20 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bundler
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '1.6'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: '1.6'
31
+ - !ruby/object:Gem::Dependency
32
+ name: rake
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: trollop
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: psych
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - '='
69
+ - !ruby/object:Gem::Version
70
+ version: 2.0.0
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - '='
77
+ - !ruby/object:Gem::Version
78
+ version: 2.0.0
79
+ - !ruby/object:Gem::Dependency
80
+ name: formatador
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :runtime
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ description:
96
+ email:
97
+ - andrew@aklabs.net
98
+ executables:
99
+ - rvpacker
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - .gitignore
104
+ - Gemfile
105
+ - LICENSE
106
+ - README.md
107
+ - Rakefile
108
+ - bin/rvpacker
109
+ - lib/RGSS.rb
110
+ - lib/RGSS/BasicCoder.rb
111
+ - lib/RGSS/psych_mods.rb
112
+ - lib/RGSS/serialize.rb
113
+ - lib/RPG.rb
114
+ - lib/rvpacker/version.rb
115
+ - rvpacker.gemspec
116
+ homepage: https://github.com/akesterson/rvpacker
117
+ licenses:
118
+ - MIT
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 1.8.23
138
+ signing_key:
139
+ specification_version: 3
140
+ summary: Pack and unpack RPG Maker data files
141
+ test_files: []