底部有编译好的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
 posted on 2013-01-02 11:08  损疾  阅读(12879)  评论(0编辑  收藏  举报