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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a3bc4fe4ef14d4c9751004acf9d58bd8d5b8f3c19b6421ec22b1ad5be6852c9
4
- data.tar.gz: d84184c23083d1bb0e270ce2bbdf91bedd880ed014a61ee6babd0d1e05f285fc
3
+ metadata.gz: a0e5f2730229700e811394349c95a58f8c882b7a10468fd2f707281313583397
4
+ data.tar.gz: 05a6b0319f97f1594c79eba492a68695abe64b15dcf928b2a1f56693ede0862e
5
5
  SHA512:
6
- metadata.gz: 211d024eddfac0d4ad0e7a62260c0f20ae5992470f3d3fdcd99c0ac5a67bac1d6f36673c2f87f3c54f4014d370d1fa9469a1bbef19cbc03b7fadc520e8ca3c94
7
- data.tar.gz: 7b5bc178dea86262f3d5b273f2130fb97b44ffc9ca2180dcc7c5edb1849bdab187a3983dc0f5490aa336ca71974df668a2131c8ff7c2388274934041288cdbf0
6
+ metadata.gz: dd9eb1770019cd97f44c5811f5f2bd7d178304333b4735d521730f8d263e5c514ec857bea6a47f2ed1f2df6f230779e986691be0d18eed5f78025d54b2905adb
7
+ data.tar.gz: cfb6ef690a70f29779d941baba5e368c33cb934d0ecb3ee6bab02bf0eaba70eef52057123670458809912ffcbb41b16ad054b8d9176510934441d3d7d7bddc7c
data/.yardopts CHANGED
@@ -1,12 +1,12 @@
1
1
  --title R3EXS
2
2
  --readme README_EN.md
3
3
  --markup markdown
4
+ --markup-provider redcarpet
4
5
  --charset utf-8
5
6
  lib/**/*.rb
6
7
  ext/**/*.c
7
- --list-undoc
8
8
  -
9
9
  LICENSE
10
10
  README.md
11
11
  README_EN.md
12
- CHANGELOG.md
12
+ CHANGELOG.md
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # [R3EXS]
2
2
 
3
+ ## [1.1.1] - 2025-07-21
4
+
5
+ - rgss3a_rvdata2 方法添加 AVX 优化
6
+ - rgss3a_rvdata2 方法使用 mmap 代替 fopen
7
+
3
8
  ## [1.1.0] - 2025-04-09
4
9
 
5
10
  - 添加了递归搜索目标目录下的 \*.rvdata2 文件的功能
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
+ }
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('R3EXS/R3EXS')