|
bt 家族 栖凤楼 - 浴火凤凰
四方统领
    
邪派首席 - 帖子
- 2507
- 精华
- 7
- 声望
- 17279
- 银两
- 535
- 来自
- Τάρταρος
- 注册时间
- 2007-11-27
|
总舵主
大 中
小 发表于 2008-1-18 21:48 只看该作者
修改z.dat简明指南
z.dat文件是《金庸群侠传》的主程序文件。对它的修改可以改变原版中很多功能,甚至增添一些功能。
论坛上有很多已有的修改方法,许多时候你只要照着做就行了。但是如果你认为你的水平足够高,足够有耐心,想自己进行一些独特的修改,你可以参考一下这篇文章,也许它可以让你少走一些弯路。如果你做出一些优秀的修改,希望能够放上来与大家共享。
什么时候需要修改z.dat文件?
由于游泳的鱼增添了50指令系统,可以做出很多特殊的效果。因此当你想到一个功能的时候,可以先想想能否用事件实现。尽管50指令集并不易懂,但是比起十六进制和汇编代码,可读性强了很多。
当你确定你所要实现的功能不能用事件实现的话,就可以试着修改z.dat文件。下面我会举出一些例子,由浅入深进行说明。
准备工作
首先你要有游戏的原版文件,之后用游泳的鱼发布的新版z.dat替换掉原版,推荐使用0.72版,这是目前的最新版,而且短时间内似乎没有更新的迹象。同时,你可能需要场景贴图合并文件,还有手动增加一个atk024.wav。
论坛上有一份注释过的z.dat反汇编说明,阅读这些注释,你可以获得游戏的很多信息,并节省你自己分析的时间。
你还需要如下软件:
IDA 5.2,反汇编文件需要它打开。
OllyDBG,翻译十六进制代码与汇编指令。z.dat是DOS程序,难以直接调试,OllyDBG只能发挥翻译指令的作用。
UltraEdit,WinHex,两者都是十六进制编辑器,功能基本相同,看个人使用习惯选用。因为不能直接进行调试,所以很多时候需要先用OllyDBG翻译指令为十六进制代码,再手动写入文件里。
C32Asm可以代替上面两个软件的功能,尽管我还不习惯使用,但推荐这个。
Fish修改器,一些已知的修改,测试需要的不同存档,用此修改。
另外,需要你有汇编语言基础,至少你要能看懂汇编指令的含义,了解内存中数据的保存方式,知道我下面所用到的一些名词。如果你发现有些地方难以理解,首先要做的可能是学习汇编,而不是提问。
修改方法及实例
一、替换
这是指不必修改代码,仅仅修改相关数据即可。代码长度并不会变化。
1、数值替换
例1:改变中毒效果,使中毒损血增加。
经过研读z.dat文件及相关注释,认为以下语句控制中毒损血程度:
dseg02:0003C603 mov esi, 0Ah
dseg02:0003C60F mov eax, edx
dseg02:0003C611 sar edx, 1Fh
dseg02:0003C614 idiv esi
中毒损血=中毒程度/10,即十六进制0A,改掉0A所在位置即可。
2、地址替换
例2:去掉“用毒”的显示,在原位置显示“道德”。
在代码中可以找到:
dseg02:000231D3 movsx eax, word_901AA[ebx] ; 用毒能力
对应十六进制代码:0F BF 83 AA 01 09 00
但是修改之后发现没有效果。这是因为在编译时凡是用到地址的部分,代码段所写的地址是无效的,程序在运行时会在“重定位表”中寻找真实的地址。
AA 01 09 00从地址231D6开始,那么减掉20000,在重定位表中查找“031D6”,你可能找到:
Target : Internal reference
Object(8) #02
Offset(32) 0021005E
Source : 32-bit offset fixup (32-bits)
2.000031D6
上面的0021005E才是“用毒”的真实地址。
在z.dat里查找5E002100,你会找到:
07 10 D6 01 02 5E 00 21 00
07,10,02都是标志位,D6 01就是1D6,与你地址的后3位相同,不必怀疑,就是它了。那么把后面的5E 00 21改为70 00 21,保存,进游戏看看效果。
二、改写指令
这类修改通常指新的指令比原来的指令要短,不够的地方可以用nop占位,无需改写空白部分。
例3:使升级增加属性固定
研读升级段代码:
.........
dseg02:0003B7CA cmp word_901C4[eax], 5Ah ; 资质
dseg02:0003B7D2 jge short loc_3B7D8
dseg02:0003B7D4 push 5
dseg02:0003B7D6 jmp short loc_3B7DA
dseg02:0003B7D8
dseg02:0003B7D8 loc_3B7D8: ; CODE XREF: sub_3B6BE+114j
dseg02:0003B7D8 push 6 ; randmax
dseg02:0003B7DA
dseg02:0003B7DA loc_3B7DA: ; CODE XREF: sub_3B6BE+EEj
dseg02:0003B7DA ; sub_3B6BE+FCj ...
dseg02:0003B7DA call random??
dseg02:0003B7DF add esp, 4
dseg02:0003B7E2 lea edi, [eax+1]
.........
dseg02:0003B89C add word_901A2[ebx], di; 攻击力
.........
之前的大段代码是根据资质,将一个数压入栈,之后调用随机数过程,清栈,令edi=eax+1,di是edi的低位部分。
通常call的返回值在eax里面,那么利用这一点,将刚才入栈的值直接取回到eax。另外我们发现random(x)过程会返回0~x-1中的某个值,为使与原版效果相同,再将eax减1。
dseg02:0003B7DA pop eax
dseg02:0003B7DB dec eax
原版的call指令是5字节,add指令3字节,修改之后的两个指令只有2字节,长度不够的地方用6个nop(90)填补。
三、增加指令
增加显示功能,改写伤害公式等都属于这方面。特点是修改的代码比原代码要长。
首先你要研究原来的代码,找一个适合的地方,改写为call指令或jmp指令。
最好你改写的地方没有地址的调用,没有对栈的操作,没有跳转指令,只有对寄存器的操作。还要足够长,至少5个字节。
在z.dat的空白位置,添加你的指令。你所用到的寄存器根据具体情况决定是否先要将原值入栈。如果添加了地址调用还要改写重定位表。在游泳的鱼发布的新版z.dat中重定位表有大量剩余空间,这是为何推荐使用新版的原因。最后根据根据情况把原来替换掉的指令写在新加程序段的开头或结尾。
例4:完全解除等级上限
先参考一下这里:http://www.txdx.net/viewthread.php?tid=393071&extra=page%3D1
研读升级部分指令:
.........
dseg02:0003B6CE mov esi, [esp+10h+arg_0]
dseg02:0003B6D2 imul eax, esi, 0B6h
dseg02:0003B6D8 movsx edx, word_9016A[eax] ; 等级
dseg02:0003B6DF mov ax, word_9016C[eax] ; 经验值
这里利用ecx,esi计算经验值所在地址,后面要用到
.........
dseg02:0003B6FA loc_3B6FA: ; CODE XREF: sub_3B6BE+5Aj
dseg02:0003B6FA mov bx, word_9016C[ebx] ; 经验值
dseg02:0003B701 cmp bx, word_5458E[eax*2] ; 升级经验列表
修改此句为
dseg02:0003B701 sub bx, word_5458E[eax*2] ; cmp指令不改变bx,但sub指令会将差值送入bx
dseg02:0003B709 jb short loc_3B70E
dseg02:0003B70B lea ecx, [eax+1]
以上2句占据5字节,可以容纳call指令,修改此2句为
dseg02:0003B709 call XXXXXXXX
dseg02:0003B70E
dseg02:0003B70E loc_3B70E: ; CODE XREF: sub_3B6BE+4Bj
dseg02:0003B70E inc eax
.........
选择适合的空白处添加:
XXXXXXXX:
jb +12 ;根据比较结果是否跳转12字节到ret
push edx ;保存edx的值,在原有程序段中,ebx,ecx,eax都被使用过,所以利用edx
imul edx, esi, 0B6h ;利用edx,esi计算经验值所在地址
lea ecx, [eax+01] ;重复原指令
mov word_9016C[edx], bx ;将减后的经验值送入原地址,这句还要添加重定位表
pop edx ;恢复edx的值
ret ;返回
你可以试着在头脑中运行改前和改后的程序,看看除了添加的效果之外是否完全一致。
后记
修改并不是一件容易的事情,在修改前你最好想一想,要怎样修改,改成什么样子。而在设计汇编的时候,要想到你在使用的寄存器是否在后面会被原来的程序使用。通常代码写错的结果会导致游戏错误而跳出,但是更多时候可能是没有效果。不要灰心,上面所提到的一些实例已经包括了修改z.dat的大部分技术,操作正确就一定可以成功。
常见指令解析
以下是一些指令的提示(我想到的话会补充)。
1、移位指令和修改方案
mov eax, ebx
shl eax, 2
sub eax, ebx
实际上是eax=ebx*3,可以用
imul eax, ebx, 3
代替,这样3就可以改为别的数字。
2、自加指令和修改方案
add eax, eax
实际上是eax=eax*2,这个指令仅有两个字节,可以改成移1位指令、空指令和清除指令(xor),相当于2可以改为1、0和0.5。如果想获得更大的自由度,必须添加转向指令,参考增加指令部分。
3、绘制黑色矩形
以下程序段用于绘制游戏中特有的那种“黑色矩形”,就是你打开“状态”看到的那样。
push 3
push offset array_VRAM
push 0
push 0FFh ; 以上3个不必管
push 0C8h ; 高度
push 0D2h ; 宽度
push 0 ; 起点的纵坐标位置
push 37h ; 起点的横坐标位置
call sub_2CEBF
add esp, 20h ; 清栈
4、显示字符串
以下程序段用于显示游戏中的字符串。
push 10h
push 705h
push offset array_VRAM
push offset byte_C07C4 ; 字串的地址,修改此处需要改重定位表
push 23h ; 字串纵坐标位置
push 6Bh ; 字串横坐标位置
call draw_string ; 显示字符串
add esp, 18h ; 清栈
[ 本帖最后由 scarscc 于 2008-6-13 22:35 编辑 ]
|