rvpacker 1.0.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.
@@ -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: []