①,必须要懂得汇编
说简单,是简单,说复杂呢,也那么复杂
我所用的编码识别是参照国外的hack方法出来的
;我们用Thumb指令,比较简单些
@Thumb
;;此处,我将用09CFADD1代替,新手可直接将从5BA2开始 0A 48 03 68
;;然后在地址5BCC 修改成 D1 AD CF 09
ldr r0,=#0x09CFADD1 ;自定义程序首地址,一定要为首地址+1,表示Thumb程序,此段在实际中会有出入,自己适当的修改
bx r0 ;跳转,或者mov r15,r0
;;我们只修改以上8个字节,,贪方便,区分自定义程序
lsl r0,r0,0
lsl r0,r0,0
lsl r0,r0,0
lsl r0,r0,0
;;自定义程序
;;我们先执行曾经改写的程序
;;用goldroad1.7编译后,用16进制编辑器从此段bin(0D 48 03 68)开始复制到结尾
;;在ROM的地址01CFADD0开始粘贴完
ldr r0,=#0x2028D70
ldr r3,[r0]
ldr r2,[r3,#4]
ldrb r1,[r4]
;;一下判断 字符编码的值,这里我们用GB 2312;
cmp r1,#0xa1
bge gbk
;;如果是小于,则执行
ldr r1,=#0x8005BA9 ;跳回原程序
bx r1
;;GB程序
;;我们要判断原程序中有哪几个寄存器是要覆盖的
;;或者,你可以把sp转移到没有数据的地址
;;分别是,r1,r2,r0,
;;竟然只有三个可用.....
;;r2为字库首地址
.gbk
lsl r1,r1,#2
add r1,r1,r2
ldrb r2,[r4+#1]
add r4,#2
;;循环判断编码2的所有比较
.loop
ldr r1,[r1]
mov r0,#9
lsl r0,r0,#0x18 ;用来判断是否为rom地址
and r0,r1
cmp r0,#0
beq .NU
ldrb r0,[r1,#4] ;;这里我们读取字首地址+4(作为编码2),因为+5为字宽度
cmp r2,r0
beq end ;;首地址+4为编码2时,跳出此循环
bne loop
.NU
mov r1,#0
.end
ldr r2,[r3,#4] ;返回状态
ldr r0,=#0x8005BB3 ;返回原程序
bx r0
程序返回原来的程序时,r1=字模首地址
用goldroad1.7编译此汇编
②然后用16进制编辑器把,一些数据导入进去变化如图
好了,自定义程序写好了.
③运行后,发现没有任何问题,但是我们的字库并未导入,所以编码还是有问题.这里我们将写入字库
经分析:
字模首地址+0:下一个字模指针
字模首地址+4:字模编码2
字模首地址+5:字模宽度
字模首地址+8:字模数据
/// <summary>
/// 图新转换成2
/// </summary>
/// <param name="b">图形</param>
/// <param name="data">输出数据</param>
public static void map2bit2(Bitmap b, out byte[] data)
{
data = new byte[0x40];
int index = 0;
int bitIn = 0;
byte[] C = new byte[] { 0, 3 }; //这里为四色,因为嫌麻烦,我取消了阴影
int Ci = 0; //颜色索引
for (int Y = 0; Y < 16; Y++)
for (int X = 0; X < 16; X++)
{
Ci = (b.GetPixel(X, Y).ToArgb() == 0) ? 0 : 1; //当有颜色是,索引值为1.
data[index] |= (byte)(C[Ci] << (bitIn * 2)); //进行位运算
bitIn++;
if (bitIn > 3)
{
bitIn = 0;
index++;
}
}
}
/// <summary>
/// 写入指针,ROM的指针,循环发现有空指针,在空指针出写上新的指针,
/// </summary>
/// <param name="fs">文件</param>
/// <param name="Y">编码2</param>
/// <param name="Xbase">编码1的指针</param>
/// <param name="baseOff">字库首地址</param>
/// <param name="Newoff">新指针</param>
public static void WritePoint(FileStream fs,byte Y,int Xbase,int baseOff, int Newoff)
{
byte[] data = new byte[5];
int off = Xbase;
while (true)
{
Xbase = off - baseOff;
fs.Position = Xbase;
fs.Read(data, 0, 5);
off = BitConverter.ToInt32(data, 0);
if (off == 0)
{
fs.Position = Xbase;
fs.Write(BitConverter.GetBytes(Newoff + baseOff), 0, 4);
break;
}
if (data[4] == Y) break;
}
}
/// <summary>
/// 写入数据
/// </summary>
/// <param name="fs">文件</param>
/// <param name="off">地址</param>
/// <param name="data">数据</param>
public static void WriteData(FileStream fs, int off,byte[] data)
{
fs.Position = off;
fs.Write(data, 0, data.Length);
}
void creatBin()
{
FileStream Bin = new FileStream(@"D:\Tile.bin",FileMode.OpenOrCreate);
Bitmap map = new Bitmap(@"D:\000001.png"); //博客中某篇文章的图片
Bitmap Tile;
Graphics g;
byte X, Y;
byte[] data; //生成的数据
int baseOff = 0x09CFAE30; //ROM中自定义字库的基址
int newDataOff = 20; //新数据的指针
int[] Xoff = new int[0xf7 - 0xb0+1]; //初始化编码1的指针,这里适应文件
bool b = true;
for (Y = 0xA1; Y <= 0xfe; Y++)
for (X = 0xB0; X <= 0xF7; X++)
{
Tile = new Bitmap(16, 16);
g = Graphics.FromImage(Tile);
g.DrawImage(map, new Rectangle(0, 0, 16, 16), new RectangleF((Y - 0xA1) * 16, (X - 0xB0) * 16, 16, 16), GraphicsUnit.Pixel);
map2bit2(Tile, out data); //转换出数据,请看上面的函数
if (b) //第一次运行时,要初始化编码1的指针,后面就不用在初始化了
{
Xoff[X - 0xB0] = newDataOff;
}
else
{
WritePoint(Bin,Y, Xoff[X - 0xB0]+baseOff, baseOff, newDataOff); //写入新指针,并且在指针地址写入数据
}
WriteData(Bin,newDataOff+4,new byte[]{Y,14}); //写入编码2的值,和宽度,最好是13,14是为了安全显示
WriteData(Bin, newDataOff + 8, data); //写入数据
if (X == 0xf7) b = false;
newDataOff += 0x50; //其中包括 下一个字指针,编码2,宽度,数据
g.Dispose();
}
Bin.Close();
}
生成好后,你就要查找检验字库是否正确
/// <summary>
/// 查找字库的指针
/// </summary>
/// <param name="fs">文件</param>
/// <param name="Y">编码2</param>
/// <param name="Xbase">编码1的指针+字库基址</param>
/// <param name="baseOff">字库基址</param>
/// <returns></returns>
public static int searchData(FileStream fs, byte Y, int Xbase, int baseOff)
{
fs.Seek(0, SeekOrigin.Begin);
byte[] data = new byte[5];
int off = Xbase;
while (true)
{
Xbase = off - baseOff;
fs.Position = Xbase;
fs.Read(data, 0, 5);
off = BitConverter.ToInt32(data, 0);
if (off == 0) return 0;
if (data[4] == Y) break;
}
return Xbase;
}
④以上,我们生成好字库后
因为我们采用0x01CFAE30为基址,所以在0x01CFAE30处把生成好的bin数据全粘贴
⑤虽说,我们改写了程序,又导入了字库,那么成功了吗?
答案是,并没有成功,因为我们找到的一段程序中:
过程大概是,
字模 = 字指针基地址+(编码1值*4),数据如图
在下方,所有指针都为空,表示没有任何指针,不出现字符
这时我们重新写入指针
生成bin文件
private void ceateBinPont()
{
FileStream Bin = new FileStream(@"D:\TilePoint.bin",FileMode.OpenOrCreate);
int[] Xoff = new int[0xf7 - 0xb0 + 1]; //所有指针
for(int Y=0;Y<Xoff.Length;Y++)
{
Xoff[Y] = Y * 0x50 + 20;
Bin.Write(BitConverter.GetBytes(Xoff[Y] + 0x9CFAE30), 0, 4); //0x9CFAE30为字库基址
}
Bin.Close();
}
因为编码1的值是0xb0-0xf7,所以计算得出指针偏移,(0xb0*4)+0xB8B5B0 = 0xB8B870
把bin数据从0xB8B870导入进去
好了,现在终于完成导入工作了
查找"A man with a strong"(注:这是The Last Promise 版本的主角说明,随意修改成GBK文本)
测试结果如下
⑥发现写得好简略,下次有时间写详细点吧
附带已导入的游戏(下载地址:这里)
注:这个是白底黑字,还有个方框字.