底部有编译好的cui插件,用于crass-0.4.14.1
源头:(同人ゲーム) [121214][DMM053990][はいぺりよん] 3M~Marionettes manipulate the marionette #define DL版 (files).rar
解开ysbin文件之后,里面有很多的ybn小文件
用OD加载游戏程序,查看文本,发现一个很可疑的字符串(应该很接近读取.ybn文件的地方)
// 读取某个子文件的入口在这里
004433D3 |. 68 8C397E00 push 007E398C ; ASCII "ysbin\yst%05d.ybn"
ysbin.ypf下面压缩了的内
ED868C9D9691A3868C8BCFCFCFC9CFD1869D910001480000003E000000724D08
程序会先尝试去读取真实目录下的文件ysbin\yst00060.ybn,发现没有,会去找压缩文件的子文件索引表;
当找到后,会打开(CreateFile)这个压缩文件(ysbin.ypf),并通过文件索引表得到指定文件的偏移与大小;
然后定位要读取的数据在压缩文件的偏移位置(SetFilePointer),再读取(ReadFile)该文件数据。
===============
Breakpoints
地址 模块 激活 反汇编 注释
004433D3 3M_d_通? 始终 push 007E398C
00443687 3M_d_通? 始终 retn
004524CE 3M_d_通? 已禁止 call 0044F2AC
004524D3 3M_d_通? 已禁止 movsx ecx, al
75769AF0 KernelBa 始终 mov edi, edi CreateFileW
7576AB4E KernelBa 始终 push 18 ReadFile
7576B1B1 KernelBa 始终 mov edi, edi SetFilePointerEx
7576BDD7 KernelBa 始终 mov edi, edi SetFilePointer
75781631 KernelBa 始终 mov edi, edi SetEndOfFile
下上面的的这些断点;
先将Kernel的断点全部都禁用,一直到004433D3位置的断点到了
开启Kernel的断点
接下来Shift+F9可以很清晰的看到CreateFileW->SetFilePointer->ReadFile这么一个过程
中间会读取文件各种部位,先不管
...
直到读取一个压缩子文件到内存之后(通过ReadFile的字节数或者SetFilePointer的偏移位置来确定读取的是不是压缩子文件)
在数据窗口Ctrl+G,定位到压缩子文件的内存内的第一个字节(调用ReadFile时,会传入目标内存地址,在数据窗口定位这个地址就OK了)
在第一个字节上,下硬件访问断点(4个字节,目的是想看看程序在哪里会去使用文件里面读出来的数据)
...
断了下来,发现有zlib字符串的函数!!!
try zip解码
在 ysbin.ypf:84D72 位置,是子文件 60.ybn 的开始位置
文件开始数据:
78018B0C0E71BAC9C8C0C0C4C0C0C001...
自己用了一个zlib库,解出来的结果
0x00500040 59 53 54 42 d9 01 00 00 02 00 00 00 08 00 00 00
0x00500050 0c 00 00 00 0c 00 00 00 08 00 00 00 00 00 00 00
0x00500060 cf 6e ac 96 df 6f ac 96 d3 6f af 96 df 6f ac 96
0x00500070 d3 6f ac 96 9e 66 ac b4 b6 1c 82 d3 81 26 ff b4
0x00500080 dd 6f ac 96 c2 6f ac 96
在OD里面看到解出来的结果
01CDFF30 59 53 54 42 D9 01 00 00 02 00 00 00 08 00 00 00 YSTB?........
01CDFF40 0C 00 00 00 0C 00 00 00 08 00 00 00 00 00 00 00 ...............
01CDFF50 CF 6E AC 96 DF 6F AC 96 D3 6F AF 96 DF 6F AC 96 蟦瑬遫瑬觨瘱遫瑬
01CDFF60 D3 6F AC 96 9E 66 AC B4 B6 1C 82 D3 81 26 FF B4 觨瑬瀎?傆??
01CDFF70 DD 6F AC 96 C2 6F AC 96 輔瑬耾瑬....
调用完下面一行后,当前堆栈指向的地址就是解压出来的数据的开始地址
004525B0 |. E8 3FA80200 |call <3M.uncompress>
自己与OD解出来的结果是一样的,那就是zlib库,并且是1.2.3版本的
但是!!!发现在OD后面对解出来的数据还做了一些处理,下面是处理时某一过程复制下来的。。。不是最终的数据
00000000 59 53 54 42 D9 01 00 00 02 00 00 00 DB 6F AC 96 YSTB?......踥瑬
00000010 DF 6F AC 96 0C 00 00 00 08 00 00 00 00 00 00 00 遫瑬............
00000020 1C 01 AC 96 DF 6F AC 96 D3 6F AF 96 DF 6F AC 96 ..瑬遫瑬觨瘱遫瑬
00000030 D3 6F AC 96 9E 66 AC B4 B6 1C 82 D3 81 26 FF B4 觨瑬瀎?傆.&?
00000040 DD 6F AC 96 C2 6F AC 96 輔瑬耾瑬
既然解包出来的数据还是做过处理的,那么我们就知道在unzip后,需要对数据再做些处理。
============================================================
在写解包工具前,希望先在程序里面能看到汉字,这样能证明,只要文件处理OK,那么汉化就没有问题
下CreateFontIndirect、CreateFont两个函数的断点
断在了CreateFontIndirect函数里面,查看一下调用堆栈,找到游戏程序空间调用的位置
00437D3C FF15 38805600 call dword ptr [<&GDI32.CreateFontInd>; gdi32.CreateFontIndirectA
在这里整了一会儿,发现游戏程序好像是有多个LOGFONT结构,每次调用CreateFontIndirect时还会修改某个LOGFONT结构,再将这个结构传入CreateFontIndirect
为了整中文,我们得搞个汉字字体呀,那就弄个"宋体"吧,还得将charset改为86h(GB2312)
LOGFONT结构数据
.游戏程序里面当前LOGFONT的数据
0087AF58 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 H...............
0087AF68 BC 02 00 00 00 00 00 80 00 00 00 00 53 79 73 74 ?.....€....Syst
0087AF78 65 6D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 em..............
0087AF88 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0087AF98 AE 08 79 05 00 00 00 8C 00 00 00 00 00 00 00 00 ?y...?.......
0087AFA8 00 00 00 00 00 00 00 00 90 01 00 00 00 00 00 80 ........?.....€
.用VC整了个LOGFONT,填上"宋体",并将codepage改为86
48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bc 02 00 00 00 00 00 86 00 00 00 00 8b 5b 53 4f
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ae 08 79 05 00 00 00 8c 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 90 01 00 00
就用这串字来做测试:
调用完后
16进制码:
B5F7D3C3CDEABAF3
我们会发现,本来的日文显示成乱码了(证明LOGFONT改成功了);
可是我们的汉字也是乱码,这就不对了!!!
看样子,还有很多任务
重新回头来看看,其实都可以不用去整那个“宋体”的,因为原来的LOGFONT用的是“System”,在中文系统上面,也就是“宋体”了。不过codepage还是要整。
继续,将<3M_d_通常版.exe>里面的代码替换下面的这段代码,目的为了:
1.修正CharSet 0x86(汉字GB2312)
2.去掉字体复制代码(默认就是"System",正是我们需要的,就不用再去每次复制了),为了给第1步的代码弄点空间来运行
00437D14 8B15 3CC55A00 mov edx, dword ptr [5AC53C]
00437D1A 8B3495 3CD87D00 mov esi, dword ptr [edx*4+7DD83C]
00437D21 83C6 17 add esi, 17
00437D24 C606 86 mov byte ptr [esi], 86
00437D27 90 nop
00437D28 90 nop
00437D29 90 nop
00437D2A 90 nop
00437D2B 90 nop
00437D2C 90 nop
00437D2D 90 nop
00437D2E 90 nop
00437D2F 90 nop
00437D30 A1 3CC55A00 mov eax, dword ptr [5AC53C]
00437D35 FF3485 3CD87D00 push dword ptr [eax*4+7DD83C]
00437D3C FF15 38805600 call dword ptr [<&GDI32.CreateFontInd>; gdi32.CreateFontIndirectA
这样程序运行起来就直接是86h的了,也就是日文直接是乱码了,我们还需要去调试为什么中文会显示不出来
一般显示文字会使用DrawText、TextOut函数,就下它们的断点吧
119.ybn,这个文件将会是第一个能写出文字的脚本,我们就用它来调试吧
119.ybn:7e30h 第一个字符串的位置
在TextOutA的函数有中断,每次都只有一个字符(CHAR/WCHAR)
一个奇怪的问题是,我们写了中文之后,会被拆分了,为什么呢?
============================
调用完后
B5F7D3C3CDEABAF3
============================
/////////////////
又需要去下硬件访问断点了
定位119.ybn第一个字符串在内存的开始地址,下硬件访问断点
...
分析
...
结论:
访问文件内的字符串,生成两个数据:
1.字符串复制
2.字符串每个字符占的字节数(1BYTE/2BYTE)
// 这个函数会使用加载了的 字符串,并读取内存
00401780 . 57 push edi
...
0040179A . 8B7C24 20 mov edi, dword ptr ss:[esp+20]
0040179E . 8B7424 24 mov esi, dword ptr ss:[esp+24]
004017A2 . 33C9 xor ecx, ecx
004017A4 . 8B5424 08 mov edx, dword ptr ss:[esp+8]
004017A8 > 0F6F04CE movq mm0, qword ptr ds:[esi+ecx*8] // 这句就是在读字符串
004017AC . 0F7F04CF movq qword ptr ds:[edi+ecx*8], mm0
004017B0 . 41 inc ecx
004017B1 . 3BCA cmp ecx, edx
004017B3 .^ 75 F3 jnz short 3M-chn.004017A8
.字符串复制
01CE6420 B5 F7 D3 C3 CD EA BA F3 82 C8 82 D1 82 AD 90 B6 调用完后側 傃偔惗
01CE6430 94 92 82 A2 8F 5F 94 A7 81 42 82 BB 82 F1 82 C8 敀偄廮敡丅偦傫側
01CE6440 83 8A 83 8A 83 56 83 59 83 80 82 C9 88 EC 82 EA 儕儕僔僘儉偵堨傟
01CE6450 82 BD 8C F5 8C 69 82 F0 81 41 96 6C 82 CD 82 BD 偨岝宨傪丄杔偼偨
01CE6460 82 BE 81 41 95 F0 91 52 82 C6 8C A9 89 BA 82 EB 偩丄曫慠偲尒壓傠
01CE6470 82 B5 82 C4 82 A2 82 BD 81 42 00 00 00 00 00 00 偟偰偄偨丅......
0044A62E > /8B48 3C mov ecx, dword ptr ds:[eax+3C] ; ecx得到字符串的字符字节数
.字符串每个字符占的字节数(1BYTE/2BYTE)
01CE8428 31 32 31 31 32 32 31 32 32 32 32 32 32 32 32 32 1211221222222222 // 看前面的7个字节
01CE8438 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 2222222222222222
01CE8448 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 222222222222222
拿字符串里每个字节去查表(字节的值直接做为表的索引)
两个字节:81h 40h
05846ADB 81 40 81 40 4D 16 00 22 65 73 2E 53 50 2E 46 4F 丂丂M."es.SP.FO
这个表是关键!!!
被用来查是否为双字节的表(只要我们将81h之后的字节全都写成01,就能显示汉字了)
这个表是针对日文的单双字节字符,我们简单的给处理成这样就好了。
0058F0A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F0B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F0F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F120 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 .
0058F130 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
0058F140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0058F180 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
0058F190 01 01 01 01 01 01 01 01 01 01 01 01 01 00 00 00 ...
0058F1A0 00 00 00 00 96 30 07 77 2C 61 0E EE BA 51 09 99 ....?w,a詈Q.?
修改、保存新的exe,再运行,在内存中替换第一句日文为汉字,成功显示!!!
004433F3 |. E8 19ED0000 call 3M.00452111 ; 读取文件,并解压
004433F8 |. 85C0 test eax, eax ; 读取文件,并解压 完成 [esp+94h] 读取数据
cmd: db [esp + 94] 在数据窗口直接定位到解压完的数据开始位置
字符串复制与检测1或2字节功能函数
0044A4EC . 56 push esi
0044A4ED . 53 push ebx
0044A4EE . 83EC 10 sub esp, 10
0044A4F1 . B8 5A000000 mov eax, 5A
0044A4F6 . E8 AE2EFDFF call 3M-chn.0041D3A9
0044A4FB . 0FBEC0 movsx eax, al
0044A4FE . 85C0 test eax, eax
0044A500 . 0F85 50010000 jnz 3M-chn.0044A656
0044A506 . 8B35 64876500 mov esi, dword ptr ds:[658764]
0044A50C . 8B15 40D67D00 mov edx, dword ptr ds:[7DD640]
0044A512 . 8B42 58 mov eax, dword ptr ds:[edx+58]
0044A515 . 8D4C30 FF lea ecx, dword ptr ds:[eax+esi-1]
0044A519 . 81F9 FE1F0000 cmp ecx, 1FFE
0044A51F . 0F8D 22010000 jge 3M-chn.0044A647
0044A525 . 8B1D 80D57D00 mov ebx, dword ptr ds:[7DD580]
0044A52B . 8B9B 20800000 mov ebx, dword ptr ds:[ebx+8020]
0044A531 . 031D 04896500 add ebx, dword ptr ds:[658904]
0044A537 . 8B52 38 mov edx, dword ptr ds:[edx+38]
0044A53A . 03D0 add edx, eax
0044A53C . 56 push esi
0044A53D . 53 push ebx
0044A53E . 52 push edx
0044A53F . FF15 8CC45A00 call near dword ptr ds:[5AC48C] ; 读取字符串
0044A545 . 83C4 0C add esp, 0C
0044A548 . A1 40D67D00 mov eax, dword ptr ds:[7DD640] ; *(DWORD*)(*(DWORD*)7DD640)
0044A54D . 8B50 58 mov edx, dword ptr ds:[eax+58]
0044A550 . 03D6 add edx, esi
0044A552 . 8950 58 mov dword ptr ds:[eax+58], edx
0044A555 . 8B48 38 mov ecx, dword ptr ds:[eax+38]
0044A558 . C6040A 00 mov byte ptr ds:[edx+ecx], 0
0044A55C . 85F6 test esi, esi
0044A55E . 0F8E D9000000 jle 3M-chn.0044A63D
0044A564 . 33C9 xor ecx, ecx
0044A566 . A1 40D67D00 mov eax, dword ptr ds:[7DD640]
0044A56B . 8B50 5C mov edx, dword ptr ds:[eax+5C]
0044A56E . 897424 0C mov dword ptr ss:[esp+C], esi
0044A572 . 897C24 08 mov dword ptr ss:[esp+8], edi
0044A576 . 896C24 04 mov dword ptr ss:[esp+4], ebp
0044A57A . 8BE9 mov ebp, ecx ; 查表,检测是否为单双字节,好像只支持日文
0044A57C > 0FB6741D 00 movzx esi, byte ptr ss:[ebp+ebx] ; 取要判断的字的一个字节
0044A581 . 0FB68E A0F058>movzx ecx, byte ptr ds:[esi+58F0A0]
0044A588 . 0FBEF1 movsx esi, cl ; esi, 将会记录是否32h,31h
0044A58B . 8B78 4C mov edi, dword ptr ds:[eax+4C]
0044A58E . 8B40 28 mov eax, dword ptr ds:[eax+28]
0044A591 . 890497 mov dword ptr ds:[edi+edx*4], eax
0044A594 . A1 40D67D00 mov eax, dword ptr ds:[7DD640]
0044A599 . 8B78 5C mov edi, dword ptr ds:[eax+5C]
0044A59C . 8B50 50 mov edx, dword ptr ds:[eax+50]
0044A59F . 8B40 2C mov eax, dword ptr ds:[eax+2C]
0044A5A2 . 8904BA mov dword ptr ds:[edx+edi*4], eax
0044A5A5 . A1 40D67D00 mov eax, dword ptr ds:[7DD640]
0044A5AA . 8B50 20 mov edx, dword ptr ds:[eax+20]
0044A5AD . 8B78 5C mov edi, dword ptr ds:[eax+5C]
0044A5B0 . 8B40 40 mov eax, dword ptr ds:[eax+40]
0044A5B3 . 66:891478 mov word ptr ds:[eax+edi*2], dx
0044A5B7 . A1 40D67D00 mov eax, dword ptr ds:[7DD640]
0044A5BC . 8B50 24 mov edx, dword ptr ds:[eax+24]
0044A5BF . 8B78 5C mov edi, dword ptr ds:[eax+5C]
0044A5C2 . 8B40 44 mov eax, dword ptr ds:[eax+44]
0044A5C5 . 66:891478 mov word ptr ds:[eax+edi*2], dx
0044A5C9 . A1 40D67D00 mov eax, dword ptr ds:[7DD640]
0044A5CE . 8B78 54 mov edi, dword ptr ds:[eax+54]
0044A5D1 . 8B50 5C mov edx, dword ptr ds:[eax+5C]
0044A5D4 . 0FB640 30 movzx eax, byte ptr ds:[eax+30]
0044A5D8 . 88043A mov byte ptr ds:[edx+edi], al
0044A5DB . 8B15 40D67D00 mov edx, dword ptr ds:[7DD640]
0044A5E1 . 8D46 31 lea eax, dword ptr ds:[esi+31] ; 看ESI有没有1,如果有就是'2',否则就是'1'
0044A5E4 . 8B7A 3C mov edi, dword ptr ds:[edx+3C]
0044A5E7 . 8B52 5C mov edx, dword ptr ds:[edx+5C]
0044A5EA . 88043A mov byte ptr ds:[edx+edi], al ; 写入'2'(32h)
0044A5ED . 8B3D 40D67D00 mov edi, dword ptr ds:[7DD640]
0044A5F3 . 8BCE mov ecx, esi
0044A5F5 . F7D9 neg ecx
0044A5F7 . 83C1 02 add ecx, 2
0044A5FA . 8B47 04 mov eax, dword ptr ds:[edi+4]
0044A5FD . 99 cdq
0044A5FE . F7F9 idiv ecx
0044A600 . 8B4F 5C mov ecx, dword ptr ds:[edi+5C]
0044A603 . 8B57 48 mov edx, dword ptr ds:[edi+48]
0044A606 . 66:89044A mov word ptr ds:[edx+ecx*2], ax
0044A60A . A1 40D67D00 mov eax, dword ptr ds:[7DD640]
0044A60F . 8B50 5C mov edx, dword ptr ds:[eax+5C]
0044A612 . 42 inc edx
0044A613 . 8950 5C mov dword ptr ds:[eax+5C], edx
0044A616 . 8D6C35 01 lea ebp, dword ptr ss:[ebp+esi+1] ; ebp+1+ 1(若双字节)
0044A61A . 8B7424 0C mov esi, dword ptr ss:[esp+C]
0044A61E . 3BEE cmp ebp, esi
0044A620 .^ 0F8C 56FFFFFF jl 3M-chn.0044A57C
0044A626 . 8B7C24 08 mov edi, dword ptr ss:[esp+8]
0044A62A . 8B6C24 04 mov ebp, dword ptr ss:[esp+4]
0044A62E > 8B48 3C mov ecx, dword ptr ds:[eax+3C] ; ecx得到字符串的字符字节数
0044A631 . C6040A 00 mov byte ptr ds:[edx+ecx], 0
0044A635 . 33C0 xor eax, eax
0044A637 . 83C4 10 add esp, 10
0044A63A . 5B pop ebx
0044A63B . 5E pop esi
0044A63C . C3 retn
修改Crass-source\cui\YU-RIS\YU-RIS.cpp,生成新的cui就能解包了
1: typedef struct {
2: s8 magic[4]; // "YSTB"
3: u32 version; // 0.473
4: u32 data1_length_div_4; // 现在我知道它是脚本文件里面的命令(函数)数量(分析了ybn文件后)
5: u32 data1_length;
6: u32 data2_length;
7: u32 data3_length;
8: u32 data4_length;
9: u32 reserved;
10: } ystb473_header_t;
11:
12: static void __ystb_decode_473(ystb473_header_t *ystb, DWORD ystb_length, BYTE *dec_tbl)
13: {
14: BYTE *enc;
15: unsigned int enc_len, i;
16:
17: enc = (BYTE *)(ystb + 1);
18: enc_len = ystb->data1_length;
19: for (i = 0; i < enc_len; i++)
20: enc[i] ^= dec_tbl[i & 3];
21:
22: enc = ((BYTE *)(ystb + 1)) + ystb->data1_length;
23: enc_len = ystb->data2_length;
24: for (i = 0; i < enc_len; i++)
25: enc[i] ^= dec_tbl[i & 3];
26:
27: enc = ((BYTE *)(ystb + 1)) + ystb->data1_length + ystb->data2_length;
28: enc_len = ystb->data3_length;
29: for (i = 0; i < enc_len; i++)
30: enc[i] ^= dec_tbl[i & 3];
31:
32: enc = ((BYTE *)(ystb + 1)) + ystb->data1_length + ystb->data2_length + ystb->data3_length;
33: enc_len = ystb->data4_length;
34: for (i = 0; i < enc_len; i++)
35: enc[i] ^= dec_tbl[i & 3];
36: }
37:
38: static void ystb_decode(ystb_header_t *ystb, DWORD ystb_length,
39: BYTE **ret_data, DWORD *ret_len)
40: {
41: BYTE dec_tbl1[4] = { 0x07, 0xb4, 0x02, 0x4a };
42: BYTE dec_tbl2[4] = { 0xd3, 0x6f, 0xac, 0x96 };
43: BYTE dec_tbl0[4] = { 0x80, 0x80, 0x80, 0x80 };
44:
45: if (ystb->version >= 473)
46: __ystb_decode_473((ystb473_header_t*)ystb, ystb_length, dec_tbl2);
47: else if (ystb->version > 263)
48: __ystb_decode(ystb, ystb_length, dec_tbl2);
49: else if (ystb->version > 224) // 确切的上限边界并不清楚,只不过碰到了这个版本而
50: __ystb_decode(ystb, ystb_length, dec_tbl1);
51: else if (ystb->version > 2)
52: __ystb_decode(ystb, ystb_length, dec_tbl0);
53: }
ybn文件的字符串切割方法
文件分成几个区段
0.文件头
1.函数区
2.参数区
3.字符串区
4.未知区
文件头
固定大小(20h bytes)
-文件类型(4bytes)
-文件版本(4bytes)
-函数数量(4bytes)
-函数区大小(4bytes)
-参数区大小(4bytes)
-字符串区大小(4bytes)
-未知区大小(4bytes)
-未知(4bytes)
函数区
一个函数占byte[4]
0=函数码
1=参数数量(一个参数用0xC个字节来表示,也就是说,当写的是7时,字节数=7*0xC)
2,3=未知
参数区
大小由函数区内每个函数总的参数数量决定。
分割字符串的数据就记录在这里,一般是[long,long,long](未知,字符串字节数量,在字符串区的偏移)
字符串区
脚本里面所有的字符串参数,没有分割符,全部被并在一起。要想将字符串给分割开来,需要通过,函数区与参数区的一些数值进行计算。
有时引擎会很奇怪,把好好的一段字给切成几个段。(可能是全角半角的原因)。
未知区
不知道有什么用,在程序里面,访问它的地方也没有任何意义。
测试了一下直接将下面那段数据用0给填了,脚本正常运行。。。
可以暂时先不管它
============================================
1: typedef struct {
2: BYTE code;
3: BYTE args;
4: BYTE un[2];
5: } ystb473_method_t;
6:
7: typedef struct {
8: long un;
9: long charCount;
10: long charOffset;
11: } ystb473_parameter_t;
12:
13: bool GBK2UNI(const char* pGBK, long nBytesCount, std::wstring& strUNI)
14: {
15: DWORD dwMinSize = MultiByteToWideChar(936, 0, pGBK, nBytesCount, NULL, 0);
16: strUNI.resize(dwMinSize);
17:
18: if (MultiByteToWideChar(936, 0, (LPCSTR)pGBK, nBytesCount, (WCHAR*)strUNI.c_str(), dwMinSize) > 0)
19: {
20: return true;
21: }
22: return false;
23: }
24:
25:
26: bool SJIS2UNI(const char* pSJIS, long nBytesCount, std::wstring& strUNI)
27: {
28: DWORD dwMinSize = MultiByteToWideChar(932, 0, pSJIS, nBytesCount, NULL, 0);
29: strUNI.resize(dwMinSize);
30:
31: if (MultiByteToWideChar(932, 0, (LPCSTR)pSJIS, nBytesCount, (WCHAR*)strUNI.c_str(), dwMinSize) > 0)
32: {
33: return true;
34: }
35: return false;
36: }
37:
38: void ys_script_export_string()
39: {
40: char* filename = "D:/game/3M/ysbin/yst00119.ybn";
41:
42: FILE *fp;
43: fp = fopen(filename,"rb");
44: if (fp == NULL)
45: {
46: printf("cannot open infile\n");
47: return ;
48: }
49:
50: fseek(fp, 0, SEEK_END);
51: int len = ftell(fp);
52: fseek(fp, 0, SEEK_SET);
53: char* filebuf = new char[len+1];
54: fread(filebuf, sizeof(char), len, fp);
55: fclose(fp);
56:
57: std::wstring newfn;
58: if (GBK2UNI(filename, strlen(filename), newfn) == false)
59: {
60: delete filebuf;
61: return;
62: }
63: newfn += L".txt";
64: FILE* pOutFile = _wfopen( newfn.c_str(), L"wt,ccs=UNICODE");
65: if (pOutFile == NULL)
66: {
67: delete filebuf;
68: return;
69: }
70:
71: ystb473_header_t headerX;
72: char* curP = filebuf;
73: memcpy(&headerX, curP, sizeof(ystb473_header_t));
74: curP += sizeof(ystb473_header_t);
75:
76: ystb473_method_t* pMethods = (ystb473_method_t*)curP;
77: ystb473_parameter_t* pParameters = (ystb473_parameter_t*)(curP + headerX.data1_length);
78: char* pStrings = curP + headerX.data1_length + headerX.data2_length;
79:
80: static BYTE methodCode_Text = 0x5B;
81: int nCurArgIndex = 0;
82: for (int i = 0; i < headerX.data1_length_div_4; ++i)
83: {
84: if (pMethods[i].args > 0)
85: {
86: if (pParameters[nCurArgIndex].charCount > 0 )
87: {
88: if (pParameters[nCurArgIndex].charOffset == 0 && pParameters[nCurArgIndex].charCount == 1)
89: {
90: // 是否为参数型字符串
91: }
92: else if (pParameters[nCurArgIndex].charCount >= 3 && pStrings[pParameters[nCurArgIndex].charOffset + 2] == 0)
93: {
94: // 是否为参数型字符串
95: }
96: else
97: {
98: // 导出文本
99: std::wstring strUNI;
100: pParameters[nCurArgIndex].charOffset;
101: if (SJIS2UNI(pStrings + pParameters[nCurArgIndex].charOffset, pParameters[nCurArgIndex].charCount, strUNI))
102: {
103: static WCHAR buf[2048]=L"";
104: swprintf(buf, L"0x%08X, %3d, %s\r\n", pParameters[nCurArgIndex].charOffset,
105: pParameters[nCurArgIndex].charCount, strUNI.c_str());
106: //OutputDebugStringW(buf);
107: fwrite(buf, sizeof(WCHAR), wcslen(buf), pOutFile);
108: }
109: }
110: }
111:
112: nCurArgIndex += pMethods[i].args;
113: }
114: }
115:
116: delete filebuf;
117: fclose(pOutFile);
118: }
119:
YU_RIS的cui插件,https://files.cnblogs.com/sunjicccc/YU_RIS.rar
YBN的字符串导出程序https://files.cnblogs.com/sunjicccc/ybn_export_strings.rar