R3EXS 1.1.0 → 1.1.1
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 +2 -2
- data/CHANGELOG.md +5 -0
- data/ext/R3EXS/R3EXS.c +601 -0
- data/ext/R3EXS/extconf.rb +3 -0
- data/lib/R3EXS/RGSS3.rb +71 -49
- data/lib/R3EXS/RGSS3_R3EXS.rb +483 -267
- data/lib/R3EXS/ast.rb +33 -24
- data/lib/R3EXS/error.rb +95 -59
- data/lib/R3EXS/extract_strings.rb +0 -1
- data/lib/R3EXS/inject_strings.rb +0 -1
- data/lib/R3EXS/json_rvdata2.rb +0 -1
- data/lib/R3EXS/rvdata2_json.rb +2 -1
- data/lib/R3EXS/utils.rb +5 -2
- data/lib/R3EXS/version.rb +1 -1
- data/lib/R3EXS.rb +2 -1
- metadata +10 -11
- data/ext/rgss3a_rvdata2/extconf.rb +0 -3
- data/ext/rgss3a_rvdata2/rgss3a_rvdata2.c +0 -616
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0e5f2730229700e811394349c95a58f8c882b7a10468fd2f707281313583397
|
4
|
+
data.tar.gz: 05a6b0319f97f1594c79eba492a68695abe64b15dcf928b2a1f56693ede0862e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd9eb1770019cd97f44c5811f5f2bd7d178304333b4735d521730f8d263e5c514ec857bea6a47f2ed1f2df6f230779e986691be0d18eed5f78025d54b2905adb
|
7
|
+
data.tar.gz: cfb6ef690a70f29779d941baba5e368c33cb934d0ecb3ee6bab02bf0eaba70eef52057123670458809912ffcbb41b16ad054b8d9176510934441d3d7d7bddc7c
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
data/ext/R3EXS/R3EXS.c
ADDED
@@ -0,0 +1,601 @@
|
|
1
|
+
#include <fcntl.h>
|
2
|
+
#include "ruby.h"
|
3
|
+
|
4
|
+
#define likely(x) __builtin_expect(!!(x), 1)
|
5
|
+
#define unlikely(x) __builtin_expect(!!(x), 0)
|
6
|
+
|
7
|
+
#ifdef _WIN32
|
8
|
+
wchar_t* filename_arr = NULL;
|
9
|
+
unsigned short filename_len = 0;
|
10
|
+
|
11
|
+
/*
|
12
|
+
* 将指定长度的 UTF-8 字符串转换为 wchar 字符串
|
13
|
+
* wchar 字符串会被存储在 filename_arr 中
|
14
|
+
* 同时更新 filename_len
|
15
|
+
*
|
16
|
+
* @param utf8char [const char*] UTF-8 字符串
|
17
|
+
* @param n [size_t] 字符串长度
|
18
|
+
*
|
19
|
+
* @return [int] 0 成功,-1 失败
|
20
|
+
*/
|
21
|
+
static int utf8towc(const char* utf8char, size_t n)
|
22
|
+
{
|
23
|
+
// 计算转换为 wchar 所需的缓冲区大小,包括结尾的 '\0'
|
24
|
+
int wc_size = MultiByteToWideChar(CP_UTF8, 0, utf8char, n, NULL, 0) + 1;
|
25
|
+
if (unlikely(wc_size == 1)) // 转换失败
|
26
|
+
{
|
27
|
+
if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION)
|
28
|
+
{
|
29
|
+
errno = EILSEQ;
|
30
|
+
return -1;
|
31
|
+
}
|
32
|
+
}
|
33
|
+
if (wc_size > filename_len)
|
34
|
+
{
|
35
|
+
wchar_t* wchar_arr_new = (wchar_t*)realloc(filename_arr, sizeof(wchar_t) * wc_size);
|
36
|
+
if (unlikely(!wchar_arr_new))
|
37
|
+
{
|
38
|
+
errno = ENOMEM;
|
39
|
+
return -1;
|
40
|
+
}
|
41
|
+
filename_arr = wchar_arr_new;
|
42
|
+
filename_len = wc_size;
|
43
|
+
}
|
44
|
+
|
45
|
+
// 执行从 UTF-8 到 wchar 的转换
|
46
|
+
MultiByteToWideChar(CP_UTF8, 0, utf8char, n, filename_arr, filename_len);
|
47
|
+
// 添加结尾的 '\0'
|
48
|
+
filename_arr[wc_size - 1] = L'\0';
|
49
|
+
return 0;
|
50
|
+
}
|
51
|
+
#endif
|
52
|
+
|
53
|
+
#ifdef __linux__
|
54
|
+
#include <sys/mman.h>
|
55
|
+
char* filename_arr = NULL;
|
56
|
+
unsigned short filename_len = 0;
|
57
|
+
|
58
|
+
/*
|
59
|
+
* 将指定长度的 UTF-8 字符串 转换为 char 字符串
|
60
|
+
* char 字符串会被存储在 char_arr 中
|
61
|
+
* 同时更新 char_arr_size
|
62
|
+
*
|
63
|
+
* @param utf8char [char*] UTF-8 字符串
|
64
|
+
* @param n [size_t] 字符串长度
|
65
|
+
*
|
66
|
+
* @return 0 成功,-1 失败
|
67
|
+
*/
|
68
|
+
static int utf8tomb(const char* utf8char, const size_t n)
|
69
|
+
{
|
70
|
+
// 计算转换为 char 所需的缓冲区大小,包括结尾的 '\0'
|
71
|
+
const size_t mb_size = n + 1;
|
72
|
+
if (mb_size > filename_len)
|
73
|
+
{
|
74
|
+
char* char_arr_new = (char*)realloc(filename_arr, sizeof(char) * mb_size);
|
75
|
+
if (unlikely(!char_arr_new))
|
76
|
+
{
|
77
|
+
errno = ENOMEM;
|
78
|
+
return -1;
|
79
|
+
}
|
80
|
+
filename_arr = char_arr_new;
|
81
|
+
filename_len = mb_size;
|
82
|
+
}
|
83
|
+
// 注意此处不能使用 strcpy
|
84
|
+
// 因为 utf8char 不是 '\0' 结尾字符串
|
85
|
+
memcpy(filename_arr, utf8char, n);
|
86
|
+
filename_arr[mb_size - 1] = '\0';
|
87
|
+
return 0;
|
88
|
+
}
|
89
|
+
#endif
|
90
|
+
|
91
|
+
VALUE R3EXS = Qnil;
|
92
|
+
|
93
|
+
ID r3exs_RGSS3AFileError_id;
|
94
|
+
ID r3exs_File_id;
|
95
|
+
ID r3exs_FileUtils_id;
|
96
|
+
ID r3exs_Dir_id;
|
97
|
+
ID r3exs_join_id;
|
98
|
+
ID r3exs_dirname_id;
|
99
|
+
ID r3exs_exist_id;
|
100
|
+
ID r3exs_mkdir_p_id;
|
101
|
+
|
102
|
+
VALUE r3exs_RGSS3AFileError_class;
|
103
|
+
VALUE r3exs_File_module;
|
104
|
+
VALUE r3exs_FileUtils_module;
|
105
|
+
VALUE r3exs_Dir_module;
|
106
|
+
|
107
|
+
// 解码文件类型
|
108
|
+
enum RGSSAD_DECRYPT_TYPE
|
109
|
+
{
|
110
|
+
RGSSAD,
|
111
|
+
Fux2Pack2
|
112
|
+
};
|
113
|
+
|
114
|
+
#ifdef __AVX512F__
|
115
|
+
#include <immintrin.h>
|
116
|
+
#define MOD_64_MASK 0b111111
|
117
|
+
#elifdef __AVX2__
|
118
|
+
#include <immintrin.h>
|
119
|
+
#define MOD_32_MASK 0b11111
|
120
|
+
#endif
|
121
|
+
|
122
|
+
#define MOD_4_MASK 0b11
|
123
|
+
#define MASK_KEY_1 0x000000FF
|
124
|
+
#define MASK_KEY_2 0x0000FFFF
|
125
|
+
#define MASK_KEY_3 0x00FFFFFF
|
126
|
+
|
127
|
+
/*
|
128
|
+
* 解码文件名
|
129
|
+
*
|
130
|
+
* @param data [uint8_t*] 文件名指针
|
131
|
+
* @param n [size_t] 文件名长度
|
132
|
+
* @param magickey [uint32_t] 解密密钥
|
133
|
+
*
|
134
|
+
* @return [void]
|
135
|
+
*/
|
136
|
+
static void decrypt_file_name(uint8_t* data, const size_t n, const uint32_t magickey)
|
137
|
+
{
|
138
|
+
const size_t q = n >> 2;
|
139
|
+
const char r = n & MOD_4_MASK;
|
140
|
+
uint32_t* data_p = (uint32_t*)data;
|
141
|
+
for (; data_p < (uint32_t*)(data + q * sizeof(uint32_t)); ++data_p)
|
142
|
+
*data_p ^= magickey;
|
143
|
+
switch (r)
|
144
|
+
{
|
145
|
+
case 1 : *data_p ^= (magickey & MASK_KEY_1); break;
|
146
|
+
case 2 : *data_p ^= (magickey & MASK_KEY_2); break;
|
147
|
+
case 3 : *data_p ^= (magickey & MASK_KEY_3); break;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
static void decrypt_file_data_scalar(uint8_t* data, const size_t n, uint32_t magickey)
|
152
|
+
{
|
153
|
+
const size_t q = n >> 2;
|
154
|
+
char r = n & MOD_4_MASK;
|
155
|
+
uint32_t* data_p = (uint32_t*)data;
|
156
|
+
for (; data_p < (uint32_t*)(data + q * sizeof(uint32_t)); ++data_p)
|
157
|
+
{
|
158
|
+
*data_p ^= magickey;
|
159
|
+
magickey = magickey * 7 + 3;
|
160
|
+
}
|
161
|
+
switch (r)
|
162
|
+
{
|
163
|
+
case 1 : *data_p ^= (magickey & MASK_KEY_1); break;
|
164
|
+
case 2 : *data_p ^= (magickey & MASK_KEY_2); break;
|
165
|
+
case 3 : *data_p ^= (magickey & MASK_KEY_3); break;
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
#ifdef __AVX512F__
|
170
|
+
static void decrypt_file_data_avx512(uint8_t* data, const size_t n, uint32_t magickey)
|
171
|
+
{
|
172
|
+
const size_t q = n >> 6; // 64 字节为一组
|
173
|
+
const char r = n & MOD_64_MASK;
|
174
|
+
__m512i* data_p = (__m512i*)data;
|
175
|
+
for (; data_p < (__m512i*)(data + q * sizeof(__m512i)); ++data_p)
|
176
|
+
{
|
177
|
+
// 每次循环以当前 magickey 生成 16 个连续密钥 k0..k15
|
178
|
+
uint32_t ks[16];
|
179
|
+
ks[0] = magickey;
|
180
|
+
for (int i = 1; i < 16; ++i)
|
181
|
+
ks[i] = ks[i - 1] * 7 + 3;
|
182
|
+
// 下一轮初始 magickey
|
183
|
+
magickey = ks[15] * 7 + 3;
|
184
|
+
|
185
|
+
__m512i v_magickey = _mm512_setr_epi32(
|
186
|
+
ks[0], ks[1], ks[2], ks[3],
|
187
|
+
ks[4], ks[5], ks[6], ks[7],
|
188
|
+
ks[8], ks[9], ks[10], ks[11],
|
189
|
+
ks[12], ks[13], ks[14], ks[15]);
|
190
|
+
// 加载 64 字节
|
191
|
+
__m512i tmp = _mm512_loadu_si512(data_p);
|
192
|
+
// 并行异或
|
193
|
+
tmp = _mm512_xor_si512(tmp, v_magickey);
|
194
|
+
// 写回
|
195
|
+
_mm512_storeu_si512(data_p, tmp);
|
196
|
+
}
|
197
|
+
// 处理剩余 r 字节
|
198
|
+
decrypt_file_data_scalar((uint8_t*)data_p, r, magickey);
|
199
|
+
}
|
200
|
+
#elifdef __AVX2__
|
201
|
+
static void decrypt_file_data_avx2(uint8_t* data, const size_t n, uint32_t magickey)
|
202
|
+
{
|
203
|
+
const size_t q = n >> 5; // 32 字节为一组
|
204
|
+
const char r = n & MOD_32_MASK;
|
205
|
+
__m256i* data_p = (__m256i*)data;
|
206
|
+
for (; data_p < (__m256i*)(data + q * sizeof(__m256i)); ++data_p)
|
207
|
+
{
|
208
|
+
// 每次循环以当前 magickey 生成 8 个连续密钥 k0..k7
|
209
|
+
uint32_t ks[8];
|
210
|
+
ks[0] = magickey;
|
211
|
+
for (int i = 1; i < 8; ++i)
|
212
|
+
ks[i] = ks[i - 1] * 7 + 3;
|
213
|
+
// 下一轮初始 magickey
|
214
|
+
magickey = ks[7] * 7 + 3;
|
215
|
+
|
216
|
+
__m256i v_magickey = _mm256_setr_epi32(
|
217
|
+
ks[0], ks[1], ks[2], ks[3],
|
218
|
+
ks[4], ks[5], ks[6], ks[7]);
|
219
|
+
// 加载 32 字节
|
220
|
+
__m256i tmp = _mm256_loadu_si256(data_p);
|
221
|
+
// 并行异或
|
222
|
+
tmp = _mm256_xor_si256(tmp, v_magickey);
|
223
|
+
// 写回
|
224
|
+
_mm256_storeu_si256(data_p, tmp);
|
225
|
+
}
|
226
|
+
// 处理剩余 r 字节
|
227
|
+
decrypt_file_data_scalar((uint8_t*)data_p, r, magickey);
|
228
|
+
}
|
229
|
+
#endif
|
230
|
+
|
231
|
+
/*
|
232
|
+
* 解码数据段
|
233
|
+
*
|
234
|
+
* @param data [uint8_t*] 数据段指针
|
235
|
+
* @param n [size_t] 数据段长度
|
236
|
+
* @param magickey [uint32_t] 解密密钥
|
237
|
+
*
|
238
|
+
* @return [void]
|
239
|
+
*/
|
240
|
+
inline static void decrypt_file_data_dispatch(uint8_t* data, size_t n, uint32_t key)
|
241
|
+
{
|
242
|
+
#ifdef __AVX512F__
|
243
|
+
decrypt_file_data_avx512(data, n, key);
|
244
|
+
#elifdef __AVX2__
|
245
|
+
decrypt_file_data_avx2(data, n, key);
|
246
|
+
#else
|
247
|
+
decrypt_file_data_scalar(data, n, key);
|
248
|
+
#endif
|
249
|
+
}
|
250
|
+
|
251
|
+
/*
|
252
|
+
* 释放 mmap 创建的内存映射
|
253
|
+
*
|
254
|
+
* @param addr [void*] 内存映射的地址
|
255
|
+
* @param len [size_t] 内存映射的大小
|
256
|
+
*
|
257
|
+
* @return [int] 0 munmap成功, -1 munmap失败
|
258
|
+
*/
|
259
|
+
inline static int munmap_wrapper(void* addr, size_t len)
|
260
|
+
{
|
261
|
+
#ifdef _WIN32
|
262
|
+
return UnmapViewOfFile(addr) ? 0 : -1;
|
263
|
+
#elif defined(__linux__)
|
264
|
+
return munmap(addr, len);
|
265
|
+
#endif
|
266
|
+
}
|
267
|
+
|
268
|
+
/* 创建 mmap 内存映射
|
269
|
+
*
|
270
|
+
* @param fd [int] 文件描述符
|
271
|
+
* @param len [size_t] 内存映射的大小
|
272
|
+
*
|
273
|
+
* @return [void*] MAP_FAILED 失败
|
274
|
+
*/
|
275
|
+
inline static void* mmap_wapper(int fd, size_t len)
|
276
|
+
{
|
277
|
+
#ifdef _WIN32
|
278
|
+
HANDLE hFile = (HANDLE)_get_osfhandle(fd);
|
279
|
+
if (unlikely(hFile == INVALID_HANDLE_VALUE)) return MAP_FAILED;
|
280
|
+
|
281
|
+
HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
|
282
|
+
if (unlikely(!hMap)) return MAP_FAILED;
|
283
|
+
|
284
|
+
void* mapped = MapViewOfFile(hMap, FILE_MAP_COPY, 0, 0, 0);
|
285
|
+
CloseHandle(hMap);
|
286
|
+
|
287
|
+
return likely(mapped) ? mapped : MAP_FAILED;
|
288
|
+
#elifdef __linux__
|
289
|
+
return mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
290
|
+
#endif
|
291
|
+
}
|
292
|
+
|
293
|
+
/*
|
294
|
+
* 获取文件描述符
|
295
|
+
*
|
296
|
+
* @param dir [const char*] UTF-8 字符串文件路径
|
297
|
+
*
|
298
|
+
* @return [int] -1 open失败, -2 utf8towc失败
|
299
|
+
*/
|
300
|
+
inline static int open_wapper(const char* dir)
|
301
|
+
{
|
302
|
+
#ifdef _WIN32
|
303
|
+
if (unlikely(utf8towc(dir, strlen(dir)) == -1))
|
304
|
+
return -2;
|
305
|
+
return _wopen(filename_arr, O_RDONLY | O_BINARY);
|
306
|
+
#elifdef __linux__
|
307
|
+
return open(dir, O_RDONLY);
|
308
|
+
#endif
|
309
|
+
}
|
310
|
+
|
311
|
+
/*
|
312
|
+
* 创建文件夹
|
313
|
+
*
|
314
|
+
* @param dir UTF-8 字符串文件路径
|
315
|
+
*
|
316
|
+
* @return [int] -1 mkdir失败, -2 utf8towc失败
|
317
|
+
*/
|
318
|
+
inline static int mkdir_wapper(const char* dir)
|
319
|
+
{
|
320
|
+
#ifdef _WIN32
|
321
|
+
if (unlikely(utf8towc(dir, strlen(dir)) == -1))
|
322
|
+
return -2;
|
323
|
+
return _wmkdir(filename_arr);
|
324
|
+
#elifdef __linux__
|
325
|
+
return mkdir(dir, 0755);
|
326
|
+
#endif
|
327
|
+
}
|
328
|
+
|
329
|
+
/*
|
330
|
+
* 解码 Game.rgss3a 文件中的 rvdata2 数据
|
331
|
+
*
|
332
|
+
* @param target_path Game.rgss3a 文件路径
|
333
|
+
* @param output_dir 输出目录
|
334
|
+
* @param verbose 是否输出详细信息
|
335
|
+
* @raise [RGSS3AFileError] 未知的 RGSS3A 文件加密类型
|
336
|
+
* @raise [SystemCallError] 系统调用失败
|
337
|
+
*
|
338
|
+
* @return [void]
|
339
|
+
*/
|
340
|
+
static VALUE r3exs_rgss3a_rvdata2(VALUE self, VALUE target_path, VALUE output_dir, const VALUE verbose)
|
341
|
+
{
|
342
|
+
bool verbose_bool = RTEST(verbose);
|
343
|
+
// 首先将 Ruby 的 VALUE 转换为 C 的字符串
|
344
|
+
char* target_path_C = StringValueCStr(target_path);
|
345
|
+
char* output_dir_C = StringValueCStr(output_dir);
|
346
|
+
|
347
|
+
// 创建 output_dir
|
348
|
+
int result = mkdir_wapper(output_dir_C);
|
349
|
+
#ifdef _WIN32
|
350
|
+
if (unlikely(result == -2))
|
351
|
+
{
|
352
|
+
free(filename_arr);
|
353
|
+
rb_sys_fail("Failed to convert UTF-8 to wchar");
|
354
|
+
}
|
355
|
+
if (unlikely(result == -1 && errno != EEXIST))
|
356
|
+
{
|
357
|
+
free(filename_arr);
|
358
|
+
rb_sys_fail(output_dir_C);
|
359
|
+
}
|
360
|
+
#elifdef __linux__
|
361
|
+
if (unlikely(result == -1 && errno != EEXIST))
|
362
|
+
rb_sys_fail(output_dir_C);
|
363
|
+
#endif
|
364
|
+
|
365
|
+
// 获取文件描述符
|
366
|
+
int fd = open_wapper(target_path_C);
|
367
|
+
#ifdef _WIN32
|
368
|
+
if (unlikely(fd == -2))
|
369
|
+
{
|
370
|
+
free(filename_arr);
|
371
|
+
rb_sys_fail("Failed to convert UTF-8 to wchar");
|
372
|
+
}
|
373
|
+
if (unlikely(fd == -1))
|
374
|
+
{
|
375
|
+
free(filename_arr);
|
376
|
+
rb_sys_fail(target_path_C);
|
377
|
+
}
|
378
|
+
#elifdef __linux__
|
379
|
+
if (unlikely(fd == -1))
|
380
|
+
rb_sys_fail(target_path_C);
|
381
|
+
#endif
|
382
|
+
|
383
|
+
// 获取文件大小
|
384
|
+
struct stat sb;
|
385
|
+
if (unlikely(fstat(fd, &sb) == -1))
|
386
|
+
{
|
387
|
+
#ifdef _WIN32
|
388
|
+
free(filename_arr);
|
389
|
+
#endif
|
390
|
+
close(fd);
|
391
|
+
rb_sys_fail(target_path_C);
|
392
|
+
}
|
393
|
+
off_t Rgss3a_file_size = sb.st_size;
|
394
|
+
|
395
|
+
// 使用 mmap 映射文件
|
396
|
+
uint8_t* Rgss3a_data = mmap_wapper(fd, Rgss3a_file_size);
|
397
|
+
if (unlikely(Rgss3a_data == MAP_FAILED))
|
398
|
+
{
|
399
|
+
#ifdef _WIN32
|
400
|
+
free(filename_arr);
|
401
|
+
#endif
|
402
|
+
close(fd);
|
403
|
+
rb_sys_fail("Failed to mmap file");
|
404
|
+
}
|
405
|
+
|
406
|
+
// 关闭文件描述符
|
407
|
+
close(fd);
|
408
|
+
#ifdef _WIN32
|
409
|
+
if (unlikely(verbose_bool))
|
410
|
+
printf("\e[2K\e[32mMmapped \e[0m%ls\n", filename_arr);
|
411
|
+
#elifdef __linux__
|
412
|
+
if (unlikely(verbose_bool))
|
413
|
+
printf("\e[2K\e[32mMmapped \e[0m%s\n", target_path_C);
|
414
|
+
#endif
|
415
|
+
|
416
|
+
// 设置文件指针索引
|
417
|
+
uint8_t* Rgss3a_p = Rgss3a_data;
|
418
|
+
|
419
|
+
// 判断加密类型
|
420
|
+
enum RGSSAD_DECRYPT_TYPE decrypt_type;
|
421
|
+
if (likely(memcmp(Rgss3a_p, "RGSSAD\x00\x03", 8) == 0))
|
422
|
+
{
|
423
|
+
decrypt_type = RGSSAD;
|
424
|
+
}
|
425
|
+
else if (memcmp(Rgss3a_p, "Fux2Pack", 8) == 0)
|
426
|
+
{
|
427
|
+
decrypt_type = Fux2Pack2;
|
428
|
+
}
|
429
|
+
else
|
430
|
+
{
|
431
|
+
#ifdef _WIN32
|
432
|
+
free(filename_arr);
|
433
|
+
#endif
|
434
|
+
// 不支持的 RGSS3A 加密格式
|
435
|
+
munmap_wrapper(Rgss3a_data, Rgss3a_file_size);
|
436
|
+
rb_raise(r3exs_RGSS3AFileError_class, "Unknown RGSS3A file decrypted type: %+" PRIsVALUE, target_path);
|
437
|
+
}
|
438
|
+
|
439
|
+
// 读取 MagicKey
|
440
|
+
Rgss3a_p += 8;
|
441
|
+
uint32_t magickey;
|
442
|
+
switch (decrypt_type)
|
443
|
+
{
|
444
|
+
case RGSSAD :
|
445
|
+
magickey = *(uint32_t*)Rgss3a_p * 9 + 3;
|
446
|
+
break;
|
447
|
+
case Fux2Pack2 :
|
448
|
+
magickey = *(uint32_t*)Rgss3a_p;
|
449
|
+
break;
|
450
|
+
}
|
451
|
+
Rgss3a_p += 4;
|
452
|
+
|
453
|
+
while (true)
|
454
|
+
{
|
455
|
+
// 读取数据段偏移量
|
456
|
+
uint32_t data_offset = *(uint32_t*)Rgss3a_p ^ magickey;
|
457
|
+
if (unlikely(data_offset == 0))
|
458
|
+
break;
|
459
|
+
Rgss3a_p += 4;
|
460
|
+
|
461
|
+
// 读取数据段长度
|
462
|
+
uint32_t data_size = *(uint32_t*)Rgss3a_p ^ magickey;
|
463
|
+
Rgss3a_p += 4;
|
464
|
+
|
465
|
+
// 读取数据段 magicKey
|
466
|
+
uint32_t data_magickey = *(uint32_t*)Rgss3a_p ^ magickey;
|
467
|
+
Rgss3a_p += 4;
|
468
|
+
|
469
|
+
// 读取文件名长度
|
470
|
+
uint32_t filename_size = *(uint32_t*)Rgss3a_p ^ magickey;
|
471
|
+
Rgss3a_p += 4;
|
472
|
+
|
473
|
+
// 解码文件名
|
474
|
+
if (unlikely(verbose_bool))
|
475
|
+
printf("\e[34mDecrypting DataName...\r");
|
476
|
+
decrypt_file_name(Rgss3a_p, filename_size, magickey);
|
477
|
+
|
478
|
+
// 将文件名中的 '\\' 替换为 '/'
|
479
|
+
for (uint32_t i = 0; i < filename_size; ++i)
|
480
|
+
{
|
481
|
+
if (Rgss3a_p[i] == '\\')
|
482
|
+
Rgss3a_p[i] = '/';
|
483
|
+
}
|
484
|
+
|
485
|
+
// 读取文件名
|
486
|
+
#ifdef _WIN32
|
487
|
+
if (unlikely(utf8towc((char*)Rgss3a_p, filename_size) == -1))
|
488
|
+
#elifdef __linux__
|
489
|
+
if (unlikely(utf8tomb((char*)Rgss3a_p, filename_size) == -1))
|
490
|
+
#endif
|
491
|
+
{
|
492
|
+
free(filename_arr);
|
493
|
+
munmap_wrapper(Rgss3a_data, Rgss3a_file_size);
|
494
|
+
#ifdef _WIN32
|
495
|
+
rb_sys_fail("Failed to convert UTF-8 to wchar");
|
496
|
+
#elifdef __linux__
|
497
|
+
rb_sys_fail("Failed to convert UTF-8 to char");
|
498
|
+
#endif
|
499
|
+
}
|
500
|
+
|
501
|
+
// 解码数据段
|
502
|
+
#ifdef _WIN32
|
503
|
+
if (unlikely(verbose_bool))
|
504
|
+
printf("\e[2K\e[32mDecrypting \e[0m%ls \e[0mOffset: \e[35m%u \e[0mSize: \e[35m%u \e[0mMagicKey: \e[35m%u\e[0m...\r", filename_arr, data_offset, data_size, data_magickey);
|
505
|
+
#elifdef __linux__
|
506
|
+
if (unlikely(verbose_bool))
|
507
|
+
printf("\e[2K\e[32mDecrypting \e[0m%s \e[0mOffset: \e[35m%u \e[0mSize: \e[35m%u \e[0mMagicKey: \e[35m%u\e[0m...\r", filename_arr, data_offset, data_size, data_magickey);
|
508
|
+
#endif
|
509
|
+
decrypt_file_data_dispatch(Rgss3a_data + data_offset, data_size, data_magickey);
|
510
|
+
#ifdef _WIN32
|
511
|
+
if (unlikely(verbose_bool))
|
512
|
+
printf("\e[2K\e[32mDecrypted \e[0m%ls \e[0mOffset: \e[35m%u \e[0mSize: \e[35m%u \e[0mMagicKey: \e[35m%u\e[0m\n", filename_arr, data_offset, data_size, data_magickey);
|
513
|
+
#elifdef __linux__
|
514
|
+
if (unlikely(verbose_bool))
|
515
|
+
printf("\e[2K\e[32mDecrypted \e[0m%s \e[0mOffset: \e[35m%u \e[0mSize: \e[35m%u \e[0mMagicKey: \e[35m%u\e[0m\n", filename_arr, data_offset, data_size, data_magickey);
|
516
|
+
#endif
|
517
|
+
|
518
|
+
// 写入解密后的数据到文件
|
519
|
+
// 先将 output_dir 和文件名拼接得到 output_full_path
|
520
|
+
// 再通过 File.dirname(output_full_path) 获得 output_full_dir
|
521
|
+
// 最后通过 FileUtils.mkdir_p(output_full_dir) 递归创建目录
|
522
|
+
VALUE output_full_path = rb_funcall(r3exs_File_module, r3exs_join_id, 2, output_dir, rb_utf8_str_new((char*)Rgss3a_p, filename_size));
|
523
|
+
VALUE output_full_dir = rb_funcall(r3exs_File_module, r3exs_dirname_id, 1, output_full_path);
|
524
|
+
char* output_full_path_C = StringValueCStr(output_full_path);
|
525
|
+
// 创建目录
|
526
|
+
rb_funcall(r3exs_FileUtils_module, r3exs_mkdir_p_id, 1, output_full_dir);
|
527
|
+
|
528
|
+
#ifdef _WIN32
|
529
|
+
if (unlikely(utf8towc(output_full_path_C, strlen(output_full_path_C)) == -1))
|
530
|
+
{
|
531
|
+
free(filename_arr);
|
532
|
+
munmap_wrapper(Rgss3a_data, Rgss3a_file_size);
|
533
|
+
rb_sys_fail("Failed to convert UTF-8 to wchar");
|
534
|
+
}
|
535
|
+
if (unlikely(verbose_bool))
|
536
|
+
printf("\e[34mWriting \e[0m%ls...\r", filename_arr);
|
537
|
+
FILE* output_file = _wfopen(filename_arr, L"wb");
|
538
|
+
#elifdef __linux__
|
539
|
+
if (unlikely(verbose_bool))
|
540
|
+
printf("\e[34mWriting \e[0m%s...\r", output_full_path_C);
|
541
|
+
FILE* output_file = fopen(output_full_path_C, "wb");
|
542
|
+
#endif
|
543
|
+
if (unlikely(!output_file))
|
544
|
+
{
|
545
|
+
free(filename_arr);
|
546
|
+
munmap_wrapper(Rgss3a_data, Rgss3a_file_size);
|
547
|
+
rb_sys_fail(output_full_path_C);
|
548
|
+
}
|
549
|
+
|
550
|
+
// 写入文件
|
551
|
+
size_t result = fwrite(Rgss3a_data + data_offset, sizeof(uint8_t), data_size, output_file);
|
552
|
+
if (unlikely(result < data_size))
|
553
|
+
{
|
554
|
+
free(filename_arr);
|
555
|
+
munmap_wrapper(Rgss3a_data, Rgss3a_file_size);
|
556
|
+
fclose(output_file);
|
557
|
+
rb_sys_fail(output_full_path_C);
|
558
|
+
}
|
559
|
+
if (unlikely(fclose(output_file) == EOF))
|
560
|
+
rb_sys_fail(output_full_path_C);
|
561
|
+
if (unlikely(verbose_bool))
|
562
|
+
#ifdef _WIN32
|
563
|
+
printf("\e[2K\e[32mWrited \e[0m%ls\n", filename_arr);
|
564
|
+
#elifdef __linux__
|
565
|
+
printf("\e[2K\e[32mWrited \e[0m%s\n", output_full_path_C);
|
566
|
+
#endif
|
567
|
+
Rgss3a_p += filename_size;
|
568
|
+
}
|
569
|
+
|
570
|
+
free(filename_arr);
|
571
|
+
munmap_wrapper(Rgss3a_data, Rgss3a_file_size);
|
572
|
+
return Qnil;
|
573
|
+
}
|
574
|
+
|
575
|
+
/*
|
576
|
+
* 初始化 R3EXS 模块
|
577
|
+
*
|
578
|
+
* @return [void]
|
579
|
+
*/
|
580
|
+
void Init_R3EXS()
|
581
|
+
{
|
582
|
+
// 定义 R3EXS 模块
|
583
|
+
R3EXS = rb_define_module("R3EXS");
|
584
|
+
// 定义 ID
|
585
|
+
r3exs_RGSS3AFileError_id = rb_intern("RGSS3AFileError");
|
586
|
+
r3exs_File_id = rb_intern("File");
|
587
|
+
r3exs_FileUtils_id = rb_intern("FileUtils");
|
588
|
+
r3exs_Dir_id = rb_intern("Dir");
|
589
|
+
r3exs_join_id = rb_intern("join");
|
590
|
+
r3exs_dirname_id = rb_intern("dirname");
|
591
|
+
r3exs_exist_id = rb_intern("exist?");
|
592
|
+
r3exs_mkdir_p_id = rb_intern("mkdir_p");
|
593
|
+
|
594
|
+
// 定义模块和类
|
595
|
+
r3exs_RGSS3AFileError_class = rb_const_get(R3EXS, r3exs_RGSS3AFileError_id);
|
596
|
+
r3exs_File_module = rb_const_get(rb_cObject, r3exs_File_id);
|
597
|
+
r3exs_FileUtils_module = rb_const_get(rb_cObject, r3exs_FileUtils_id);
|
598
|
+
r3exs_Dir_module = rb_const_get(rb_cObject, r3exs_Dir_id);
|
599
|
+
// 定义 rgss3a_rvdata2 方法
|
600
|
+
rb_define_singleton_method(R3EXS, "rgss3a_rvdata2", r3exs_rgss3a_rvdata2, 3);
|
601
|
+
}
|