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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +260 -0
- data/README_EN.md +260 -0
- data/bin/R3EXS +217 -0
- data/ext/rgss3a_rvdata2/extconf.rb +3 -0
- data/ext/rgss3a_rvdata2/rgss3a_rvdata2.c +565 -0
- data/lib/R3EXS/RGSS3.rb +1457 -0
- data/lib/R3EXS/RGSS3_R3EXS.rb +1804 -0
- data/lib/R3EXS/ast.rb +154 -0
- data/lib/R3EXS/error.rb +209 -0
- data/lib/R3EXS/extract_strings.rb +80 -0
- data/lib/R3EXS/inject_strings.rb +73 -0
- data/lib/R3EXS/json_rvdata2.rb +105 -0
- data/lib/R3EXS/rvdata2_json.rb +66 -0
- data/lib/R3EXS/utils.rb +971 -0
- data/lib/R3EXS/version.rb +7 -0
- data/lib/R3EXS.rb +12 -0
- metadata +157 -0
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
|
data/lib/R3EXS/error.rb
ADDED
@@ -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
|