R3EXS 1.0.2 → 1.0.3

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