R3EXS 1.0.5 → 1.1.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.
@@ -1,616 +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 == 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
- }
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
+ static 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
+ static 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
+ static 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
+ static 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
+ static 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
+ static 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
+ static void mkdir_error_handler(const char* dir)
186
+ {
187
+ rb_sys_fail(dir);
188
+ }
189
+
190
+ /*
191
+ * 处理 malloc 错误
192
+ *
193
+ * @return [void]
194
+ */
195
+ static 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
+ static 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
+ static 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
+ static 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
+ static 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
+ static 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
+ static 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
+ static 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
+ }