R3EXS 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.
data/lib/R3EXS/ast.rb ADDED
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'prism'
4
+
5
+ module R3EXS
6
+
7
+ # 用来提取源码生成的 AST 中的字符串和符号
8
+ class StringsExtractor < Prism::Visitor
9
+
10
+ # @param strings [Array<String>] 存储提取出的字符串的数组
11
+ # @param with_symbol [Boolean] 是否包含脚本中的符号
12
+ # @return [StringsExtractor]
13
+ def initialize(strings, with_symbol)
14
+ @strings = strings
15
+ @with_symbol = with_symbol
16
+ end
17
+
18
+ # 处理类型为 StringNode 的节点
19
+ #
20
+ # @param node [Prism::StringNode] AST 节点
21
+ # @return [void]
22
+ def visit_string_node(node)
23
+ @strings << node.content
24
+ super
25
+ end
26
+
27
+ # 处理类型为 SymbolNode 的节点
28
+ #
29
+ # @param node [Prism::SymbolNode] AST 节点
30
+ # @return [void]
31
+ def visit_symbol_node(node)
32
+ @strings << node.value if @with_symbol
33
+ super
34
+ end
35
+
36
+ # 提取后存储的字符串数组
37
+ #
38
+ # @return [Array<String>]
39
+ attr_accessor :strings
40
+
41
+ # 是否包含脚本中的符号
42
+ #
43
+ # @return [Boolean]
44
+ attr_accessor :with_symbol
45
+
46
+ end
47
+
48
+ # 用来替换源码里面的字符串和符号
49
+ class StringsInjector < Prism::Visitor
50
+
51
+ # 用来记录字符串的位置
52
+ class Location
53
+
54
+ # @note start_offset 是字符串在二进制下打开时的位置
55
+ #
56
+ # @param start_offset [Integer] 字符串在源文件中的起始位置
57
+ # @param length [Integer] 字符串的长度
58
+ # @param content [String] 字符串内容
59
+ # @return [Location]
60
+ def initialize(start_offset, length, content)
61
+ @start_offset = start_offset
62
+ @length = length
63
+ @content = content
64
+ end
65
+
66
+ # 字符串在二进制源文件中的起始位置
67
+ #
68
+ # @return [Integer]
69
+ attr_reader :start_offset
70
+
71
+ # 字符串的长度
72
+ #
73
+ # @return [Integer]
74
+ attr_reader :length
75
+
76
+ # 字符串内容
77
+ #
78
+ # @return [String]
79
+ attr_reader :content
80
+
81
+ end
82
+
83
+ # @param hash [Hash<String, String>] 字符串翻译表
84
+ # @return [StringsInjector]
85
+ def initialize(hash)
86
+ @strings_hash = hash
87
+ @content_loc = []
88
+ @code = []
89
+ end
90
+
91
+ # 处理类型为 StringNode 的节点
92
+ #
93
+ # @param node [Prism::StringNode] AST 节点
94
+ # @return [void]
95
+ def visit_string_node(node)
96
+ location = node.content_loc
97
+ value = location.slice
98
+ if @strings_hash.has_key?(value)
99
+ @content_loc << Location.new(location.start_offset, location.length, @strings_hash[value])
100
+ end
101
+ super
102
+ end
103
+
104
+ # 处理类型为 SymbolNode 的节点
105
+ #
106
+ # @param node [Prism::SymbolNode] AST 节点
107
+ # @return [void]
108
+ def visit_symbol_node(node)
109
+ location = node.value_loc
110
+ # 如果 location 不为 nil,说明这个符号是一个字符串
111
+ if location
112
+ value = location.slice
113
+ if @strings_hash.has_key?(value)
114
+ @content_loc << Location.new(location.start_offset, location.length, @strings_hash[value])
115
+ end
116
+ end
117
+ super
118
+ end
119
+
120
+ # 将 file_path 对应位置的源码中的字符串替换成 '@strings_hash' 翻译后的字符串
121
+ #
122
+ # @param file_path [String] 源文件路径
123
+ # @param ast_root [Prism::ProgramNode] AST 树根节点
124
+ # @return [String]
125
+ def rewrite(file_path, ast_root)
126
+ # 读取文件内容
127
+ # 这里必须使用 rb 模式,因为 Prism 定位的位置是二进制下的位置
128
+ # 也就是说没有考虑换行符的问题,所以必须使用二进制模式读取文件
129
+ code = File.read(file_path, mode: 'rb')
130
+
131
+ # 首先遍历一遍,找到所有需要替换的字符串的位置
132
+ visit(ast_root)
133
+
134
+ # 然后开始替换 code 中的字符串
135
+ # 先将 @content_loc 按照 start_offset 从小到大排序
136
+ @content_loc.sort_by! { |loc| loc.start_offset }
137
+
138
+ # 然后将 code 切片,将字符串替换成新的字符串
139
+ start_offset = 0
140
+ @content_loc.each do |loc|
141
+ @code << code[start_offset...loc.start_offset]
142
+ @code << loc.content
143
+ start_offset = loc.start_offset + loc.length
144
+ end
145
+ @code << code[start_offset..-1]
146
+
147
+ # 将 @code 里面的字符串全部改为 UTF-8 编码
148
+ @code.map! { |str| str.force_encoding('UTF-8') }
149
+ @code.join
150
+ end
151
+
152
+ end
153
+
154
+ end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ module R3EXS
4
+
5
+ # 用来处理 RPG 模块下的类型错误的异常
6
+ class RPGTypeError < TypeError
7
+
8
+ # @param msg [String] 异常信息
9
+ # @param obj [Object] 引发异常的类
10
+ # @return [RPGTypeError]
11
+ def initialize(msg = '', obj)
12
+ super(msg)
13
+ @obj = obj
14
+ end
15
+
16
+ # 引发异常的类
17
+ #
18
+ # @return [Object]
19
+ attr_reader :obj
20
+ end
21
+
22
+ # 用来处理 R3EXS 模块下的类型错误的异常
23
+ class R3EXSTypeError < TypeError
24
+
25
+ # @param msg [String] 异常信息
26
+ # @param obj [Object] 引发异常的的类型
27
+ # @return [R3EXSTypeError]
28
+ def initialize(msg = '', obj)
29
+ super(msg)
30
+ @obj = obj
31
+ end
32
+
33
+ # 引发异常的类型
34
+ #
35
+ # @return [Object]
36
+ attr_reader :obj
37
+ end
38
+
39
+ # 用来处理 rvdata2 文件损坏的异常
40
+ class Rvdata2FileError < StandardError
41
+
42
+ # @param msg [String] 异常信息
43
+ # @param rvdata2_path [String] 引发异常的 rvdata2 文件路径
44
+ # @return [Rvdata2DirError]
45
+ def initialize(msg = '', rvdata2_path)
46
+ super(msg)
47
+ @rvdata2_path = rvdata2_path
48
+ end
49
+
50
+ # 引发异常的 rvdata2 文件路径
51
+ #
52
+ # @return [String]
53
+ attr_reader :rvdata2_path
54
+ end
55
+
56
+ # 用来处理 RPG 模块下 JSON 文件损坏的异常
57
+ class RPGJsonFileError < StandardError
58
+
59
+ # @param msg [String] 异常信息
60
+ # @param json_path [String] 引发异常的 json 文件路径
61
+ # @return [RPGJsonFileError]
62
+ def initialize(msg = '', json_path)
63
+ super(msg)
64
+ @json_path = json_path
65
+ end
66
+
67
+ # 引发异常的 json 文件路径
68
+ #
69
+ # @return [String]
70
+ attr_reader :json_path
71
+ end
72
+
73
+ # 用来处理 R3EXS 模块下 JSON 文件损坏的异常
74
+ class R3EXSJsonFileError < StandardError
75
+
76
+ # @param msg [String] 异常信息
77
+ # @param json_path [String] 引发异常的 json 文件路径
78
+ # @return [R3EXSJsonFileError]
79
+ def initialize(msg = '', json_path)
80
+ super(msg)
81
+ @json_path = json_path
82
+ end
83
+
84
+ # 引发异常的 json 文件路径
85
+ #
86
+ # @return [String]
87
+ attr_reader :rvdata2_path
88
+ end
89
+
90
+ # 用来处理模块名错误的异常
91
+ class ModuleNameError < ArgumentError
92
+
93
+ # @param msg [String] 异常信息
94
+ # @param module_name [Symbol] 引发异常的模块名
95
+ # @return [ModuleNameError]
96
+ def initialize(msg = '', module_name)
97
+ super(msg)
98
+ @module_name = module_name
99
+ end
100
+
101
+ # 引发异常的模块名
102
+ #
103
+ # @return [Symbol]
104
+ attr_reader :module_name
105
+ end
106
+
107
+ # 用来处理文件名错误的异常
108
+ class FileBaseNameError < ArgumentError
109
+
110
+ # @param msg [String] 异常信息
111
+ # @param filebasename [String] 引发异常的文件名
112
+ # @return [FileBaseNameError]
113
+ def initialize(msg = '', filebasename)
114
+ super(msg)
115
+ @filebasename = filebasename
116
+ end
117
+
118
+ # 引发异常的文件名
119
+ #
120
+ # @return [String]
121
+ attr_reader :filebasename
122
+ end
123
+
124
+ # 用来处理 *.rvdata2 文件目录不存在的异常
125
+ class Rvdata2DirError < IOError
126
+
127
+ # @param msg [String] 异常信息
128
+ # @param rvdata2_dir [String] 引发异常的 *.rvdata2 文件目录
129
+ # @return [Rvdata2DirError]
130
+ def initialize(msg = '', rvdata2_dir)
131
+ super(msg)
132
+ @rvdata2_dir = rvdata2_dir
133
+ end
134
+
135
+ # 引发异常的 *.rvdata2 文件目录
136
+ #
137
+ # @return [String]
138
+ attr_reader :rvdata2_dir
139
+ end
140
+
141
+ # 用来处理 *.json 文件目录不存在的异常
142
+ class JsonDirError < IOError
143
+
144
+ # @param msg [String] 异常信息
145
+ # @param json_dir [String] 引发异常的 *.json 文件目录
146
+ # @return [JsonDirError]
147
+ def initialize(msg = '', json_dir)
148
+ super(msg)
149
+ @json_dir = json_dir
150
+ end
151
+
152
+ # 引发异常的 *.json 文件目录
153
+ #
154
+ # @return [String]
155
+ attr_reader :json_dir
156
+ end
157
+
158
+ # 用来处理 *.rb 文件目录不存在的异常
159
+ class ScriptsDirError < IOError
160
+
161
+ # @param msg [String] 异常信息
162
+ # @param scripts_dir [String] 引发异常的 *.rb 文件目录
163
+ # @return [ScriptsDirError]
164
+ def initialize(msg = '', scripts_dir)
165
+ super(msg)
166
+ @scripts_dir = scripts_dir
167
+ end
168
+
169
+ # 引发异常的 *.rb 文件目录
170
+ #
171
+ # @return [String]
172
+ attr_reader :scripts_dir
173
+ end
174
+
175
+ # 用来处理 Scripts_info.json 文件不存在的异常
176
+ class ScriptsInfoPathError < IOError
177
+
178
+ # @param msg [String] 异常信息
179
+ # @param scripts_info_path [String] 引发异常的 Scripts_info.json 文件路径
180
+ # @return [ScriptsInfoPathError]
181
+ def initialize(msg = '', scripts_info_path)
182
+ super(msg)
183
+ @scripts_info_path = scripts_info_path
184
+ end
185
+
186
+ # 引发异常的 Scripts_info.json 文件路径
187
+ #
188
+ # @return [String]
189
+ attr_reader :scripts_info_path
190
+ end
191
+
192
+ # 用来处理 ManualTransFile.json 文件不存在的异常
193
+ class ManualTransFilePathError < IOError
194
+
195
+ # @param msg [String] 异常信息
196
+ # @param manualtransfile_path [String] 引发异常的 ManualTransFile.json 文件路径
197
+ # @return [ManualTransFilePathError]
198
+ def initialize(msg = '', manualtransfile_path)
199
+ super(msg)
200
+ @manualtransfile_path = manualtransfile_path
201
+ end
202
+
203
+ # 引发异常的 ManualTransFile.json 文件路径
204
+ #
205
+ # @return [String]
206
+ attr_reader :manualtransfile_path
207
+ end
208
+
209
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+ require_relative 'ast'
5
+ require_relative 'utils'
6
+ require_relative 'RGSS3_R3EXS'
7
+
8
+ module R3EXS
9
+
10
+ # 将 Ruby 源码中的字符串和符号提取出来
11
+ #
12
+ # @param target_dir [String] 目标目录
13
+ # @param with_symbol [Boolean] 是否包含脚本中的符号
14
+ #
15
+ # @raise [ScriptsDirError] Scripts 目录不存在
16
+ #
17
+ # @return [Array<String>]
18
+ def R3EXS.rb_ex_strings(target_dir, with_symbol)
19
+ full_dir = File.join(target_dir, 'Scripts')
20
+ Dir.exist?(full_dir) or raise ScriptsDirError.new(full_dir), "Scripts directory not found: #{full_dir}"
21
+
22
+ strings = []
23
+ strings_extractor = StringsExtractor.new(strings, with_symbol)
24
+ Dir.glob(File.join(full_dir, '*.rb')).each do |script_file_path|
25
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Exreacting from #{Utils::RESET_COLOR}#{script_file_path}...\r" if $global_options[:verbose]
26
+
27
+ strings_extractor.visit(Prism.parse_file(script_file_path).value)
28
+
29
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Exreacted #{Utils::RESET_COLOR}#{script_file_path}\n" if $global_options[:verbose]
30
+ end
31
+ strings
32
+ end
33
+
34
+ # 将指定目录下的所有已经序列化为 R3EXS 后的 JOSN 文件中的字符串提取出来
35
+ #
36
+ # @param target_dir [String] 目标目录
37
+ # @param output_dir [String] 输出目录
38
+ # @param with_scripts [Boolean] 是否包含脚本
39
+ # @param with_symbol [Boolean] 是否包含脚本中的符号
40
+ # @param with_scripts_separate [Boolean] 是否将脚本提取的字符串单独存放
41
+ #
42
+ # @raise [R3EXSJsonFileError] json 文件不是 R3EXS 模块中的对象
43
+ # @raise [JsonDirError] target_dir 不存在
44
+ # @raise [ScriptsDirError] Scripts 目录不存在
45
+ #
46
+ # @return [void]
47
+ def R3EXS.ex_strings(target_dir, output_dir, with_scripts, with_symbol, with_scripts_separate)
48
+ FileUtils.mkdir(output_dir) unless Dir.exist?(output_dir)
49
+ all_ex_strings = []
50
+
51
+ Utils.all_json_files(target_dir, :R3EXS) do |object, file_basename|
52
+ file_path = File.join(target_dir, "#{file_basename}.json")
53
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Extracting from #{Utils::RESET_COLOR}#{file_path}...\r" if $global_options[:verbose]
54
+ if object.is_a?(Array)
55
+ object.each do |obj|
56
+ all_ex_strings.concat(obj.ex_strings)
57
+ end
58
+ else
59
+ all_ex_strings.concat(object.ex_strings)
60
+ end
61
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Extracted #{Utils::RESET_COLOR}#{file_basename}\n" if $global_options[:verbose]
62
+ end
63
+
64
+ if with_scripts
65
+ scripts_strings = rb_ex_strings(target_dir, with_symbol)
66
+ if with_scripts_separate
67
+ # 去除 nil 元素
68
+ scripts_strings.compact!
69
+ Utils.object_json(scripts_strings.each_with_object({}) { |item, h| h[item] = item }, File.join(output_dir, 'ManualTransFile_scripts.json'))
70
+ else
71
+ all_ex_strings.concat(scripts_strings)
72
+ end
73
+ end
74
+
75
+ # 去除 nil 元素
76
+ all_ex_strings.compact!
77
+ Utils.object_json(all_ex_strings.each_with_object({}) { |item, h| h[item] = item }, File.join(output_dir, "ManualTransFile.json"))
78
+ end
79
+
80
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+ require_relative 'ast'
5
+ require_relative 'utils'
6
+ require_relative 'RGSS3_R3EXS'
7
+
8
+ module R3EXS
9
+
10
+ # 将 Ruby 源码中的字符串和符号替换为指定的字符串
11
+ #
12
+ # @param target_dir [String] 目标目录
13
+ # @param output_dir [String] 输出目录
14
+ # @param hash [Hash]
15
+ #
16
+ # @raise [ScriptsDirError] Scripts 目录不存在
17
+ #
18
+ # @return [void]
19
+ def R3EXS.rb_in_strings(target_dir, output_dir, hash)
20
+ target_full_dir = File.join(target_dir, 'Scripts')
21
+ output_full_dir = File.join(output_dir, 'Scripts')
22
+ FileUtils.mkdir(output_full_dir) unless Dir.exist?(output_full_dir)
23
+ Dir.exist?(target_full_dir) or raise ScriptsDirError.new(target_full_dir), "Scripts directory not found: #{target_full_dir}"
24
+
25
+ Dir.glob(File.join(target_full_dir, "*.rb")).each do |script_file_path|
26
+ output_script_file_dir = File.join(output_full_dir, File.basename(script_file_path))
27
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Injecting to #{Utils::RESET_COLOR}#{output_script_file_dir}...\r" if $global_options[:verbose]
28
+
29
+ File.write(output_script_file_dir, StringsInjector.new(hash).rewrite(script_file_path, Prism.parse_file(script_file_path).value), mode: 'w')
30
+
31
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Injected #{Utils::RESET_COLOR}#{output_script_file_dir}\n" if $global_options[:verbose]
32
+ end
33
+ end
34
+
35
+ # 将指定目录下的所有已经序列化为 R3EXS 后的 JOSN 文件按照 ManualTransFile.json 翻译注入
36
+ #
37
+ # @param target_dir [String] 目标目录
38
+ # @param output_dir [String] 输出目录
39
+ # @param manualtransfile_path [String] ManualTransFile.json 文件路径
40
+ # @param with_scripts [Boolean] 是否包含脚本
41
+ #
42
+ # @raise [R3EXSJsonFileError] json 文件不是 R3EXS 模块中的对象
43
+ # @raise [JsonDirError] target_dir 不存在
44
+ # @raise [ScriptsDirError] Scripts 目录不存在
45
+ # @raise [ManualTransFilePath] ManualTransFile.json 不存在
46
+ #
47
+ # @return [void]
48
+ def R3EXS.in_strings(target_dir, output_dir, manualtransfile_path, with_scripts)
49
+ FileUtils.mkdir(output_dir) unless Dir.exist?(output_dir)
50
+ File.exist?(manualtransfile_path) or raise ManualTransFilePathError.new(manualtransfile_path), "ManualTransFile.json not found: #{manualtransfile_path}"
51
+
52
+ manual_trans_hash = Oj.load_file(manualtransfile_path)
53
+
54
+ Utils.all_json_files(target_dir, :R3EXS) do |object, file_basename|
55
+ file_path = File.join(output_dir, "#{file_basename}.json")
56
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Injecting to #{Utils::RESET_COLOR}#{file_path}...\r" if $global_options[:verbose]
57
+ if object.is_a?(Array)
58
+ object.each do |obj|
59
+ obj.in_strings(manual_trans_hash)
60
+ end
61
+ else
62
+ object.in_strings(manual_trans_hash)
63
+ end
64
+ Utils.object_json(object, file_path)
65
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Injected #{Utils::RESET_COLOR}#{file_basename}\n" if $global_options[:verbose]
66
+ end
67
+
68
+ if with_scripts
69
+ rb_in_strings(target_dir, output_dir, manual_trans_hash)
70
+ end
71
+ end
72
+
73
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+ require_relative 'utils'
5
+ require_relative 'RGSS3_R3EXS'
6
+
7
+ module R3EXS
8
+
9
+ # 将 Ruby 源码读取压缩后序列化到输出目录
10
+ #
11
+ # @param target_dir [String] 目标目录
12
+ # @param output_dir [String] 输出目录
13
+ #
14
+ # @raise [ScriptsDirError] Scripts 目录不存在
15
+ # @raise [ScriptsInfoPathError] Scripts_info.json 文件不存在
16
+ #
17
+ # @return [void]
18
+ def R3EXS.rb_scripts(target_dir, output_dir)
19
+ FileUtils.mkdir(output_dir) unless Dir.exist?(output_dir)
20
+ scripts_array = []
21
+ full_dir = File.join(target_dir, 'Scripts')
22
+ script_info_file_path = File.join(full_dir, 'Scripts_info.json')
23
+
24
+ Dir.exist?(full_dir) or raise ScriptsDirError.new(full_dir), "Scripts directory not found: #{full_dir}"
25
+ File.exist?(script_info_file_path) or raise ScriptsInfoPathError.new(script_info_file_path), "Scripts_info.json not found: #{script_info_file_path}"
26
+
27
+ print "#{Utils::ESCAPE}#{Utils::YELLOW_COLOR}Reading from #{Utils::RESET_COLOR}#{script_info_file_path}...\r" if $global_options[:verbose]
28
+ scripts_info_array = Oj.load_file(script_info_file_path)
29
+
30
+ scripts_info_array.each do |script_info|
31
+ index = script_info[:index]
32
+ script_file_path = File.join(full_dir, "#{format('%03d', index)}.rb")
33
+ print "#{Utils::ESCAPE}#{Utils::YELLOW_COLOR}Reading from #{Utils::RESET_COLOR}#{script_file_path}...\r" if $global_options[:verbose]
34
+ scripts_array << [114514, script_info[:name], Zlib::Deflate.deflate(File.read(script_file_path, mode: "r"))]
35
+ end
36
+ Utils.object_rvdata2(scripts_array, File.join(output_dir, 'Scripts.rvdata2'))
37
+ end
38
+
39
+ # 将指定目录下的所有JSON文件转换为 rvdata2 文件
40
+ #
41
+ # @param [String] target_dir 目标目录
42
+ # @param [String] output_dir 输出目录
43
+ # @param [String] original_dir 原始 rvdata2 文件目录
44
+ # @param [Boolean] complete 是否完全转换
45
+ # @param [Boolean] with_scripts 是否转换Scripts
46
+ #
47
+ # @raise [RPGJsonFileError] json 文件不是 RPG 模块中的对象
48
+ # @raise [R3EXSJsonFileError] json 文件不是 R3EXS 模块中的对象
49
+ # @raise [Rvdata2FileError] 原始 rvdata2 文件可能损坏
50
+ # @raise [JsonDirError] target_dir 不存在
51
+ # @raise [Rvdata2DirError] original_dir 不存在
52
+ # @raise [ScriptsDirError] Scripts 目录不存在
53
+ # @raise [ScriptsInfoPathError] Scripts_info.json 文件不存在
54
+ #
55
+ # @return [void]
56
+ def R3EXS.json_rvdata2(target_dir, output_dir, original_dir, complete, with_scripts)
57
+ FileUtils.mkdir(output_dir) unless Dir.exist?(output_dir)
58
+
59
+ if complete
60
+ Utils.all_json_files(target_dir, :RPG) do |object, file_basename|
61
+ file_path = File.join(output_dir, "#{file_basename}.rvdata2")
62
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Serializing to #{Utils::RESET_COLOR}#{file_path}...\n" if $global_options[:verbose]
63
+ Utils.object_rvdata2(object, file_path)
64
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Serialized #{Utils::RESET_COLOR}#{file_basename}\n" if $global_options[:verbose]
65
+ end
66
+ else
67
+ # 检查 original_dir 是否存在
68
+ Dir.exist?(original_dir) or raise Rvdata2DirError.new(original_dir), "Original rvdata2 directory not found: #{original_dir}"
69
+ Utils.all_json_files(target_dir, :R3EXS) do |object, file_basename|
70
+
71
+ original_file_path = File.join(original_dir, file_basename + '.rvdata2')
72
+ print "#{Utils::ESCAPE}#{Utils::BLUE_COLOR}Reading and Deserializing #{Utils::RESET_COLOR}#{original_file_path}...\r" if $global_options[:verbose]
73
+ original_object = File.open(original_file_path, 'rb') { |file| Marshal.load(file) }
74
+
75
+ # 这里的类型检查要用紧凑模式,因为 rvdata2 文件中可能存在 nil 元素,必须忽略
76
+ begin
77
+ Utils.check_type(original_object, file_basename, true, :RPG)
78
+ rescue RPGTypeError
79
+ raise Rvdata2FileError.new(original_file_path), "Invalid rvdata2 file: #{original_file_path}"
80
+ end
81
+
82
+ file_path = File.join(output_dir, "#{file_basename}.rvdata2")
83
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Serializing to #{Utils::RESET_COLOR}#{file_path}...\n" if $global_options[:verbose]
84
+
85
+ # 根据是否为数组进行不同的处理
86
+ if object.is_a?(Array)
87
+ object.each do |obj|
88
+ obj.inject_to(original_object[obj.index])
89
+ end
90
+ else
91
+ object.inject_to(original_object)
92
+ end
93
+ Utils.object_rvdata2(original_object, file_path)
94
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Serialized #{Utils::RESET_COLOR}#{file_basename}\n" if $global_options[:verbose]
95
+ end
96
+ end
97
+
98
+ if with_scripts
99
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Serializing to #{Utils::RESET_COLOR}#{File.join(output_dir, 'Scripts.rvdata2')}...\r" if $global_options[:verbose]
100
+ rb_scripts(target_dir, output_dir)
101
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Serialized #{Utils::RESET_COLOR}Scripts\n" if $global_options[:verbose]
102
+ end
103
+ end
104
+
105
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+ require_relative 'utils'
5
+ require_relative 'RGSS3_R3EXS'
6
+
7
+ module R3EXS
8
+
9
+ # 将 Script 对象数组序列化为 Ruby 源码
10
+ #
11
+ # @param scripts [Array<Array>] 待转换的 Script 对象数组
12
+ # @param output_dir [String] 输出目录
13
+ # @return [void]
14
+ def R3EXS.scripts_rb(scripts, output_dir)
15
+ FileUtils.mkdir(output_dir) unless Dir.exist?(output_dir)
16
+ full_dir = File.join(output_dir, 'Scripts')
17
+ FileUtils.mkdir(full_dir) unless Dir.exist?(full_dir)
18
+
19
+ scripts_info_array = []
20
+ scripts.each_with_index do |script, index|
21
+ next if script.nil?
22
+ scripts_info_array << { index: index, name: script[1] }
23
+ script_file_path = File.join(full_dir, "#{format('%03d', index)}.rb")
24
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Serializing to #{Utils::RESET_COLOR}#{script_file_path}...\r" if $global_options[:verbose]
25
+ File.write(script_file_path, Zlib::Inflate.inflate(script[2]).encode(universal_newline: true), mode: 'w')
26
+ end
27
+
28
+ script_info_file_path = File.join(full_dir, 'Scripts_info.json')
29
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Serializing to #{Utils::RESET_COLOR}#{script_info_file_path}\r" if $global_options[:verbose]
30
+ Utils.object_json(scripts_info_array, script_info_file_path)
31
+ end
32
+
33
+ # 将指定目录下的所有 rvdata2 文件序列化为 JSON 格式
34
+ #
35
+ # @param target_dir [String] 目标目录
36
+ # @param output_dir [String] 输出目录
37
+ # @param complete [Boolean] 是否序列化所有内容
38
+ # @param with_scripts [Boolean] 是否包含脚本
39
+ # @param with_notes [Boolean] 是否包含备注
40
+ #
41
+ # @raise [Rvdata2FileError] rvdata2 文件可能损坏
42
+ # @raise [Rvdata2DirError] target_dir 不存在
43
+ #
44
+ # @return [void]
45
+ def R3EXS.rvdata2_json(target_dir, output_dir, complete, with_scripts, with_notes)
46
+ FileUtils.mkdir(output_dir) unless Dir.exist?(output_dir)
47
+ Utils.all_rvdata2_files(target_dir) do |object, file_basename|
48
+
49
+ if file_basename == 'Scripts'
50
+ scripts_rb(object, output_dir) if with_scripts
51
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Serialized #{Utils::RESET_COLOR}#{file_basename}\n" if $global_options[:verbose]
52
+ next
53
+ end
54
+
55
+ file_path = File.join(output_dir, "#{file_basename}.json")
56
+ print "#{Utils::ESCAPE}#{Utils::MAGENTA_COLOR}Serializing to #{Utils::RESET_COLOR}#{file_path}...\r" if $global_options[:verbose]
57
+ if complete
58
+ Utils.object_json(object, file_path)
59
+ else
60
+ Utils.object_json(Utils.rpg_r3exs(object, file_basename, with_notes), file_path)
61
+ end
62
+ print "#{Utils::ESCAPE}#{Utils::GREEN_COLOR}Serialized #{Utils::RESET_COLOR}#{file_basename}\n" if $global_options[:verbose]
63
+ end
64
+ end
65
+
66
+ end