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.
@@ -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')