rvpacker-txt 1.3.0 → 1.4.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.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/README.md +15 -16
- data/bin/rvpacker-txt +113 -66
- data/lib/classes.rb +51 -17
- data/lib/read.rb +341 -0
- data/lib/write.rb +401 -0
- data/rvpacker-txt.gemspec +5 -6
- metadata +5 -35
- data/Rakefile +0 -1
- data/lib/serialize.rb +0 -857
- data/sig/global_variables.rbs +0 -4
- data/sig/rgss.rbs +0 -39
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90b4214a5f31ca357f08bb57ab08a8fdfeeed37aff4674d73dc2c866bdb3f30e
|
|
4
|
+
data.tar.gz: d35c5da8a2755b486ef5b98f9c9091f682b8d3143f3dde0f2adafa4348fc71e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db1067185a2c07d44c96393d9d4b1c63bd27d424d946558e23a9bd4c6f8f6a9f4961cbddba5f8ce32ac5d1074d15aa2cc6cf64a45d3a10400ac753cc8380b26d
|
|
7
|
+
data.tar.gz: 8a696a3a4cb22ac06425207218f8aedebbe1bc730fc36a474d589a6dd0d1b9c8320d270beb67235f7d75666caf35edc5fd7a83f5245ffe798aa76f50b29fa099
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -5,9 +5,9 @@ port of Ruby 1.9.x's rvpacker to Ruby 3.x to use .txt files instead of YAML, and
|
|
|
5
5
|
|
|
6
6
|
rvpacker consists of 3 parts:
|
|
7
7
|
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* classes.rb library - all necessary classes to properly load and dump RPG Maker files
|
|
9
|
+
* read.rb library - all necessary functions for reading and parsing RPG Maker files to text
|
|
10
|
+
* write.rb library - all necessary functions for writing parsed RPG Maker files back to their initial form.
|
|
11
11
|
|
|
12
12
|
# Installation
|
|
13
13
|
|
|
@@ -15,8 +15,9 @@ rvpacker consists of 3 parts:
|
|
|
15
15
|
$ gem install rvpacker-txt
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Usage
|
|
19
|
-
|
|
18
|
+
# Usage
|
|
19
|
+
|
|
20
|
+
You can get a help message on usage using `rvpacker-txt -h`.
|
|
20
21
|
|
|
21
22
|
```
|
|
22
23
|
$ rvpacker-txt -h
|
|
@@ -28,15 +29,12 @@ COMMANDS:
|
|
|
28
29
|
read - Parses RPG Maker game files to .txt
|
|
29
30
|
write - Writes parsed files back to their initial form
|
|
30
31
|
OPTIONS:
|
|
31
|
-
-d, --input-dir
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
--
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-s, --shuffle NUMBER At value 1: Shuffles all lines in strings, at value 2: shuffles all lines and words in strings.
|
|
38
|
-
-l, --log Log information while processing.
|
|
39
|
-
-h, --help Show help message.
|
|
32
|
+
-d, --input-dir DIR Input directory of RPG Maker project
|
|
33
|
+
--disable-processing FILES Don't process specified files (maps, other, system, plugins)
|
|
34
|
+
-s, --shuffle NUM Shuffle level (1: lines, 2: lines and words)
|
|
35
|
+
--disable-custom-parsing Disables built-in custom parsing for some games
|
|
36
|
+
-l, --log Log information while processing
|
|
37
|
+
-h, --help Show help message
|
|
40
38
|
```
|
|
41
39
|
|
|
42
40
|
For example, to read a RPG Maker VX Ace project in E:/Documents/RPGMakerGame to .txt files:
|
|
@@ -49,7 +47,8 @@ Program determines game engine automatically.
|
|
|
49
47
|
|
|
50
48
|
This will parse all text from Data/* files into translation/maps and translation/other directories as files without
|
|
51
49
|
_trans postfix that contain original text and files with _trans postfix that contain empty lines for translation.
|
|
52
|
-
Lines from Scripts file will be parsed into translation/other/scripts.txt file as plain text
|
|
50
|
+
Lines from Scripts file will be parsed into translation/other/scripts.txt file as plain text, and
|
|
51
|
+
also into a scripts_plain.txt file that contains scripts contents as a whole - that's just for convenience.
|
|
53
52
|
|
|
54
53
|
To write previously parsed project back to its initial form:
|
|
55
54
|
|
|
@@ -80,4 +79,4 @@ https://github.com/ymaxkrapzv/rvpacker
|
|
|
80
79
|
|
|
81
80
|
Darkness9724 forked rvpacker to rvpacker-ng, ported it to Ruby 3.x and updated dependencies.
|
|
82
81
|
|
|
83
|
-
https://gitlab.com/Darkness9724/rvpacker-ng
|
|
82
|
+
https://gitlab.com/Darkness9724/rvpacker-ng
|
data/bin/rvpacker-txt
CHANGED
|
@@ -1,83 +1,130 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
3
4
|
require 'optparse'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
require 'classes'
|
|
8
|
+
require 'read'
|
|
9
|
+
require 'write'
|
|
10
|
+
|
|
11
|
+
def self.parse_options
|
|
12
|
+
options = { disable_processing: Array.new(4, false) }
|
|
13
|
+
|
|
14
|
+
OptionParser.new do |cmd|
|
|
15
|
+
cmd.banner = "This tool allows to parse RPG Maker project to .txt files and back.\n\nUsage: rvpacker-txt COMMAND [options]\n\nCOMMANDS:\n read - Parses RPG Maker game files to .txt\n write - Writes parsed files back to their initial form\nOPTIONS:\n"
|
|
16
|
+
|
|
17
|
+
cmd.on('-i', '--input-dir DIR', String, 'Input directory of RPG Maker project') do |dir|
|
|
18
|
+
options[:input_dir] = File.exist?(dir) ? File.realpath(dir) : (raise "#{dir} not found")
|
|
19
|
+
options[:output_dir] = options[:input_dir]
|
|
20
|
+
end
|
|
4
21
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
command.on('-d',
|
|
14
|
-
'--input-dir DIRECTORY',
|
|
15
|
-
'Input directory of RPG Maker project.',
|
|
16
|
-
'Must contain "Data" or "original" folder to read,',
|
|
17
|
-
'and additionally "translation" with "maps" and "other" subdirectories to write.') { |dir| options[:input_dir] = dir }
|
|
18
|
-
|
|
19
|
-
command.on('--no',
|
|
20
|
-
"Don't process specified files.",
|
|
21
|
-
'Takes multiple values separated by a comma.',
|
|
22
|
-
'Allowed values: maps, other, system, plugins') do |files|
|
|
23
|
-
actual_files = files.split(',')
|
|
24
|
-
|
|
25
|
-
actual_files.each do |file|
|
|
26
|
-
case file
|
|
27
|
-
when "maps"
|
|
28
|
-
$no[0] = true
|
|
29
|
-
when "other"
|
|
30
|
-
$no[1] = true
|
|
31
|
-
when "system"
|
|
32
|
-
$no[2] = true
|
|
33
|
-
when "scripts"
|
|
34
|
-
$no[3] = true
|
|
35
|
-
else
|
|
36
|
-
puts "Wrong value for no argument: #{file}.\nAllowed values: maps, other, system, plugins"
|
|
37
|
-
exit
|
|
22
|
+
cmd.on('-o', '--output-dir DIR', String, 'Output directory of parsed/written files') do |dir|
|
|
23
|
+
options[:output_dir] = File.exist?(dir) ? File.realpath(dir) : (raise "#{dir} not found")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
cmd.on('--disable-processing FILES', Array, 'Don\'t process specified files (maps, other, system, plugins)') do |files|
|
|
27
|
+
files.each do |file|
|
|
28
|
+
index = %w[maps other system scripts].index(file)
|
|
29
|
+
options[:disable_processing][index] = true if index
|
|
38
30
|
end
|
|
39
31
|
end
|
|
40
|
-
end
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
cmd.on('-s', '--shuffle NUM', Integer, 'Shuffle level (1: lines, 2: lines and words)') do |num|
|
|
34
|
+
options[:shuffle_level] = num
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
cmd.on('--disable-custom-parsing', 'Disables built-in custom parsing for some games') do
|
|
38
|
+
options[:disable_custom_parsing] = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
cmd.on('-l', '--log', 'Log information while processing') do
|
|
42
|
+
options[:logging] = true
|
|
43
|
+
end
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
cmd.on('-h', '--help', 'Show help message') do
|
|
46
|
+
puts cmd
|
|
47
|
+
exit
|
|
48
|
+
end
|
|
49
|
+
end.parse!
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
end.parse!(ARGV)
|
|
51
|
+
options[:action] = ARGV.shift
|
|
52
|
+
raise 'COMMAND argument is required. Use rvpacker-txt -h for help.' if options[:action].nil?
|
|
53
|
+
raise 'Invalid command. Allowed commands are: read, write.' unless %w[read write].include?(options[:action])
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
puts 'COMMAND argument is required.'
|
|
57
|
-
exit
|
|
55
|
+
options
|
|
58
56
|
end
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
def self.get_game_type(system_file_path, disable_custom_parsing)
|
|
59
|
+
return nil if disable_custom_parsing
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
object = Marshal.load(File.binread(system_file_path))
|
|
62
|
+
game_title = object.instance_variable_get(:@game_title).to_s.downcase
|
|
63
|
+
game_title.include?('lisa') ? 'lisa' : nil
|
|
65
64
|
end
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
start_time = Time.now
|
|
67
|
+
|
|
68
|
+
options = parse_options
|
|
69
|
+
input_dir = options[:input_dir]
|
|
70
|
+
output_dir = options[:output_dir]
|
|
71
|
+
disable_custom_parsing = options[:disable_custom_parsing]
|
|
72
|
+
shuffle_level = options[:shuffle_level]
|
|
73
|
+
logging = options[:logging]
|
|
74
|
+
disable_processing = options[:disable_processing]
|
|
75
|
+
|
|
76
|
+
extensions = { xp: '.rxdata', vx: 'rvdata', ace: 'rvdata2' }
|
|
77
|
+
|
|
78
|
+
original_directory = Dir.glob(File.join(input_dir, '{data,original}'), File::FNM_CASEFOLD).first
|
|
79
|
+
raise '"Data" or "original" directory not found within input directory.' unless original_directory
|
|
80
|
+
|
|
81
|
+
paths = {
|
|
82
|
+
original_path: original_directory,
|
|
83
|
+
translation_path: File.join(input_dir, 'translation'),
|
|
84
|
+
maps_path: File.join(input_dir, 'translation', 'maps'),
|
|
85
|
+
other_path: File.join(input_dir, 'translation', 'other'),
|
|
86
|
+
output_path: File.join(output_dir, 'output')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
paths.each_value { |path| FileUtils.mkdir_p(path) }
|
|
90
|
+
|
|
91
|
+
engine = extensions.find do |symbol, extension|
|
|
92
|
+
symbol if File.exist?(File.join(paths[:original_path], "System.#{extension}"))
|
|
93
|
+
end || (raise "Couldn't determine project engine.")
|
|
69
94
|
|
|
70
|
-
|
|
71
|
-
raise '"Data" or "original" directory not found within input directory.' if original_directory.nil?
|
|
95
|
+
files = Dir.glob("#{paths[:original_path]}/*#{extensions[engine]}")
|
|
72
96
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
maps_files = []
|
|
98
|
+
other_files = []
|
|
99
|
+
system_file = nil
|
|
100
|
+
scripts_file = nil
|
|
101
|
+
|
|
102
|
+
files.each do |file|
|
|
103
|
+
basename = File.basename(file)
|
|
104
|
+
|
|
105
|
+
if basename.start_with?(/Map[0-9]/)
|
|
106
|
+
maps_files.push(file)
|
|
107
|
+
elsif !basename.start_with?(/Map|Tilesets|Animations|System|Scripts|Areas/)
|
|
108
|
+
other_files.push(file)
|
|
109
|
+
elsif basename.start_with?('System')
|
|
110
|
+
system_file = file
|
|
111
|
+
elsif basename.start_with?('Scripts')
|
|
112
|
+
scripts_file = file
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
game_type = get_game_type(system_file, disable_custom_parsing)
|
|
117
|
+
|
|
118
|
+
if options[:action] == 'read'
|
|
119
|
+
read_map(maps_files, paths[:maps_path], logging, game_type) unless disable_processing[0]
|
|
120
|
+
read_other(other_files, paths[:other_path], logging, game_type) unless disable_processing[1]
|
|
121
|
+
read_system(system_file, paths[:other_path], logging) unless disable_processing[2]
|
|
122
|
+
read_scripts(scripts_file, paths[:other_path], logging) unless disable_processing[3]
|
|
123
|
+
else
|
|
124
|
+
write_map(maps_files, paths[:maps_path], paths[:output_path], shuffle_level, logging, game_type) unless disable_processing[0]
|
|
125
|
+
write_other(other_files, paths[:other_path], paths[:output_path], shuffle_level, logging, game_type) unless disable_processing[1]
|
|
126
|
+
write_system(system_file, paths[:other_path], paths[:output_path], shuffle_level, logging) unless disable_processing[2]
|
|
127
|
+
write_scripts(scripts_file, paths[:other_path], paths[:output_path], logging) unless disable_processing[3]
|
|
128
|
+
end
|
|
82
129
|
|
|
83
|
-
|
|
130
|
+
puts "Done in #{Time.now - start_time}"
|
data/lib/classes.rb
CHANGED
|
@@ -22,9 +22,7 @@ class Table
|
|
|
22
22
|
def initialize(bytes)
|
|
23
23
|
@dim, @x, @y, @z, items, *@data = bytes.unpack('L5 S*')
|
|
24
24
|
|
|
25
|
-
unless items == @data.length && @x * @y * @z == items
|
|
26
|
-
raise 'Size mismatch loading Table from data'
|
|
27
|
-
end
|
|
25
|
+
raise 'Size mismatch loading Table from data' unless items == @data.length && @x * @y * @z == items
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
def _dump(*_ignored)
|
|
@@ -32,7 +30,7 @@ class Table
|
|
|
32
30
|
end
|
|
33
31
|
|
|
34
32
|
def self._load(bytes)
|
|
35
|
-
|
|
33
|
+
new(bytes)
|
|
36
34
|
end
|
|
37
35
|
end
|
|
38
36
|
|
|
@@ -46,7 +44,7 @@ class Color
|
|
|
46
44
|
end
|
|
47
45
|
|
|
48
46
|
def self._load(bytes)
|
|
49
|
-
|
|
47
|
+
new(bytes)
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
50
|
|
|
@@ -60,7 +58,7 @@ class Tone
|
|
|
60
58
|
end
|
|
61
59
|
|
|
62
60
|
def self._load(bytes)
|
|
63
|
-
|
|
61
|
+
new(bytes)
|
|
64
62
|
end
|
|
65
63
|
end
|
|
66
64
|
|
|
@@ -74,7 +72,44 @@ class Rect
|
|
|
74
72
|
end
|
|
75
73
|
|
|
76
74
|
def self._load(bytes)
|
|
77
|
-
|
|
75
|
+
new(bytes)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Fuck using an array with set, that's just straight dumb and not efficient
|
|
80
|
+
class IndexedSet
|
|
81
|
+
def initialize
|
|
82
|
+
@hash = Hash.new
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def add(item)
|
|
86
|
+
return if @hash.include?(item)
|
|
87
|
+
@hash[item] = hash.size
|
|
88
|
+
@hash
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def include?(item)
|
|
92
|
+
@hash.include?(item)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def each(&block)
|
|
96
|
+
@hash.each_key(&block)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def to_a
|
|
100
|
+
@hash.dup
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def join(delimiter = '')
|
|
104
|
+
@hash.keys.join(delimiter)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def length
|
|
108
|
+
@hash.size
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def empty?
|
|
112
|
+
@hash.empty?
|
|
78
113
|
end
|
|
79
114
|
end
|
|
80
115
|
|
|
@@ -84,17 +119,14 @@ end
|
|
|
84
119
|
module RGSS
|
|
85
120
|
# creates an empty class in a potentially nested scope
|
|
86
121
|
def self.process(root, name, *args)
|
|
87
|
-
if
|
|
88
|
-
|
|
122
|
+
if args.empty?
|
|
123
|
+
root.const_set(name, Class.new) unless root.const_defined?(name, false)
|
|
89
124
|
else
|
|
90
|
-
|
|
91
|
-
root.const_set(name, Class.new)
|
|
92
|
-
end
|
|
125
|
+
process(root.const_get(name), *args)
|
|
93
126
|
end
|
|
94
127
|
end
|
|
95
128
|
|
|
96
|
-
|
|
97
|
-
[
|
|
129
|
+
classes_nested_array = [
|
|
98
130
|
# RGSS data structures
|
|
99
131
|
%i[RPG Actor],
|
|
100
132
|
%i[RPG Animation],
|
|
@@ -143,7 +175,9 @@ module RGSS
|
|
|
143
175
|
%i[RPG UsableItem Damage],
|
|
144
176
|
%i[RPG UsableItem Effect],
|
|
145
177
|
%i[RPG Weapon]
|
|
146
|
-
].
|
|
147
|
-
end
|
|
178
|
+
].freeze
|
|
148
179
|
|
|
149
|
-
|
|
180
|
+
classes_nested_array.each do |symbol_array|
|
|
181
|
+
process(Object, *symbol_array)
|
|
182
|
+
end
|
|
183
|
+
end
|