R3EXS 1.1.0 → 2.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 +4 -4
- data/.yardopts +10 -12
- data/CHANGELOG.md +46 -21
- data/README.md +17 -38
- data/README_EN.md +21 -40
- data/bin/R3EXS +191 -217
- data/ext/R3EXS/R3EXS.cxx +334 -0
- data/ext/R3EXS/extconf.rb +12 -0
- data/lib/R3EXS/RGSS3.rb +553 -1437
- data/lib/R3EXS/RGSS3_R3EXS.rb +1939 -1811
- data/lib/R3EXS/ast.rb +209 -149
- data/lib/R3EXS/error.rb +39 -209
- data/lib/R3EXS/extract_strings.rb +86 -63
- data/lib/R3EXS/inject_strings.rb +83 -61
- data/lib/R3EXS/json_rvdata2.rb +122 -109
- data/lib/R3EXS/logger.rb +44 -0
- data/lib/R3EXS/rvdata2_json.rb +79 -82
- data/lib/R3EXS/utils.rb +404 -1055
- data/lib/R3EXS/version.rb +3 -4
- data/lib/R3EXS.rb +14 -12
- metadata +20 -34
- data/ext/rgss3a_rvdata2/extconf.rb +0 -3
- data/ext/rgss3a_rvdata2/rgss3a_rvdata2.c +0 -616
data/ext/R3EXS/R3EXS.cxx
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
#include "ruby.h"
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
#include <cstring>
|
|
4
|
+
#include <filesystem>
|
|
5
|
+
#include <fstream>
|
|
6
|
+
#include <immintrin.h>
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include <print>
|
|
9
|
+
#include <span>
|
|
10
|
+
#include <unordered_set>
|
|
11
|
+
#include <vector>
|
|
12
|
+
|
|
13
|
+
#define GREEN_COLOR(str) "\e[32m" str "\e[0m" // 绿色
|
|
14
|
+
#define MAGENTA_COLOR(str) "\e[35m" str "\e[0m" // 紫色
|
|
15
|
+
|
|
16
|
+
namespace
|
|
17
|
+
{
|
|
18
|
+
|
|
19
|
+
// 加密文件头部常量
|
|
20
|
+
constexpr std::uint64_t RGSSAD_HEADER {0X0300444153534752}; // "RGSSAD\0\3"
|
|
21
|
+
constexpr std::uint64_t FUX2PACK2_HEADER {0X6B63615032787546}; // "Fux2Pack"
|
|
22
|
+
|
|
23
|
+
struct DecryptTask
|
|
24
|
+
{
|
|
25
|
+
std::span<char> filename;
|
|
26
|
+
std::uint32_t filename_magickey;
|
|
27
|
+
std::span<char> data;
|
|
28
|
+
std::uint32_t data_magickey;
|
|
29
|
+
std::filesystem::path output_full_path;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
class RGSS3AFileError: public std::runtime_error
|
|
33
|
+
{
|
|
34
|
+
public:
|
|
35
|
+
explicit RGSS3AFileError(const std::string& message): std::runtime_error(message)
|
|
36
|
+
{}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
template <typename T>
|
|
40
|
+
requires std::is_trivially_copyable_v<T>
|
|
41
|
+
[[nodiscard]]
|
|
42
|
+
inline T load_little_data(char*& data) noexcept
|
|
43
|
+
{
|
|
44
|
+
T t;
|
|
45
|
+
std::memcpy(&t, data, sizeof(T));
|
|
46
|
+
data += sizeof(T);
|
|
47
|
+
return t;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
constexpr std::uint32_t MOD_4_MASK {0B11};
|
|
51
|
+
|
|
52
|
+
inline void xor_u32(char* data, std::uint32_t key) noexcept
|
|
53
|
+
{
|
|
54
|
+
std::uint32_t value {};
|
|
55
|
+
std::memcpy(&value, data, sizeof(value));
|
|
56
|
+
value ^= key;
|
|
57
|
+
std::memcpy(data, &value, sizeof(value));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void decrypt_file_name(std::span<char> data, std::uint32_t magickey) noexcept
|
|
61
|
+
{
|
|
62
|
+
// 主循环:每轮处理 1 块,共 4 字节
|
|
63
|
+
auto n {data.size()};
|
|
64
|
+
auto* data_p {data.data()};
|
|
65
|
+
for (; data_p < data.data() + (n & ~MOD_4_MASK); data_p += 4)
|
|
66
|
+
{
|
|
67
|
+
xor_u32(data_p, magickey);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
switch (n & MOD_4_MASK)
|
|
71
|
+
{
|
|
72
|
+
case 3 : data_p[2] ^= magickey >> 16 & 0XFF; [[fallthrough]];
|
|
73
|
+
case 2 : data_p[1] ^= magickey >> 8 & 0XFF; [[fallthrough]];
|
|
74
|
+
case 1 : data_p[0] ^= magickey & 0XFF;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
void decrypt_file_data_scalar(std::span<char> data, std::uint32_t magickey) noexcept
|
|
79
|
+
{
|
|
80
|
+
// 主循环:每轮处理 1 块,共 4 字
|
|
81
|
+
auto n {data.size()};
|
|
82
|
+
auto* data_p {data.data()};
|
|
83
|
+
for (; data_p < data.data() + (n & ~MOD_4_MASK); data_p += 4)
|
|
84
|
+
{
|
|
85
|
+
xor_u32(data_p, magickey);
|
|
86
|
+
magickey = (magickey * 7) + 3;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
switch (n & MOD_4_MASK)
|
|
90
|
+
{
|
|
91
|
+
case 3 : data_p[2] ^= magickey >> 16 & 0XFF; [[fallthrough]];
|
|
92
|
+
case 2 : data_p[1] ^= magickey >> 8 & 0XFF; [[fallthrough]];
|
|
93
|
+
case 1 : data_p[0] ^= magickey & 0XFF;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#ifdef __AVX2__
|
|
98
|
+
constexpr std::uint32_t MOD_128_MASK {0B1111111};
|
|
99
|
+
|
|
100
|
+
alignas(32) constexpr std::uint32_t AVX2_MUL_TABLE[8] {
|
|
101
|
+
1U, // 7^0 mod 2^32
|
|
102
|
+
7U, // 7^1 mod 2^32
|
|
103
|
+
49U, // 7^2 mod 2^32
|
|
104
|
+
343U, // 7^3 mod 2^32
|
|
105
|
+
2401U, // 7^4 mod 2^32
|
|
106
|
+
16807U, // 7^5 mod 2^32
|
|
107
|
+
117649U, // 7^6 mod 2^32
|
|
108
|
+
823543U, // 7^7 mod 2^32
|
|
109
|
+
};
|
|
110
|
+
constexpr std::uint32_t AVX2_MUL_STEP {5764801U}; // 7^8 mod 2^32
|
|
111
|
+
constexpr std::uint32_t AVX2_MUL_STEP_4 {1855011585U}; // 7^32 mod 2^32
|
|
112
|
+
|
|
113
|
+
alignas(32) constexpr uint32_t AVX2_ADD_TABLE[8] {
|
|
114
|
+
0U, // (7^0-1)/2 mod 2^32
|
|
115
|
+
3U, // (7^1-1)/2 mod 2^32
|
|
116
|
+
24U, // (7^2-1)/2 mod 2^32
|
|
117
|
+
171U, // (7^3-1)/2 mod 2^32
|
|
118
|
+
1200U, // (7^4-1)/2 mod 2^32
|
|
119
|
+
8403U, // (7^5-1)/2 mod 2^32
|
|
120
|
+
58824U, // (7^6-1)/2 mod 2^32
|
|
121
|
+
411771U, // (7^7-1)/2 mod 2^32
|
|
122
|
+
};
|
|
123
|
+
constexpr std::uint32_t AVX2_ADD_STEP {2882400U}; // (7^8-1)/2 mod 2^32
|
|
124
|
+
constexpr std::uint32_t AVX2_ADD_STEP_4 {927505792U}; // (7^32-1)/2 mod 2^32
|
|
125
|
+
|
|
126
|
+
void decrypt_file_data_avx2_4_unroll(std::span<char> data, std::uint32_t magickey) noexcept
|
|
127
|
+
{
|
|
128
|
+
auto v_mul_table {_mm256_load_si256(reinterpret_cast<const __m256i*>(AVX2_MUL_TABLE))};
|
|
129
|
+
auto v_add_table {_mm256_load_si256(reinterpret_cast<const __m256i*>(AVX2_ADD_TABLE))};
|
|
130
|
+
|
|
131
|
+
// 4 路循环展开
|
|
132
|
+
auto v_mul_step {_mm256_set1_epi32(AVX2_MUL_STEP)};
|
|
133
|
+
auto v_add_step {_mm256_set1_epi32(AVX2_ADD_STEP)};
|
|
134
|
+
auto v_mul_step_4 {_mm256_set1_epi32(AVX2_MUL_STEP_4)};
|
|
135
|
+
auto v_add_step_4 {_mm256_set1_epi32(AVX2_ADD_STEP_4)};
|
|
136
|
+
|
|
137
|
+
// 4 路独立初始 key
|
|
138
|
+
auto v_key0 {_mm256_add_epi32(_mm256_mullo_epi32(_mm256_set1_epi32(magickey), v_mul_table), v_add_table)};
|
|
139
|
+
auto v_key1 {_mm256_add_epi32(_mm256_mullo_epi32(v_key0, v_mul_step), v_add_step)};
|
|
140
|
+
auto v_key2 {_mm256_add_epi32(_mm256_mullo_epi32(v_key1, v_mul_step), v_add_step)};
|
|
141
|
+
auto v_key3 {_mm256_add_epi32(_mm256_mullo_epi32(v_key2, v_mul_step), v_add_step)};
|
|
142
|
+
|
|
143
|
+
// 主循环:每轮处理 4 块,共 128 字节
|
|
144
|
+
auto simd_size {data.size() & ~MOD_128_MASK};
|
|
145
|
+
auto* data_p {data.data()};
|
|
146
|
+
for (; data_p < data.data() + simd_size; data_p += 128)
|
|
147
|
+
{
|
|
148
|
+
_mm256_storeu_si256(
|
|
149
|
+
reinterpret_cast<__m256i_u*>(data_p + 0),
|
|
150
|
+
_mm256_xor_si256(_mm256_loadu_si256(reinterpret_cast<__m256i_u*>(data_p + 0)), v_key0)
|
|
151
|
+
);
|
|
152
|
+
_mm256_storeu_si256(
|
|
153
|
+
reinterpret_cast<__m256i_u*>(data_p + 32),
|
|
154
|
+
_mm256_xor_si256(_mm256_loadu_si256(reinterpret_cast<__m256i_u*>(data_p + 32)), v_key1)
|
|
155
|
+
);
|
|
156
|
+
_mm256_storeu_si256(
|
|
157
|
+
reinterpret_cast<__m256i_u*>(data_p + 64),
|
|
158
|
+
_mm256_xor_si256(_mm256_loadu_si256(reinterpret_cast<__m256i_u*>(data_p + 64)), v_key2)
|
|
159
|
+
);
|
|
160
|
+
_mm256_storeu_si256(
|
|
161
|
+
reinterpret_cast<__m256i_u*>(data_p + 96),
|
|
162
|
+
_mm256_xor_si256(_mm256_loadu_si256(reinterpret_cast<__m256i_u*>(data_p + 96)), v_key3)
|
|
163
|
+
);
|
|
164
|
+
v_key0 = _mm256_add_epi32(_mm256_mullo_epi32(v_key0, v_mul_step_4), v_add_step_4);
|
|
165
|
+
v_key1 = _mm256_add_epi32(_mm256_mullo_epi32(v_key1, v_mul_step_4), v_add_step_4);
|
|
166
|
+
v_key2 = _mm256_add_epi32(_mm256_mullo_epi32(v_key2, v_mul_step_4), v_add_step_4);
|
|
167
|
+
v_key3 = _mm256_add_epi32(_mm256_mullo_epi32(v_key3, v_mul_step_4), v_add_step_4);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
decrypt_file_data_scalar(data.subspan(simd_size), _mm256_cvtsi256_si32(v_key0));
|
|
171
|
+
}
|
|
172
|
+
#endif
|
|
173
|
+
|
|
174
|
+
inline void decrypt_file_data_dispatch(std::span<char> data, std::uint32_t magickey) noexcept
|
|
175
|
+
{
|
|
176
|
+
#ifdef __AVX2__
|
|
177
|
+
decrypt_file_data_avx2_4_unroll(data, magickey);
|
|
178
|
+
#else
|
|
179
|
+
decrypt_file_data_scalar(data, magickey);
|
|
180
|
+
#endif
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
void write_file(const std::filesystem::path& path, std::span<char> data)
|
|
184
|
+
{
|
|
185
|
+
std::ofstream f {path, std::ios::binary};
|
|
186
|
+
f.write(data.data(), data.size());
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
std::unique_ptr<char[]> read_file(const std::filesystem::path& path)
|
|
190
|
+
{
|
|
191
|
+
auto size {std::filesystem::file_size(path)};
|
|
192
|
+
auto data {std::make_unique_for_overwrite<char[]>(size)};
|
|
193
|
+
std::ifstream f {path, std::ios::binary};
|
|
194
|
+
f.read(data.get(), size);
|
|
195
|
+
return data;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
void r3exs_rgss3a_rvdata2_cxx(
|
|
199
|
+
const std::filesystem::path& target_path, const std::filesystem::path& output_dir, bool verbose
|
|
200
|
+
)
|
|
201
|
+
{
|
|
202
|
+
// 读取整个 RGSS3A 文件到内存并设置文件指针游标
|
|
203
|
+
auto rgss3a_data {read_file(target_path)};
|
|
204
|
+
auto* rgss3a_p {rgss3a_data.get()};
|
|
205
|
+
|
|
206
|
+
// 判断加密类型并计算 magickey
|
|
207
|
+
auto decrypt_header {load_little_data<std::uint64_t>(rgss3a_p)};
|
|
208
|
+
auto magickey {load_little_data<std::uint32_t>(rgss3a_p)};
|
|
209
|
+
switch (decrypt_header)
|
|
210
|
+
{
|
|
211
|
+
[[likely]]
|
|
212
|
+
case RGSSAD_HEADER :
|
|
213
|
+
magickey = (magickey * 9) + 3;
|
|
214
|
+
break;
|
|
215
|
+
case FUX2PACK2_HEADER :
|
|
216
|
+
// Fux2Pack2 的 magickey 就是文件头中读取的值,不需要额外计算
|
|
217
|
+
break;
|
|
218
|
+
default : throw RGSS3AFileError {std::format("Unknown RGSS3A file decrypted type:{}", target_path.string())};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 记录所有的解密任务,最后统一处理
|
|
222
|
+
std::vector<DecryptTask> tasks;
|
|
223
|
+
tasks.reserve(1024);
|
|
224
|
+
while (true)
|
|
225
|
+
{
|
|
226
|
+
// 读取数据段偏移量
|
|
227
|
+
auto data_offset {load_little_data<std::uint32_t>(rgss3a_p) ^ magickey};
|
|
228
|
+
if (data_offset == 0) [[unlikely]]
|
|
229
|
+
{
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
// 读取数据段长度
|
|
233
|
+
auto data_size {load_little_data<std::uint32_t>(rgss3a_p) ^ magickey};
|
|
234
|
+
// 读取数据段 magicKey
|
|
235
|
+
auto data_magickey {load_little_data<std::uint32_t>(rgss3a_p) ^ magickey};
|
|
236
|
+
// 读取文件名长度
|
|
237
|
+
auto filename_size {load_little_data<std::uint32_t>(rgss3a_p) ^ magickey};
|
|
238
|
+
// 记录解密任务
|
|
239
|
+
tasks.emplace_back(
|
|
240
|
+
std::span<char> {rgss3a_p, filename_size},
|
|
241
|
+
magickey,
|
|
242
|
+
std::span<char> {rgss3a_data.get() + data_offset, data_size},
|
|
243
|
+
data_magickey
|
|
244
|
+
);
|
|
245
|
+
// 移动文件指针游标到下一个文件头
|
|
246
|
+
rgss3a_p += filename_size;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 处理所有的解密任务,并记录需要创建的目录
|
|
250
|
+
std::unordered_set<std::filesystem::path> dirs;
|
|
251
|
+
for (auto&& task : tasks)
|
|
252
|
+
{
|
|
253
|
+
decrypt_file_name(task.filename, task.filename_magickey);
|
|
254
|
+
decrypt_file_data_dispatch(task.data, task.data_magickey);
|
|
255
|
+
std::ranges::replace(task.filename, '\\', '/'); // 处理路径分隔符
|
|
256
|
+
|
|
257
|
+
task.output_full_path = output_dir / std::string_view(task.filename);
|
|
258
|
+
dirs.emplace(task.output_full_path.parent_path());
|
|
259
|
+
}
|
|
260
|
+
// 创建所有需要的目录
|
|
261
|
+
for (auto&& dir : dirs)
|
|
262
|
+
{
|
|
263
|
+
std::filesystem::create_directories(dir);
|
|
264
|
+
}
|
|
265
|
+
// 写入文件
|
|
266
|
+
for (auto&& task : tasks)
|
|
267
|
+
{
|
|
268
|
+
write_file(task.output_full_path, task.data);
|
|
269
|
+
if (verbose) [[unlikely]]
|
|
270
|
+
{
|
|
271
|
+
std::println(
|
|
272
|
+
GREEN_COLOR("Decrypted:") "{} " GREEN_COLOR("Size:") MAGENTA_COLOR("{}"),
|
|
273
|
+
task.output_full_path.string(),
|
|
274
|
+
task.data.size()
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* 解码 Game.rgss3a 文件中的 rvdata2 数据
|
|
282
|
+
*
|
|
283
|
+
* @param target_path Game.rgss3a 文件路径
|
|
284
|
+
* @param output_dir 输出目录
|
|
285
|
+
* @param verbose 是否输出详细信息
|
|
286
|
+
* @raise [RGSS3AFileError] 未知的 RGSS3A 文件加密类型
|
|
287
|
+
* @raise [SystemCallError] 系统调用失败
|
|
288
|
+
*
|
|
289
|
+
* @return [void]
|
|
290
|
+
*/
|
|
291
|
+
VALUE r3exs_rgss3a_rvdata2(VALUE self, VALUE target_path, VALUE output_dir, VALUE verbose)
|
|
292
|
+
{
|
|
293
|
+
try
|
|
294
|
+
{
|
|
295
|
+
r3exs_rgss3a_rvdata2_cxx(
|
|
296
|
+
std::filesystem::path(RSTRING_PTR(target_path)),
|
|
297
|
+
std::filesystem::path(RSTRING_PTR(output_dir)),
|
|
298
|
+
RB_TEST(verbose)
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
catch (const RGSS3AFileError& e)
|
|
302
|
+
{
|
|
303
|
+
rb_exc_raise(rb_funcall(
|
|
304
|
+
rb_const_get(self, rb_intern("RGSS3AFileError")),
|
|
305
|
+
rb_intern("new"),
|
|
306
|
+
2,
|
|
307
|
+
target_path,
|
|
308
|
+
rb_str_new_cstr(e.what())
|
|
309
|
+
));
|
|
310
|
+
}
|
|
311
|
+
catch (const std::filesystem::filesystem_error& e)
|
|
312
|
+
{
|
|
313
|
+
rb_syserr_fail(e.code().value(), e.what());
|
|
314
|
+
}
|
|
315
|
+
return Qnil;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
} // namespace
|
|
319
|
+
|
|
320
|
+
extern "C"
|
|
321
|
+
{
|
|
322
|
+
/*
|
|
323
|
+
* 初始化 R3EXS 模块
|
|
324
|
+
*
|
|
325
|
+
* @return [void]
|
|
326
|
+
*/
|
|
327
|
+
void Init_R3EXS()
|
|
328
|
+
{
|
|
329
|
+
// 定义 R3EXS 模块
|
|
330
|
+
VALUE R3EXS {rb_define_module("R3EXS")};
|
|
331
|
+
// 定义 rgss3a_rvdata2 方法
|
|
332
|
+
rb_define_singleton_method(R3EXS, "rgss3a_rvdata2", r3exs_rgss3a_rvdata2, 3);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mkmf'
|
|
4
|
+
|
|
5
|
+
$ARCH_FLAG = '-march=native'
|
|
6
|
+
$CXXFLAGS << ' -O3 -std=c++23' # 添加对C++23的支持
|
|
7
|
+
|
|
8
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
|
9
|
+
have_library('stdc++exp') # 在Windows上链接stdc++exp库以支持C++23特性
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
create_makefile('R3EXS/R3EXS')
|