天天看点

有关The Last Promise的汉化②改写程序

①,必须要懂得汇编

说简单,是简单,说复杂呢,也那么复杂

我所用的编码识别是参照国外的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           
有关The Last Promise的汉化②改写程序

程序返回原来的程序时,r1=字模首地址

用goldroad1.7编译此汇编

②然后用16进制编辑器把,一些数据导入进去变化如图

有关The Last Promise的汉化②改写程序
有关The Last Promise的汉化②改写程序

好了,自定义程序写好了.

③运行后,发现没有任何问题,但是我们的字库并未导入,所以编码还是有问题.这里我们将写入字库

经分析:

字模首地址+0:下一个字模指针
字模首地址+4:字模编码2
字模首地址+5:字模宽度
字模首地址+8:字模数据           

生成字库C#代码

/// <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数据全粘贴

有关The Last Promise的汉化②改写程序

⑤虽说,我们改写了程序,又导入了字库,那么成功了吗?

答案是,并没有成功,因为我们找到的一段程序中:

过程大概是,

字模 = 字指针基地址+(编码1值*4),数据如图

有关The Last Promise的汉化②改写程序

在下方,所有指针都为空,表示没有任何指针,不出现字符

这时我们重新写入指针

生成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文本)

测试结果如下

有关The Last Promise的汉化②改写程序

⑥发现写得好简略,下次有时间写详细点吧

附带已导入的游戏(下载地址:这里)

注:这个是白底黑字,还有个方框字.

GBA