破解这个游戏也有一段时间了,期间陆陆续续遇到一些困难,不过都得以解决,但是现在遇到的新问题就是游戏在模拟器上没问题(包括NO$GBA和ideal),不过在真机上死机(TT),也想不到什么方法解决,死神的提议也不知道怎么弄...。这里真的要感谢给我帮助的大大们(flyeyes,死神,空气,以及Cristal的教程《破解塞尔达字库》(tgb进不去了,所以大家想看教程的话,网上搜一下吧))
没打算写教程,所以只是一些简单的描述。
先来张测试截图吧:
1.封包的格式
dat.bin
evt.bin --文本(标准JIS编码)
grp.bin --图片,其中最大的文件为字库
scn.bin
其中文本主要在evt.bin中,但是其他文件里也有一些零散的文本。
格式:
格式:(封包格式头也是0x800字节对齐) 0x0-0x3 :4bytes 文件个数 0x3-0x1F:未知 0x20-0x22:第一个文件size = 2bytes * 8 0x22-0x24:第一个文件在此文件中的绝对地址 p_addr = 2bytes/2 * 0x800 (0x800对齐) n-1个文件* 4bytes 同第一个文件一样 n个文件 * 2bytes 文件名的相对指针(相对于文件名开始的地址) n个文件 * 2bytes 未知 n个文件名,以0结束 n个文件内容
2.压缩
在封包里的每个文件是压缩了的,跟asm后发现是lz的变种,这里放上asm代码和解压代码,如果有人有兴趣可以写写压缩代码。压缩代码我已经写了,不过只是随便写的,效率很低,就不放上来了。
所有的代码都会被折叠,请按expand source查看。
02026198 E4D03001 ldrb r3,[r0],1h ;2 74 0202619C E3530000 cmp r3,0h ;1 75 020261A0 0A00003E beq 20262A0h ;3 78 020261A4 E3130080 tst r3,80h ;1 79 020261A8 0A00001D beq 2026224h ;3 82 020261AC E4D0C001 ldrb r12,[r0],1h ;2 84 020261B0 E203E060 and r14,r3,60h ;1 85 020261B4 E1A03D83 mov r3,r3,lsl 1Bh ;1 86 020261B8 E1A0E2CE mov r14,r14,asr 5h ;1 87 020261BC E18C39A3 orr r3,r12,r3,lsr 13h ;1 88 020261C0 E28EC004 add r12,r14,4h ;1 89 020261C4 E041E003 sub r14,r1,r3 ;1 90 020261C8 E4DE3001 ldrb r3,[r14],1h ;2 92 020261CC E24CC001 sub r12,r12,1h ;1 93 020261D0 E35C0000 cmp r12,0h ;1 94 020261D4 E4C13001 strb r3,[r1],1h ;1 95 020261D8 CAFFFFFA bgt 20261C8h ;3 98 020261DC E5D03000 ldrb r3,[r0] ;2 100 020261E0 E20330E0 and r3,r3,0E0h ;1 101 020261E4 E3530060 cmp r3,60h ;1 102 020261E8 1AFFFFEA bne 2026198h ;3 105 020261EC E4D03001 ldrb r3,[r0],1h ;2 107 020261F0 E203C01F and r12,r3,1Fh ;1 108 020261F4 E35C0000 cmp r12,0h ;1 109 020261F8 DA000004 ble 2026210h ;3 112 020261FC E4DE3001 ldrb r3,[r14],1h ;2 114 02026200 E24CC001 sub r12,r12,1h ;1 115 02026204 E35C0000 cmp r12,0h ;1 116 02026208 E4C13001 strb r3,[r1],1h ;1 117 0202620C CAFFFFFA bgt 20261FCh ;3 120 02026210 E5D03000 ldrb r3,[r0] ;2 122 02026214 E20330E0 and r3,r3,0E0h ;1 123 02026218 E3530060 cmp r3,60h ;1 124 0202621C 0AFFFFF2 beq 20261ECh ;3 127 02026220 EAFFFFDC b 2026198h ;3 130 02026224 E3130040 tst r3,40h ;1 131 02026228 0A00000E beq 2026268h ;3 134 0202622C E3130010 tst r3,10h ;1 135 02026230 0203300F andeq r3,r3,0Fh ;1 136 02026234 0A000002 beq 2026244h ;3 139 02026238 E4D0C001 ldrb r12,[r0],1h ;2 141 0202623C E1A03E03 mov r3,r3,lsl 1Ch ;1 142 02026240 E18C3A23 orr r3,r12,r3,lsr 14h ;1 143 02026244 E283C004 add r12,r3,4h ;1 144 02026248 E35C0000 cmp r12,0h ;1 145 0202624C E4D03001 ldrb r3,[r0],1h ;2 147 02026250 DAFFFFD0 ble 2026198h ;3 150 02026254 E24CC001 sub r12,r12,1h ;1 151 02026258 E35C0000 cmp r12,0h ;1 152 0202625C E4C13001 strb r3,[r1],1h ;1 153 02026260 CAFFFFFB bgt 2026254h ;3 156 02026264 EAFFFFCB b 2026198h ;3 159 02026268 E3130020 tst r3,20h ;1 160 0202626C 0203C01F andeq r12,r3,1Fh ;1 161 02026270 0A000002 beq 2026280h ;3 164 02026274 E4D0C001 ldrb r12,[r0],1h ;2 166 02026278 E1A03D83 mov r3,r3,lsl 1Bh ;1 167 0202627C E18CC9A3 orr r12,r12,r3,lsr 13h ;1 168 02026280 E35C0000 cmp r12,0h ;1 169 02026284 DAFFFFC3 ble 2026198h ;3 172 02026288 E4D03001 ldrb r3,[r0],1h ;2 174 0202628C E24CC001 sub r12,r12,1h ;1 175 02026290 E35C0000 cmp r12,0h ;1 176 02026294 E4C13001 strb r3,[r1],1h ;1 177 02026298 CAFFFFFA bgt 2026288h ;3 180 0202629C EAFFFFBD b 2026198h ;3 183
解压代码:
/* read_file_to_buf:读取文件到内存 write_buf_to_file:写内存到文件 r0是压缩的内容的下标 r1是解压缩的内容的下标 */ void Uncompress(string file_name, string out_file_name) { int file_sz = read_file_to_buf(file_name, g_buf); memset(g_trs_buf, 0, sizeof(g_trs_buf)); int r0 = 0; int r1 = 0; uint r3, r12, r14; int i, j; r3 = g_buf[r0++]; while (r3) { if (!(r3&0x80)) { if (!(r3&0x40)) { if (!(r3&0x20)) r12 = r3&0x1F; else { r12 = g_buf[r0++]; r3 <<= 0x1B; r12 |= (r3>>0x13); } for (i=0; i<r12; ++i) { g_trs_buf[r1++] = g_buf[r0++]; } } else { if (!(r3&0x10)) r3 = r3&0xF; else { r12 = g_buf[r0++]; r3 <<= 0x1C; r3 = r12 | (r3>>0x14); } r12 = r3 + 4; r3 = g_buf[r0++]; for (i=0; i<r12; ++i) g_trs_buf[r1++] = r3; } } else { r12 = g_buf[r0++]; r14 = r3&0x60; r3 <<= 0x1B; r14 >>= 0x5; r3 = r12 | (r3>>0x13); if (r3 == 0xFFFFF140) { printf("r12=%08X/n", r12); } r12 = r14 + 4; r14 = r1 - r3; if (r14 > r1) { printf("r12=%08X r14=%08X r1=%08X/n", r12, r14, r1); printf("r3=%08X/n", r3); break; } for (i=0; i<r12; ++i) { g_trs_buf[r1++] = g_trs_buf[r14++]; } r3 = g_buf[r0]; r3 = r3&0xE0; while (r3 == 0x60) { r3 = g_buf[r0++]; r12 = r3&0x1F; if (r12 > 0) for (i=0; i<r12; ++i) g_trs_buf[r1++] = g_trs_buf[r14++]; r3 = g_buf[r0]; r3 = r3&0xE0; } } r3 = g_buf[r0++]; } write_buf_to_file(out_file_name, g_trs_buf, r1); printf("Uncompress succeed!/n"); }
3.扩容
这个游戏是需要扩容的,一共有1900多个字左右,根据大大们以前的经验,向这样的文字类游戏需要2700个字左右。
下面是我遇到的问题:
解压后字库格式很简单,就不多说了,经过测试,发现最大只能扩到2300个,否则就白屏(模拟器crash)。
可能的原因以及解决方法:
1.猜想程序员在写程序的时候malloc一个固定大小的内存,所以导致扩到2300个以上后,就导致缓冲区溢出...但是测试后发现貌似不是固定大小的(也有可能是先malloc一个固定大小,等解压后知道解压后的空间又调用relloc来重新分配...),这个没法解决,没跟到这个值。
2.因为知道有这么一块内存放字库的话,只要减少字模的大小,然后在显示时再转换为4bpp就能扩容了。这里我选择的是转2bpp为4bpp。
这里之所以能用这个方法是因为,凉宫在现实字模的时候,是先会每一个字都copy到内存的另外一个地方,然后再交由显存(这是我的猜想,没有继续往下跟)。所以我们只需要让游戏调用我们自己的copy函数,copy的时候顺便转换。
下面贴上我的转换函数,写的很挫,转换后的4bpp只能显示2种颜色...另外,gba 4bpp是从右往左存的,而2bpp是从左往右存的。参考的是白总的教材:
ASM_Begin: ;r0 解压后存放的地方 ;r1 字模id ;r3 字库首地址 Change_4Bpp_to_2Bpp: stmfd r13!,{r3-r9,lr} ;计算2bpp字模的地址 mov r2, r3 mov r3, ASM_End-ASM_Begin+4 ;Asm后面作为字模存放的地方 add r2, r2,r3 add r1, r2,r1,lsl #0x6 mov r2, r1 mov r1, r0 mov r0, r2 mov r2, #0x40 Loop: cmp r2, #0x0 beq Loop_Out ldrb r3,[r0],1 ;part1 and r4,r3,#0xC0 and r5,r3,#0x30 mov r4,r4,lsr #0x4 cmp r4,0 movne r4,#0x1 mov r5,r5,lsr #0x4 cmp r5,0 movne r5,#0x1 orr r4,r4,r5,lsl #0x4 strb r4,[r1],1 ;part2 and r4,r3,#0x0c and r5,r3,#0x03 cmp r4,0 movne r4,#0x1 cmp r5,0 movne r5,#0x1 orr r4,r4,r5,lsl #0x4 strb r4,[r1],1 sub r2,r2,1 b Loop Loop_Out: ldmfd r13!,{r3-r9,pc} ASM_End: