rvpacker-ng 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 548008eb85d312d1f74cf7be57e145797b3a6e0506c66e7768159ab74dd26ced
4
+ data.tar.gz: f8ead7347cdc744c982395ec592021ee48c24643e752b809ed24b9aea78cf8b2
5
+ SHA512:
6
+ metadata.gz: 8f298da9896242cacd9616f932cd17dfc5cf392d2e0810e055663d6b6995fd660559c77e201dc60f250c47f67536fc7fdf1e63e501b6dc74422213e0c17d62d7
7
+ data.tar.gz: 6a6520720f0eba8d9db96455caaaee8d0a11b7a8125a21de150196bca1fa5aedb4ca3701dda293e7b1f47bccd0dfa2322d401ec9046f3b71b3376c121b40e2e9
data/.gitignore ADDED
@@ -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/.prettierrc ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "tabWidth": 2,
3
+ "useTabs": false,
4
+ "rubyToProc": true,
5
+ "rubyArrayLiteral": true,
6
+ "rubyHashLabel": true,
7
+ "rubyModifier": true
8
+ }
9
+
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.
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # rvpacker
2
+
3
+ A tool to unpack & pack RPGMaker data files into text so they can be version controlled & collaborated on. It's a port of Ruby 1.9.x's rvpacker to Ruby 3.x.
4
+
5
+ rvpacker consists of 3 parts:
6
+
7
+ * RPG library (stub classes for serialization of RPGMaker game data)
8
+ * RGSS library (some more classes for RPGMaker serialization)
9
+ * rvpacker (the script you call on the frontend)
10
+
11
+ # Installation
12
+
13
+ ```
14
+ $ gem install rvpacker-ng
15
+ ```
16
+
17
+ Usage
18
+ =====
19
+
20
+ ```
21
+ $ rvpacker --help
22
+ Options:
23
+ -a, --action=<s> Action to perform on project (unpack|pack)
24
+ -d, --project=<s> RPG Maker Project directory
25
+ -f, --force Update target even when source is older than target
26
+ -t, --project-type=<s> Project type (vx|ace|xp)
27
+ -V, --verbose Print verbose information while processing
28
+ -D, --database=<s> Only work on the given database
29
+ -h, --help Show this message
30
+ ```
31
+
32
+ For example, to unpack a RPG Maker VX Ace project in ~/Documents/RPGVXAce/Project1:
33
+
34
+ ```
35
+ $ rvpacker --action unpack --project ~/Documents/RPGVXAce/Project1 --project-type ace
36
+ ```
37
+
38
+ This will expand all Data/* files into (PROJECT)/YAML/ as YAML files (YAML is used because the object serialization data is retained, which ruby's YAML parser is very good at - otherwise I would have changed it to JSON). The Scripts will be unpacked as individual .rb files into (PROJECT)/Scripts/.
39
+
40
+ To take a previously unpacked project, and pack it back up:
41
+
42
+ ```
43
+ $ rvpacker --action pack --project ~/Documents/RPGVXAce/Project1 --project-type ace
44
+ ```
45
+
46
+ This will take all of the yaml files in (PROJECT)/YAML and all the scripts in (PROJECT)/Scripts, and repack all of your (PROJECT)/Data/* files. You can trust this to completely reassemble your Data/ directory, so long as the Scripts/ and YAML/ directories remain intact.
47
+
48
+ ## FAQ
49
+
50
+ ### General
51
+
52
+ This is great for teams that are collaborating on an RPG Maker project. Just add a few steps to your existing workflow:
53
+
54
+ * Checkout the project from version control
55
+ * Run 'rvpacker --action pack' on the project to repack it for the RPG Maker tool
56
+ * Load up RPG Maker and do whatever you're going to do; save the project
57
+ * Run 'rvpacker --action unpack' on the project
58
+ * Commit everything to version control (ignore the Data directory since you don't need it anymore; use .gitignore or .hgignore or whatever)
59
+
60
+ Now your project can be forked/merged in a much more safe/sane way, and you don't have to have someone bottlenecking the entire process.
61
+
62
+ ### Avoiding Map Collisions
63
+
64
+ One thing that rvpacker really can't help you with right now (and, ironically, probably one of the reasons you want it) is map collisions. Consider this situation:
65
+
66
+ * The project has 10 maps in it, total.
67
+ * Developer A makes a new map. It gets saved by the editor as Map011
68
+ * Developer B makes a new map, in a different branch. It also gets saved by the editor as Map011.
69
+ * Developer A and Developer B attempt to merge their changes. The merge fails because of the collision on the Map011 file.
70
+
71
+ The best way to avoid this that I can see is to use blocks of pre-allocated maps. You appoint one person in your project to be principally responsible for the map assets. It then becomes this person's responsibility to allocate maps in "blocks", so that people can work on maps in a distributed way without clobbering one another. The workflow looks like this:
72
+
73
+ * The project has 10 maps in it, total.
74
+ * Developer A needs to make 4 maps. He sends a request to the "map owner", requesting a block of 4 maps.
75
+ * The map owner creates 4 default, blank maps, and names them all "Request #12345" for Developer A
76
+ * Developer A starts working on his maps
77
+ * Developer B needs to make 6 maps. He sends a request to the "map owner", requesting a block of 6 maps.
78
+ * The map owner 4 default, blank maps, and names them all "Request #12346" or something similar for Developer B
79
+ * Developer B starts working on his maps
80
+
81
+ Using this workflow, it doesn't matter what order Developers A and B request their map blocks, or what order the map owner creates their map blocks. By giving the map owner the authority to create the map blocks, the individual developers can work freely in their map blocks. They can rename them, reorder them, change all of the map attributes (Size, tileset, whatever), without getting in danger of a map collision.
82
+
83
+ While this may seem like unnecessary process, it is a reasonable workaround. For a better explanation of why rvpacker can't do this for you, read the next section.
84
+
85
+ ### Automatic ID generation
86
+
87
+ You can add new elements to the YAML files manually, and leave their 'id:' field set to 'null'. This will cause the rvpacker pack action to automatically assign them a new ID number at the end of the sequence (e.g., if you have 17 items, the new one becomes ID 18). This is mainly handy for adding new scripts to the project without having to open the RPG maker and paste the script in; just make the new script file, add its entry in YAML/Scripts.yaml, and the designer will have your script accessible the next time they repack and open the project.
88
+
89
+ Also, the rvpacker tool sets the ID of script files to an autoincrementing integer. The scripts exist in the database with a magic number that I can't recreate, and nothing in the editor (RPG VX Ace anyway) seems to care if the magic number changes. It doesn't even affect the ordering. So in order to support adding new scripts with null IDs, like everything else, the magic numbers on scripts are disregarded and a new ID number is forced on the scripts when the rvpacker pack action occurs.
90
+
91
+ Note that this does not apply to Map files. Do not try changing the map ID numbers manually (see the "Avoiding Map Collisions" workflow, above, and "Why rvpacker can't help with map collisions", below).
92
+
93
+ ### Why rvpacker can't help with map collisions
94
+
95
+ If you look at the map collision problem described above, the way out of this situation might seem obvious: "Rename Map011.yaml in one of the branches to Map012.yaml, and problem solved. However, there are several significant problems with this approach:
96
+
97
+ * The ID numbers on the map files correspond to ID number entries in MapInfos.yaml (and the corresponding MapInfos.rvdata objects)
98
+ * The ID numbers are used to specify a parent/child relationship between one or more maps
99
+ * The ID numbers are used to specify the target of a map transition/warp event in event scripting
100
+
101
+ This means that changing the ID number assigned to a map (and, thereby, making it possible to merge 2 maps with the same ID number) becomes very nontrivial. The event scripting portion, especially, presents a difficult problem for rvpacker to overcome. It is simple enough for rvpacker to change the IDs of any new map created, and to change the reference to that ID number from any child maps. However, the events are where it gets sticky. The format of event calls in RPG Maker map files is not terribly well defined, and even if it was, I sincerely doubt that you want rvpacker tearing around in the guts of your map events.
102
+
103
+ ## Credit to SiCrane
104
+
105
+ The RPG and RGSS libraries were originally taken from SiCrane's YAML importer/exporter on the gamedev forums. I initially just put them in github so I wouldn't lose them, and added the rvpacker script frontend. They are starting to drift a bit, but SiCrane still gets original credit for the grand majority of the work that rvpacker does.
106
+
107
+ http://www.gamedev.net/topic/646333-rpg-maker-vx-ace-data-conversion-utility/
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/rvpacker ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'optimist'
5
+ require 'RGSS'
6
+
7
+ opts =
8
+ Optimist.options do
9
+ opt :action,
10
+ 'Action to perform on project (unpack|pack)',
11
+ short: 'a',
12
+ type: String
13
+ opt :project, 'RPG Maker Project directory', short: 'd', type: String
14
+ opt :force,
15
+ 'Update target even when source is older than target',
16
+ short: 'f'
17
+ opt :project_type, 'Project type (vx|ace|xp)', short: 't', type: String
18
+ opt :verbose, 'Print verbose information while processing', short: 'V'
19
+ opt :database,
20
+ 'Only work on the given database',
21
+ short: 'D',
22
+ type: String
23
+ end
24
+
25
+ directions = { 'unpack' => :all_bin_to_text, 'pack' => :all_text_to_bin }
26
+ projecttypes = { 'vx' => :vx, 'ace' => :ace, 'xp' => :xp }
27
+ $VERBOSE = opts[:verbose]
28
+
29
+ RGSS.serialize(
30
+ projecttypes[opts[:project_type]],
31
+ directions[opts[:action]],
32
+ opts[:project],
33
+ {
34
+ force: (opts[:force] ? true : false),
35
+ line_width: -1,
36
+ table_width: -1,
37
+ database: opts[:database]
38
+ }
39
+ )
@@ -0,0 +1,53 @@
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(
39
+ c,
40
+ :ivars,
41
+ -> { return instance_variables }
42
+ )
43
+ else
44
+ RGSS.reset_method(
45
+ c,
46
+ :ivars,
47
+ -> { return instance_variables.sort }
48
+ )
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,448 @@
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 'psych'
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(
38
+ Dir
39
+ .entries(directory)
40
+ .select { |file| File.extname(file) == extension }
41
+ )
42
+ end
43
+
44
+ def self.inflate(str)
45
+ text = Zlib::Inflate.inflate(str)
46
+ return text.force_encoding('UTF-8')
47
+ end
48
+
49
+ def self.deflate(str)
50
+ return Zlib::Deflate.deflate(str, Zlib::BEST_COMPRESSION)
51
+ end
52
+
53
+ def self.dump_data_file(file, data, time, options)
54
+ File.open(file, 'wb') { |f| Marshal.dump(data, f) }
55
+ File.utime(time, time, file)
56
+ end
57
+
58
+ def self.dump_yaml_file(file, data, time, options)
59
+ File.open(file, 'wb') { |f| Psych.dump(data, f, options) }
60
+ File.utime(time, time, file)
61
+ end
62
+
63
+ def self.dump_save(file, data, time, options)
64
+ File.open(file, 'wb') do |f|
65
+ data.each { |chunk| Marshal.dump(chunk, f) }
66
+ end
67
+ File.utime(time, time, file)
68
+ end
69
+
70
+ def self.dump_raw_file(file, data, time, options)
71
+ File.open(file, 'wb') { |f| f.write(data) }
72
+ File.utime(time, time, file)
73
+ end
74
+
75
+ def self.dump(dumper, file, data, time, options)
76
+ self.method(dumper).call(file, data, time, options)
77
+ rescue StandardError
78
+ warn "Exception dumping #{file}"
79
+ raise
80
+ end
81
+
82
+ def self.load_data_file(file)
83
+ File.open(file, 'rb') { |f| return Marshal.load(f) }
84
+ end
85
+
86
+ def self.load_yaml_file(file)
87
+ formatador = Formatador.new
88
+ obj = nil
89
+ File.open(file, 'rb') { |f| obj = Psych.load(f) }
90
+ max = 0
91
+ return obj unless obj.kind_of?(Array)
92
+ seen = {}
93
+ idx =
94
+ obj.each do |elem|
95
+ next if elem.nil?
96
+ if elem.instance_variable_defined?('@id')
97
+ id = elem.instance_variable_get('@id')
98
+ else
99
+ id = nil
100
+ elem.instance_variable_set('@id', nil)
101
+ end
102
+ next if id.nil?
103
+
104
+ if seen.has_key?(id)
105
+ formatador.display_line(
106
+ "[red]#{file}: Duplicate ID #{id}[/]"
107
+ )
108
+ formatador.indent do
109
+ formatador.indent do
110
+ elem
111
+ .pretty_inspect
112
+ .split(/\n/)
113
+ .each do |line|
114
+ formatador.display_line("[red]#{line}[/]")
115
+ end
116
+ end
117
+ formatador.display_line
118
+ formatador.display_line("[red]Last seen at:\n[/]")
119
+ formatador.indent do
120
+ elem
121
+ .pretty_inspect
122
+ .split(/\n/)
123
+ .each do |line|
124
+ formatador.display_line("[red]#{line}[/]")
125
+ end
126
+ end
127
+ end
128
+ exit
129
+ end
130
+ seen[id] = elem
131
+ max = ((id + 1) unless id < max)
132
+ end
133
+ obj.each do |elem|
134
+ next if elem.nil?
135
+ id = elem.instance_variable_get('@id')
136
+ if id.nil?
137
+ elem.instance_variable_set('@id', max)
138
+ max += 1
139
+ end
140
+ end
141
+ return obj
142
+ end
143
+
144
+ def self.load_raw_file(file)
145
+ File.open(file, 'rb') { |f| return f.read }
146
+ end
147
+
148
+ def self.load_save(file)
149
+ File.open(file, 'rb') do |f|
150
+ data = []
151
+ while not f.eof?
152
+ o = Marshal.load(f)
153
+ data.push(o)
154
+ end
155
+ return data
156
+ end
157
+ end
158
+
159
+ def self.load(loader, file)
160
+ return self.method(loader).call(file)
161
+ rescue StandardError
162
+ warn "Exception loading #{file}"
163
+ raise
164
+ end
165
+
166
+ def self.scripts_to_text(dirs, src, dest, options)
167
+ formatador = Formatador.new
168
+ src_file = File.join(dirs[:data], src)
169
+ dest_file = File.join(dirs[:yaml], dest)
170
+ raise "Missing #{src}" unless File.exist?(src_file)
171
+
172
+ script_entries = load(:load_data_file, src_file)
173
+ check_time = !options[:force] && File.exist?(dest_file)
174
+ oldest_time = File.mtime(dest_file) if check_time
175
+
176
+ file_map, script_index, script_code = Hash.new(-1), [], {}
177
+
178
+ idx = 0
179
+ script_entries.each do |script|
180
+ idx += 1
181
+ magic_number, script_name, code = idx, script[1], inflate(script[2])
182
+ script_name.force_encoding('UTF-8')
183
+
184
+ if code.length > 0
185
+ filename =
186
+ if script_name.empty?
187
+ 'blank'
188
+ else
189
+ sanitize_filename(script_name)
190
+ end
191
+ key = filename.upcase
192
+ value = (file_map[key] += 1)
193
+ actual_filename =
194
+ filename + (value == 0 ? '' : ".#{value}") + RUBY_EXT
195
+ script_index.push([magic_number, script_name, actual_filename])
196
+ full_filename = File.join(dirs[:script], actual_filename)
197
+ script_code[full_filename] = code
198
+ check_time = false unless File.exist?(full_filename)
199
+ oldest_time = [
200
+ File.mtime(full_filename),
201
+ oldest_time
202
+ ].min if check_time
203
+ else
204
+ script_index.push([magic_number, script_name, nil])
205
+ end
206
+ end
207
+
208
+ src_time = File.mtime(src_file)
209
+ if check_time && (src_time - 1) < oldest_time
210
+ if $VERBOSE
211
+ formatador.display_line('[yellow]Skipping scripts to text[/]')
212
+ end
213
+ else
214
+ if $VERBOSE
215
+ formatador.display_line('[green]Converting scripts to text[/]')
216
+ end
217
+ dump(:dump_yaml_file, dest_file, script_index, src_time, options)
218
+ script_code.each do |file, code|
219
+ dump(:dump_raw_file, file, code, src_time, options)
220
+ end
221
+ end
222
+ end
223
+
224
+ def self.scripts_to_binary(dirs, src, dest, options)
225
+ formatador = Formatador.new
226
+ src_file = File.join(dirs[:yaml], src)
227
+ dest_file = File.join(dirs[:data], dest)
228
+ raise "Missing #{src}" unless File.exist?(src_file)
229
+ check_time = !options[:force] && File.exist?(dest_file)
230
+ newest_time = File.mtime(src_file) if check_time
231
+
232
+ index = load(:load_yaml_file, src_file)
233
+ script_entries = []
234
+ index.each do |entry|
235
+ magic_number, script_name, filename = entry
236
+ code = ''
237
+ if filename
238
+ full_filename = File.join(dirs[:script], filename)
239
+ unless File.exist?(full_filename)
240
+ raise "Missing script file #{filename}"
241
+ end
242
+ newest_time = [
243
+ File.mtime(full_filename),
244
+ newest_time
245
+ ].max if check_time
246
+ code = load(:load_raw_file, full_filename)
247
+ end
248
+ script_entries.push([magic_number, script_name, deflate(code)])
249
+ end
250
+ if check_time && (newest_time - 1) < File.mtime(dest_file)
251
+ if $VERBOSE
252
+ formatador.display_line('[yellow]Skipping scripts to binary[/]')
253
+ end
254
+ else
255
+ if $VERBOSE
256
+ formatador.display_line(
257
+ '[green]Converting scripts to binary[/]'
258
+ )
259
+ end
260
+ dump(
261
+ :dump_data_file,
262
+ dest_file,
263
+ script_entries,
264
+ newest_time,
265
+ options
266
+ )
267
+ end
268
+ end
269
+
270
+ def self.process_file(
271
+ file,
272
+ src_file,
273
+ dest_file,
274
+ dest_ext,
275
+ loader,
276
+ dumper,
277
+ options
278
+ )
279
+ formatador = Formatador.new
280
+ fbase = File.basename(file, File.extname(file))
281
+ if (!options[:database].nil?) and
282
+ (options[:database].downcase != fbase.downcase)
283
+ return
284
+ end
285
+ src_time = File.mtime(src_file)
286
+ if !options[:force] && File.exist?(dest_file) &&
287
+ (src_time - 1) < File.mtime(dest_file)
288
+ formatador.display_line("[yellow]Skipping #{file}[/]") if $VERBOSE
289
+ else
290
+ if $VERBOSE
291
+ formatador.display_line(
292
+ "[green]Converting #{file} to #{dest_ext}[/]"
293
+ )
294
+ end
295
+ data = load(loader, src_file)
296
+ dump(dumper, dest_file, data, src_time, options)
297
+ end
298
+ end
299
+
300
+ def self.convert(src, dest, options)
301
+ files = files_with_extension(src[:directory], src[:ext])
302
+ files -= src[:exclude]
303
+
304
+ files.each do |file|
305
+ src_file = File.join(src[:directory], file)
306
+ dest_file =
307
+ File.join(dest[:directory], change_extension(file, dest[:ext]))
308
+
309
+ process_file(
310
+ file,
311
+ src_file,
312
+ dest_file,
313
+ dest[:ext],
314
+ src[:load_file],
315
+ dest[:dump_file],
316
+ options
317
+ )
318
+ end
319
+ end
320
+
321
+ def self.convert_saves(base, src, dest, options)
322
+ save_files = files_with_extension(base, src[:ext])
323
+ save_files.each do |file|
324
+ src_file = File.join(base, file)
325
+ dest_file = File.join(base, change_extension(file, dest[:ext]))
326
+
327
+ process_file(
328
+ file,
329
+ src_file,
330
+ dest_file,
331
+ dest[:ext],
332
+ src[:load_save],
333
+ dest[:dump_save],
334
+ options
335
+ )
336
+ end
337
+ end
338
+
339
+ # [version] one of :ace, :vx, :xp
340
+ # [direction] one of :data_bin_to_text, :data_text_to_bin, :save_bin_to_text,
341
+ # :save_text_to_bin, :scripts_bin_to_text, :scripts_text_to_bin,
342
+ # :all_text_to_bin, :all_bin_to_text
343
+ # [directory] directory that project file is in
344
+ # [options] :force - ignores file dates when converting (default false)
345
+ # :round_trip - create yaml data that matches original marshalled data skips
346
+ # data cleanup operations (default false)
347
+ # :line_width - line width form YAML files, -1 for no line width limit
348
+ # (default 130)
349
+ # :table_width - maximum number of entries per row for table data, -1 for no
350
+ # table row limit (default 20)
351
+ def self.serialize(version, direction, directory, options = {})
352
+ raise "#{directory} not found" unless File.exist?(directory)
353
+
354
+ setup_classes(version, options)
355
+ options = options.clone
356
+ options[:sort] = true if %i[vx xp].include?(version)
357
+ options[:flow_classes] = FLOW_CLASSES
358
+ options[:line_width] ||= 130
359
+
360
+ table_width = options[:table_width]
361
+ RGSS.reset_const(Table, :MAX_ROW_LENGTH, table_width ? table_width : 20)
362
+
363
+ base = File.realpath(directory)
364
+
365
+ dirs = {
366
+ base: base,
367
+ data: get_data_directory(base),
368
+ yaml: get_yaml_directory(base),
369
+ script: get_script_directory(base)
370
+ }
371
+
372
+ dirs.values.each { |d| FileUtils.mkdir(d) unless File.exist?(d) }
373
+
374
+ exts = { ace: ACE_DATA_EXT, vx: VX_DATA_EXT, xp: XP_DATA_EXT }
375
+
376
+ yaml_scripts = SCRIPTS_BASE + YAML_EXT
377
+ yaml = {
378
+ directory: dirs[:yaml],
379
+ exclude: [yaml_scripts],
380
+ ext: YAML_EXT,
381
+ load_file: :load_yaml_file,
382
+ dump_file: :dump_yaml_file,
383
+ load_save: :load_yaml_file,
384
+ dump_save: :dump_yaml_file
385
+ }
386
+
387
+ scripts = SCRIPTS_BASE + exts[version]
388
+ data = {
389
+ directory: dirs[:data],
390
+ exclude: [scripts],
391
+ ext: exts[version],
392
+ load_file: :load_data_file,
393
+ dump_file: :dump_data_file,
394
+ load_save: :load_save,
395
+ dump_save: :dump_save
396
+ }
397
+
398
+ if options[:database].nil? or options[:database].downcase == 'scripts'
399
+ convert_scripts = true
400
+ else
401
+ convert_scripts = false
402
+ end
403
+ if options[:database].nil? or options[:database].downcase == 'saves'
404
+ convert_saves = true
405
+ else
406
+ convert_saves = false
407
+ end
408
+
409
+ case direction
410
+ when :data_bin_to_text
411
+ convert(data, yaml, options)
412
+ if convert_scripts
413
+ scripts_to_text(dirs, scripts, yaml_scripts, options)
414
+ end
415
+ when :data_text_to_bin
416
+ convert(yaml, data, options)
417
+ if convert_scripts
418
+ scripts_to_binary(dirs, yaml_scripts, scripts, options)
419
+ end
420
+ when :save_bin_to_text
421
+ convert_saves(base, data, yaml, options) if convert_saves
422
+ when :save_text_to_bin
423
+ convert_saves(base, yaml, data, options) if convert_saves
424
+ when :scripts_bin_to_text
425
+ if convert_scripts
426
+ scripts_to_text(dirs, scripts, yaml_scripts, options)
427
+ end
428
+ when :scripts_text_to_bin
429
+ if convert_scripts
430
+ scripts_to_binary(dirs, yaml_scripts, scripts, options)
431
+ end
432
+ when :all_bin_to_text
433
+ convert(data, yaml, options)
434
+ if convert_scripts
435
+ scripts_to_text(dirs, scripts, yaml_scripts, options)
436
+ end
437
+ convert_saves(base, data, yaml, options) if convert_saves
438
+ when :all_text_to_bin
439
+ convert(yaml, data, options)
440
+ if convert_scripts
441
+ scripts_to_binary(dirs, yaml_scripts, scripts, options)
442
+ end
443
+ convert_saves(base, yaml, data, options) if convert_saves
444
+ else
445
+ raise "Unrecognized direction :#{direction}"
446
+ end
447
+ end
448
+ end
data/lib/RGSS.rb ADDED
@@ -0,0 +1,409 @@
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
+ unless items == @data.length
27
+ raise 'Size mismatch loading Table from data'
28
+ end
29
+ unless @x * @y * @z == items
30
+ raise 'Size mismatch loading Table from data'
31
+ end
32
+ end
33
+
34
+ MAX_ROW_LENGTH = 20
35
+
36
+ def encode_with(coder)
37
+ coder.style = Psych::Nodes::Mapping::BLOCK
38
+
39
+ coder['dim'] = @dim
40
+ coder['x'] = @x
41
+ coder['y'] = @y
42
+ coder['z'] = @z
43
+
44
+ if @x * @y * @z > 0
45
+ stride = @x < 2 ? (@y < 2 ? @z : @y) : @x
46
+ rows = @data.each_slice(stride).to_a
47
+ if MAX_ROW_LENGTH != -1 && stride > MAX_ROW_LENGTH
48
+ block_length = (stride + MAX_ROW_LENGTH - 1) / MAX_ROW_LENGTH
49
+ row_length = (stride + block_length - 1) / block_length
50
+ rows =
51
+ rows
52
+ .collect { |x| x.each_slice(row_length).to_a }
53
+ .flatten(1)
54
+ end
55
+ rows = rows.collect { |x| x.collect { |y| '%04x' % y }.join(' ') }
56
+ coder['data'] = rows
57
+ else
58
+ coder['data'] = []
59
+ end
60
+ end
61
+
62
+ def init_with(coder)
63
+ @dim = coder['dim']
64
+ @x = coder['x']
65
+ @y = coder['y']
66
+ @z = coder['z']
67
+ @data =
68
+ coder['data'].collect { |x| x.split(' ').collect(&:hex) }.flatten
69
+ items = @x * @y * @z
70
+ unless items == @data.length
71
+ raise 'Size mismatch loading Table from YAML'
72
+ end
73
+ end
74
+
75
+ def _dump(*ignored)
76
+ return [@dim, @x, @y, @z, @x * @y * @z, *@data].pack('L5 S*')
77
+ end
78
+
79
+ def self._load(bytes)
80
+ Table.new(bytes)
81
+ end
82
+ end
83
+
84
+ class Color
85
+ def initialize(bytes)
86
+ @r, @g, @b, @a = *bytes.unpack('D4')
87
+ end
88
+
89
+ def _dump(*ignored)
90
+ return [@r, @g, @b, @a].pack('D4')
91
+ end
92
+
93
+ def self._load(bytes)
94
+ Color.new(bytes)
95
+ end
96
+ end
97
+
98
+ class Tone
99
+ def initialize(bytes)
100
+ @r, @g, @b, @a = *bytes.unpack('D4')
101
+ end
102
+
103
+ def _dump(*ignored)
104
+ return [@r, @g, @b, @a].pack('D4')
105
+ end
106
+
107
+ def self._load(bytes)
108
+ Tone.new(bytes)
109
+ end
110
+ end
111
+
112
+ class Rect
113
+ def initialize(bytes)
114
+ @x, @y, @width, @height = *bytes.unpack('i4')
115
+ end
116
+
117
+ def _dump(*ignored)
118
+ return [@x, @y, @width, @height].pack('i4')
119
+ end
120
+
121
+ def self._load(bytes)
122
+ Rect.new(bytes)
123
+ end
124
+ end
125
+
126
+ module RGSS
127
+ def self.remove_defined_method(scope, name)
128
+ if scope.instance_methods(false).include?(name)
129
+ scope.send(:remove_method, name)
130
+ end
131
+ end
132
+
133
+ def self.reset_method(scope, name, method)
134
+ remove_defined_method(scope, name)
135
+ scope.send(:define_method, name, method)
136
+ end
137
+
138
+ def self.reset_const(scope, sym, value)
139
+ scope.send(:remove_const, sym) if scope.const_defined?(sym)
140
+ scope.send(:const_set, sym, value)
141
+ end
142
+
143
+ def self.array_to_hash(arr, &block)
144
+ h = {}
145
+ arr.each_with_index do |val, index|
146
+ r = block_given? ? block.call(val) : val
147
+ h[index] = r unless r.nil?
148
+ end
149
+ if arr.length > 0
150
+ last = arr.length - 1
151
+ h[last] = nil unless h.has_key?(last)
152
+ end
153
+ return h
154
+ end
155
+
156
+ def self.hash_to_array(hash)
157
+ arr = []
158
+ hash.each { |k, v| arr[k] = v }
159
+ return arr
160
+ end
161
+
162
+ require 'RGSS/BasicCoder'
163
+ require 'RPG'
164
+
165
+ # creates an empty class in a potentially nested scope
166
+ def self.process(root, name, *args)
167
+ if args.length > 0
168
+ process(root.const_get(name), *args)
169
+ else
170
+ unless root.const_defined?(name, false)
171
+ root.const_set(name, Class.new)
172
+ end
173
+ end
174
+ end
175
+
176
+ # other classes that don't need definitions
177
+ [
178
+ # RGSS data structures
179
+ %i[RPG Actor],
180
+ %i[RPG Animation],
181
+ %i[RPG Animation Frame],
182
+ %i[RPG Animation Timing],
183
+ %i[RPG Area],
184
+ %i[RPG Armor],
185
+ %i[RPG AudioFile],
186
+ %i[RPG BaseItem],
187
+ %i[RPG BaseItem Feature],
188
+ %i[RPG BGM],
189
+ %i[RPG BGS],
190
+ %i[RPG Class],
191
+ %i[RPG Class Learning],
192
+ %i[RPG CommonEvent],
193
+ %i[RPG Enemy],
194
+ %i[RPG Enemy Action],
195
+ %i[RPG Enemy DropItem],
196
+ %i[RPG EquipItem],
197
+ %i[RPG Event],
198
+ %i[RPG Event Page],
199
+ %i[RPG Event Page Condition],
200
+ %i[RPG Event Page Graphic],
201
+ %i[RPG Item],
202
+ %i[RPG Map],
203
+ %i[RPG Map Encounter],
204
+ %i[RPG MapInfo],
205
+ %i[RPG ME],
206
+ %i[RPG MoveCommand],
207
+ %i[RPG MoveRoute],
208
+ %i[RPG SE],
209
+ %i[RPG Skill],
210
+ %i[RPG State],
211
+ %i[RPG System Terms],
212
+ %i[RPG System TestBattler],
213
+ %i[RPG System Vehicle],
214
+ %i[RPG System Words],
215
+ %i[RPG Tileset],
216
+ %i[RPG Troop],
217
+ %i[RPG Troop Member],
218
+ %i[RPG Troop Page],
219
+ %i[RPG Troop Page Condition],
220
+ %i[RPG UsableItem],
221
+ %i[RPG UsableItem Damage],
222
+ %i[RPG UsableItem Effect],
223
+ %i[RPG Weapon],
224
+ # Script classes serialized in save game files
225
+ [:Game_ActionResult],
226
+ [:Game_Actor],
227
+ [:Game_Actors],
228
+ [:Game_BaseItem],
229
+ [:Game_BattleAction],
230
+ [:Game_CommonEvent],
231
+ [:Game_Enemy],
232
+ [:Game_Event],
233
+ [:Game_Follower],
234
+ [:Game_Followers],
235
+ [:Game_Interpreter],
236
+ [:Game_Map],
237
+ [:Game_Message],
238
+ [:Game_Party],
239
+ [:Game_Picture],
240
+ [:Game_Pictures],
241
+ [:Game_Player],
242
+ [:Game_System],
243
+ [:Game_Timer],
244
+ [:Game_Troop],
245
+ [:Game_Screen],
246
+ [:Game_Vehicle],
247
+ [:Interpreter]
248
+ ].each { |x| process(Object, *x) }
249
+
250
+ def self.setup_system(version, options)
251
+ # convert variable and switch name arrays to a hash when serialized
252
+ # if round_trip isn't set change version_id to fixed number
253
+ if options[:round_trip]
254
+ iso = ->(val) { return val }
255
+ reset_method(RPG::System, :reduce_string, iso)
256
+ reset_method(RPG::System, :map_version, iso)
257
+ reset_method(Game_System, :map_version, iso)
258
+ else
259
+ reset_method(
260
+ RPG::System,
261
+ :reduce_string,
262
+ ->(str) do
263
+ return nil if str.nil?
264
+ stripped = str.strip
265
+ return stripped.empty? ? nil : stripped
266
+ end
267
+ )
268
+
269
+ # These magic numbers should be different. If they are the same, the saved version
270
+ # of the map in save files will be used instead of any updated version of the map
271
+ reset_method(
272
+ RPG::System,
273
+ :map_version,
274
+ ->(ignored) { return 12_345_678 }
275
+ )
276
+ reset_method(
277
+ Game_System,
278
+ :map_version,
279
+ ->(ignored) { return 87_654_321 }
280
+ )
281
+ end
282
+ end
283
+
284
+ def self.setup_interpreter(version)
285
+ # Game_Interpreter is marshalled differently in VX Ace
286
+ if version == :ace
287
+ reset_method(Game_Interpreter, :marshal_dump, -> { return @data })
288
+ reset_method(
289
+ Game_Interpreter,
290
+ :marshal_load,
291
+ ->(obj) { @data = obj }
292
+ )
293
+ else
294
+ remove_defined_method(Game_Interpreter, :marshal_dump)
295
+ remove_defined_method(Game_Interpreter, :marshal_load)
296
+ end
297
+ end
298
+
299
+ def self.setup_event_command(version, options)
300
+ # format event commands to flow style for the event codes that aren't move commands
301
+ if options[:round_trip]
302
+ reset_method(RPG::EventCommand, :clean, -> { })
303
+ else
304
+ reset_method(
305
+ RPG::EventCommand,
306
+ :clean,
307
+ -> { @parameters[0].rstrip! if @code == 401 }
308
+ )
309
+ end
310
+ reset_const(
311
+ RPG::EventCommand,
312
+ :MOVE_LIST_CODE,
313
+ version == :xp ? 209 : 205
314
+ )
315
+ end
316
+
317
+ def self.setup_classes(version, options)
318
+ setup_system(version, options)
319
+ setup_interpreter(version)
320
+ setup_event_command(version, options)
321
+ BasicCoder.set_ivars_methods(version)
322
+ end
323
+
324
+ FLOW_CLASSES = [Color, Tone, RPG::BGM, RPG::BGS, RPG::MoveCommand, RPG::SE]
325
+
326
+ SCRIPTS_BASE = 'Scripts'
327
+
328
+ ACE_DATA_EXT = '.rvdata2'
329
+ VX_DATA_EXT = '.rvdata'
330
+ XP_DATA_EXT = '.rxdata'
331
+ YAML_EXT = '.yaml'
332
+ RUBY_EXT = '.rb'
333
+
334
+ def self.get_data_directory(base)
335
+ return File.join(base, 'Data')
336
+ end
337
+
338
+ def self.get_yaml_directory(base)
339
+ return File.join(base, 'YAML')
340
+ end
341
+
342
+ def self.get_script_directory(base)
343
+ return File.join(base, 'Scripts')
344
+ end
345
+
346
+ class Game_Switches
347
+ include RGSS::BasicCoder
348
+
349
+ def encode(name, value)
350
+ return array_to_hash(value)
351
+ end
352
+
353
+ def decode(name, value)
354
+ return hash_to_array(value)
355
+ end
356
+ end
357
+
358
+ class Game_Variables
359
+ include RGSS::BasicCoder
360
+
361
+ def encode(name, value)
362
+ return array_to_hash(value)
363
+ end
364
+
365
+ def decode(name, value)
366
+ return hash_to_array(value)
367
+ end
368
+ end
369
+
370
+ class Game_SelfSwitches
371
+ include RGSS::BasicCoder
372
+
373
+ def encode(name, value)
374
+ return(
375
+ Hash[
376
+ value.collect do |pair|
377
+ key, value = pair
378
+ next ['%03d %03d %s' % key, value]
379
+ end
380
+ ]
381
+ )
382
+ end
383
+
384
+ def decode(name, value)
385
+ return(
386
+ Hash[
387
+ value.collect do |pair|
388
+ key, value = pair
389
+ next [key.scanf('%d %d %s'), value]
390
+ end
391
+ ]
392
+ )
393
+ end
394
+ end
395
+
396
+ class Game_System
397
+ include RGSS::BasicCoder
398
+
399
+ def encode(name, value)
400
+ if name == 'version_id'
401
+ return map_version(value)
402
+ else
403
+ return value
404
+ end
405
+ end
406
+ end
407
+
408
+ require 'RGSS/serialize'
409
+ end
data/lib/RPG.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'RGSS'
2
+ module RPG
3
+ class System
4
+ include RGSS::BasicCoder
5
+ HASHED_VARS = %w[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
+ if instance_variables.length != 3
42
+ raise 'Unexpected number of instance variables'
43
+ end
44
+ clean
45
+
46
+ case @code
47
+ when MOVE_LIST_CODE
48
+ # move list
49
+ coder.style = Psych::Nodes::Mapping::BLOCK
50
+ else
51
+ coder.style = Psych::Nodes::Mapping::FLOW
52
+ end
53
+ coder['i'], coder['c'], coder['p'] = @indent, @code, @parameters
54
+ end
55
+
56
+ def init_with(coder)
57
+ @indent, @code, @parameters = coder['i'], coder['c'], coder['p']
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module RVPACKER
2
+ VERSION = "1.3.0"
3
+ end
data/rvpacker.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ require_relative 'lib/rvpacker/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'rvpacker-ng'
5
+ spec.version = RVPACKER::VERSION
6
+ spec.authors = ['Howard Jeng', 'Andrew Kesterson', 'Solistra', 'Darkness9724']
7
+ spec.email = ['darkness9724@gmail.com']
8
+ spec.summary = 'Pack and unpack any RPG Maker VX Ace data files'
9
+ spec.homepage = 'https://gitlab.com/Darkness9724/rvpacker'
10
+ spec.license = 'MIT'
11
+
12
+ spec.files = `git ls-files -z`.split("\x0")
13
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.require_paths = ['lib']
16
+
17
+ spec.add_development_dependency 'bundler', '2.3.10'
18
+ spec.add_development_dependency 'rake', '13.0.6'
19
+ spec.add_dependency 'optimist', '3.0.1'
20
+ spec.add_dependency 'psych', '4.0.3'
21
+ spec.add_dependency 'formatador', '0.3.0'
22
+ spec.add_dependency 'scanf', '1.0.0'
23
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rvpacker-ng
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Howard Jeng
8
+ - Andrew Kesterson
9
+ - Solistra
10
+ - Darkness9724
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2022-03-29 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: bundler
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - '='
21
+ - !ruby/object:Gem::Version
22
+ version: 2.3.10
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.3.10
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - '='
35
+ - !ruby/object:Gem::Version
36
+ version: 13.0.6
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '='
42
+ - !ruby/object:Gem::Version
43
+ version: 13.0.6
44
+ - !ruby/object:Gem::Dependency
45
+ name: optimist
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - '='
49
+ - !ruby/object:Gem::Version
50
+ version: 3.0.1
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - '='
56
+ - !ruby/object:Gem::Version
57
+ version: 3.0.1
58
+ - !ruby/object:Gem::Dependency
59
+ name: psych
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - '='
63
+ - !ruby/object:Gem::Version
64
+ version: 4.0.3
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - '='
70
+ - !ruby/object:Gem::Version
71
+ version: 4.0.3
72
+ - !ruby/object:Gem::Dependency
73
+ name: formatador
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '='
77
+ - !ruby/object:Gem::Version
78
+ version: 0.3.0
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - '='
84
+ - !ruby/object:Gem::Version
85
+ version: 0.3.0
86
+ - !ruby/object:Gem::Dependency
87
+ name: scanf
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - '='
91
+ - !ruby/object:Gem::Version
92
+ version: 1.0.0
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '='
98
+ - !ruby/object:Gem::Version
99
+ version: 1.0.0
100
+ description:
101
+ email:
102
+ - darkness9724@gmail.com
103
+ executables:
104
+ - rvpacker
105
+ extensions: []
106
+ extra_rdoc_files: []
107
+ files:
108
+ - ".gitignore"
109
+ - ".prettierrc"
110
+ - Gemfile
111
+ - LICENSE
112
+ - README.md
113
+ - Rakefile
114
+ - bin/rvpacker
115
+ - lib/RGSS.rb
116
+ - lib/RGSS/BasicCoder.rb
117
+ - lib/RGSS/serialize.rb
118
+ - lib/RPG.rb
119
+ - lib/rvpacker/version.rb
120
+ - rvpacker.gemspec
121
+ homepage: https://gitlab.com/Darkness9724/rvpacker
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubygems_version: 3.3.8
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Pack and unpack any RPG Maker VX Ace data files
144
+ test_files: []