rvpacker-txt 1.3.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be60b9ca7d30b5ba65f22b5c71b2051cddc357d0793e5040bddddb4b940f7aec
4
- data.tar.gz: a62fc90e814c94a9eeb0d56299c6a9211311263589d6d55b0ef5367a74a281ae
3
+ metadata.gz: 90b4214a5f31ca357f08bb57ab08a8fdfeeed37aff4674d73dc2c866bdb3f30e
4
+ data.tar.gz: d35c5da8a2755b486ef5b98f9c9091f682b8d3143f3dde0f2adafa4348fc71e7
5
5
  SHA512:
6
- metadata.gz: 58e86a866916217af89835096794ecdcaef8803eadc695f24347790179e5e3fda42510d5ce968a904e6d86e84c88602cbbd7fdf7411d2294b253c2a4d225b63b
7
- data.tar.gz: 4a6b9e66cc383defab76c9d6216afdfcc45f16de09530ebec498b2401faf4fac16dd3d1aa1843ecc6992585116f22f750f123a9df4d76dd1e1cacbede6ca2705
6
+ metadata.gz: db1067185a2c07d44c96393d9d4b1c63bd27d424d946558e23a9bd4c6f8f6a9f4961cbddba5f8ce32ac5d1074d15aa2cc6cf64a45d3a10400ac753cc8380b26d
7
+ data.tar.gz: 8a696a3a4cb22ac06425207218f8aedebbe1bc730fc36a474d589a6dd0d1b9c8320d270beb67235f7d75666caf35edc5fd7a83f5245ffe798aa76f50b29fa099
data/Gemfile CHANGED
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
- # Specify your gem's dependencies in rvpacker-txt.gemspec
4
5
  gemspec
5
6
  gem 'rubocop', group: 'development', require: false
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
- * RPG library (stub classes for serialization of RPGMaker game data)
9
- * RGSS library (some more classes for RPGMaker serialization)
10
- * rvpacker-txt (the script you call on the frontend)
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 DIRECTORY Input directory of RPG Maker project.
32
- Must contain "Data" or "original" folder to read,
33
- and additionally "translation" with "maps" and "other" subdirectories to write.
34
- --no Don't process specified files.
35
- Takes multiple values separated by a comma.
36
- Allowed values: maps, other, system, plugins
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,88 +1,130 @@
1
1
  #!/usr/bin/env ruby
2
- require 'classes'
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
21
+
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
4
25
 
5
- $logging = false
6
- $shuffle = 0
7
- $no = [false, false, false, false] # 0 is whether to NOT process maps, 1 is other, 2 is system, 3 is scripts
8
- $disable_custom_parsing = false
9
-
10
- options = {}
11
- OptionParser.new do |command|
12
- command.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"
13
-
14
- command.on('-d',
15
- '--input-dir DIRECTORY',
16
- 'Input directory of RPG Maker project.',
17
- 'Must contain "Data" or "original" folder to read,',
18
- 'and additionally "translation" with "maps" and "other" subdirectories to write.') { |dir| options[:input_dir] = dir }
19
-
20
- command.on('--no',
21
- "Don't process specified files.",
22
- 'Takes multiple values separated by a comma.',
23
- 'Allowed values: maps, other, system, plugins') do |files|
24
- actual_files = files.split(',')
25
-
26
- actual_files.each do |file|
27
- case file
28
- when "maps"
29
- $no[0] = true
30
- when "other"
31
- $no[1] = true
32
- when "system"
33
- $no[2] = true
34
- when "scripts"
35
- $no[3] = true
36
- else
37
- puts "Wrong value for no argument: #{file}.\nAllowed values: maps, other, system, plugins"
38
- exit
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
39
30
  end
40
31
  end
41
- end
42
32
 
43
- command.on('-s',
44
- '--shuffle NUMBER',
45
- 'At value 1: Shuffles all lines in strings, at value 2: shuffles all lines and words in strings.') { |number| $shuffle = number }
33
+ cmd.on('-s', '--shuffle NUM', Integer, 'Shuffle level (1: lines, 2: lines and words)') do |num|
34
+ options[:shuffle_level] = num
35
+ end
46
36
 
47
- command.on('--disable-custom-parsing',
48
- 'Disables built-in custom parsing for some games, which may improperly parse/write some games.')
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
49
44
 
50
- command.on('-l',
51
- '--log',
52
- 'Log information while processing.') { $logging = true }
45
+ cmd.on('-h', '--help', 'Show help message') do
46
+ puts cmd
47
+ exit
48
+ end
49
+ end.parse!
53
50
 
54
- command.on_tail('-h',
55
- '--help',
56
- 'Show help message.') { puts command; exit }
57
- 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])
58
54
 
59
- if ARGV.empty?
60
- puts 'COMMAND argument is required. Use rvpacker-txt -h for help.'
61
- exit
55
+ options
62
56
  end
63
57
 
64
- options[:action] = ARGV.shift
58
+ def self.get_game_type(system_file_path, disable_custom_parsing)
59
+ return nil if disable_custom_parsing
65
60
 
66
- unless %w[read write].include?(options[:action])
67
- puts 'Invalid command. Allowed commands are: read, write.'
68
- exit
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
69
64
  end
70
65
 
71
- directory = options[:input_dir]
72
- raise "#{directory} not found" unless File.exist?(directory)
73
- directory = File.realpath(directory)
74
-
75
- original_directory = Dir.foreach(directory).find { |dirname| dirname.downcase == 'original' || dirname.downcase == 'data' }
76
- raise '"Data" or "original" directory not found within input directory.' if original_directory.nil?
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.")
94
+
95
+ files = Dir.glob("#{paths[:original_path]}/*#{extensions[engine]}")
96
+
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
77
115
 
78
- engine = if File.exist?(File.join(directory, original_directory, 'System.rxdata'))
79
- :xp
80
- elsif File.exist?(File.join(directory, original_directory, 'System.rvdata'))
81
- :vx
82
- elsif File.exist?(File.join(directory, original_directory, 'System.rvdata2'))
83
- :ace
84
- else
85
- raise "Couldn't determine project engine."
86
- end
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
87
129
 
88
- RGSS.serialize(engine, options[:action], directory, original_directory,)
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
- Table.new(bytes)
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
- Color.new(bytes)
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
- Tone.new(bytes)
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
- Rect.new(bytes)
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
 
@@ -146,5 +181,3 @@ module RGSS
146
181
  process(Object, *symbol_array)
147
182
  end
148
183
  end
149
-
150
- require 'serialize'