R3EXS 1.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,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 == 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
+ }