火花天龙剑 -> 火花学园 -> 用VC++做最简单的静态修改器

Dragon-Master 2004-09-07 18:03
自己动手制作ROM(静态)修改器(VC++)
当你使用别人制作的修改器时,可能其中没有你想修改的内容,而用FPE等一步步修改又十分烦琐,现在我简单介绍一下做一个静态修改器的原理及思路,虽然可能和你用FPE等修改差不多麻烦,但是如果修改的多了,比如反复对776人物成长率修改,你会发现做个修改器会比较方便,同时希望有一天你也可以让大家共同分享你的成果。
本人从事修改时间不长,做修改器更是,也算是个新手吧,本文只是对做修改器提供了一个简单的思路和方法,可能很不周全,望大家谅解。

★初次完游戏就修改无益。★

1.打开Visual C++。

2.选择文件(File)->新建(New);在工程(Project)选项卡中选择MFC AppWizard(exe),选项,并在左上方的工程名(Project name)输入一个工程名,例如Fix,下方是你存放此工程的路径及位置。


3.单击OK,在中可以选择工程类型,有单文档(Single document),多文档(Multiple document),基于对话框(Dialog Based)三种模式,做简单的修改器用基于对话框模式即可,单击Finish保持默认设置即可以完成新建工作。(当然,你也可以单击Next进行自行设置,是关于外观设置的,是关于自动注释和连接库的设置的,此步中如果选使用共享连接库(As a shared DLL)修改器若拿到其它机器上则那些机器必须具有相应的DLL文件才可以运行,常见的缺少Mfc42d.dll等就是这个问题,如果选使用静态连接库(As a statically linked library)则生成的exe文件会大很多,但是将用到的DLL文件包含了进去。是关于创建跟随类的,不需去管。)

4.Finish后,是关于你做的设置的描述的,单击OK,创建结束。
这4步应该每什么问题,一学就会了。

下面是关于修改器框架的制作。
5.创建完成,你会见到如下界面,上方的红色框内是菜单区,一般应用程序都有这个,往下的粉红框是工作区(Workspace),再往下的天蓝色框是输出区(Output Pane),调试时错误和结果在此显示,右上的绿色框是最常用的两个按纽,编译、运行(当然在菜单中也有),往下的深蓝框是添加控件用的没有的话单击右键,选中Controls,在这里,只需要知道静态文本按纽(用于添加静态文本的,如对话框中的那些字),文本编辑框按纽(添加最常用的控件之一,编辑框),按纽空间(看看那个确定、取消就明白了),实际上用这3个控件基本已经可以做简单的修改器了。后面还有下拉式组合框、下拉式列表、单选按纽、复选按纽等都是常用控件,为避免麻烦尽量少用,如果用到再说。


6.现在,随自己喜好设计界面,同时你应先想好做什么游戏的修改器,修改写什么比如改多拉基亚776的主角利夫的成长率,首先应知道成长率在镜像的位置(位置基本可以在火花论坛上找到,如果找不到你想改的可以自己试着去找,如果你想修改的内容太......)。现得知利夫能力在31C2D,则成长率后数11字节即31C38。数数成长率共9字节,那就放上9个文本编辑框,再放两个按纽(用原来那两个也行),为让别人知道你那9个框是些什么,至少要放点注释吧,放9个静态文本就成了,编辑静态文本和按纽上的文字用右键,选Properties,打开如图对话框,红框是编辑文字,绿框是此空间ID(一会介绍),其它部分是些外观等设置,有兴趣可以自己试试,反正点上看看就是了。编辑完对话框如下图:


7.现在要设置控件ID(关键)
为了便于记忆,将HP、STR、MAG、SKL、SPD、DEF、BLD、LUK、MOV成长率对应的编辑框依次设为IDC_HP、IDC_STR、IDC_MAG......IDC_MOV(当然设成这样是个人习惯,随自己喜好设吧,不过不能重复),将那个确定按纽改成:读出(IDC_READ),取消改成:写入(IDC_WRITE)(你非改成:出来(asfasfa)、进去(ssdgsd)也没人拦你),再设计个按纽,名为打开(IDC_OPEN)用于选择要打开的ROM。(如果你自己用,这个可以不要)

8.用类向导(ClassWizard)为各控件添加入口地址(类向导在View菜单中,或者Ctrl+W)(关键)
调出类向导,选Member Variables标签,在Class name中选CFixDlg(一般默认在此),可以见到你刚才添加的那些控件ID,但是没有类型(Type)、成员(Member),现在为其添加,双击其中一项,以IDC_HP为例,添加成员名如m_HP,Category中选Value,Variable type中选UINT(无符号整型,因为成长率是一项大于0的整数),如图:点击OK结束,重复为其它成长率也添加。不过那两(三)个按纽就不必添加了。


再选Message Maps选项卡,为那两个按纽添加消息函数,选中IDC_READ,在Messages中出现两项,一个单击生效,一个是双击生效,选单击生效(BN_CLICKED),双击->OK,即可添加完毕,同样再为IDC_WRITE、(IDC_OPEN)添加。最后单击OK结束类向导。


至此对话框设计完毕。
最后,是编程阶段,也是最复杂的阶段(如果你想编个十分完善的修改器,当然,控件不能就这些)。
10.添加完类向导后,回到对话框,双击读出按纽,就来到void CFixDlg::OnWrite() 函数,读出命令就在此编写,那两个按纽也是。这些内容位于FixDlg.cpp文件中,可以在工作区的FileView选项卡中见到它,也可以在ClassView中见到每个函数。

11.首先编写单击打开按纽的效果程序。(你可以没接触过任何编程,着葫芦画瓢就可以了)
整个函数如下:
先在整个文件的最上部#endif下面添加如下代码("//"后面是注释与程序无关)
#endif
static CString FilePath;//CSrting类型的全局变量FilePath,用于保存ROM路径
再在OnOpen函数中添加如下代码:
void CFixDlg::OnOpen()
{
CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,"smc文件 (*.smc)|*.smc||");
//打开后缀为smc的文件,即SFC的ROM文件
if(dlg.DoModal()==IDOK)
{
FilePath=dlg.GetPathName();
}
}

12.再编写单击读出按纽时的效果程序。
void CFixDlg::OnRead()
{
BYTE temp[9];int ID=0x31C38;
//定义一个BYTE型数组,包含9个元素,用于存放读出的数据再定义一个int型变量存放HP成长率地址 (开头加0x表示16进制数,也可写成203832)
CFile My776;
//定义一个CFile类型的成员My776(可随便起名,但必须由字母,数字,下划线组成, 开头必须是字母,下划线,并且不能是if,case等C语言本身的关键字)
My776.Open(FilePath,CFile::modeRead);//打开文件
for(int i=0;i<9;i++)//循环9次,读出9个数,分别放到temp[0]-temp[8]中
{
My776.Seek(ID+i,CFile::begin);//从文件头开始向后数到0x31C3A
My776.Read((void*)&temp,1);//读出一字节
}
m_HP=temp[0],m_STR=temp[1],m_MAG=temp[2],m_SKL=temp[3],m_SPD=temp[4],m_DEF=temp[5],
m_BLD=temp[6],m_LUK=temp[7],m_MOV=temp[8];//读出的数传递给那9个编辑框
UpdateData(FALSE);//将数据显现
RMy776.Close();//关闭文件
}
完成这部,你可以编译一下,看看有没有错误,没有错误就可以运行一下看看结果是否正确(此地址对应776中文非压缩ROM),如果有错误,按F4看看哪行有错,是不是拼写有误。注意,如果自己用,第11步可以省,将
My776.Open(FilePath,CFile::modeRead);中FilePath改成你ROM的路径,加引号,\\分隔,例如"C:\\My Document\\776ROM\\多拉基亚776.smc"

13.最后是写入部分了
和读出基本相同了
void CFixDlg::OnWrite()
{
BYTE temp2[9];int ID2=0x31C38;
if(UpdateData()==TRUE)
{
temp2[0]=m_HP,temp2[1]=m_STR,temp2[2]=m_MAG,temp2[3]=m_SKL,temp2[4]=m_SPD,temp2[5]=m_DEF,
temp2[6]=m_BLD,temp2[7]=m_LUK,temp2[8]=m_MOV;
}
CFile WMy776;
WMy776.Open(FilePath,CFile::modeWrite);
for(int i=0;i<9;i++)
{
WMy776.Seek(ID2+i,CFile::begin);
WMy776.Write((void*)&temp2,1);
}
WMy776.Close();
}
思路基本和12步相反。个人使用FilePath的处理同上。
最后,编译,运行,实验修改一下,如果无误,恭喜你,一个最简单的修改器做成功了。

扩展处理
14.一个修改器,就算是极其简单的,仅做成这样也是不够的,设想776那么多人,你自己用还好,可以随时改改int ID=0x31C38;修改其它人的,况且也很麻烦,如果给别人,何解?总不能做它几十个吧。现在,就简单介绍一下利用控件"下拉式组合框"解决这个问题。
先说说,之所以用"下拉式组合框"而不用"下拉式列表"原因有二,一是占地面积小(垃圾原因),二是选项添加简单。不过用起来不象"下拉式列表"那么直观。
(1).添加控件,很简单,但要注意单击右面的尖头向下拉长点。
(2).设置控件ID:IDC_CHAR;添加项目:在Properties的data选项卡中添加,每行对应一项,用Ctrl+Enter换行
,假设添加5项:利夫、菲恩、奥辛、哈鲁巴恩、艾维尔。
(3).用类向导为IDC_CHAR添加成员变量m_CHAR,类型CString。
(4).对原来的程序进行添加
void CFixDlg::OnRead()
{
BYTE temp[9];int ID=0;☆
if(UpdateData()==TRUE)★
{★
if(m_CHAR=="利夫")★
ID=0x31C38;★
else if(m_CHAR=="菲恩")★
ID=0x31C68;★
else if(m_CHAR=="奥辛")★
ID=0x31C98;★
else if(m_CHAR=="哈鲁巴恩")★
ID=0x31CC8;★
else if(m_CHAR=="艾维尔")★
ID=0x31CF8;★
}★
CFile My776;
My776.Open(FilePath,CFile::modeRead);
for(int i=0;i<9;i++)
{
My776.Seek(ID+i,CFile::begin);
My776.Read((void*)&temp,1);
}
m_HP=temp[0],m_STR=temp[1],m_MAG=temp[2],m_SKL=temp[3],m_SPD=temp[4],m_DEF=temp[5],
m_BLD=temp[6],m_LUK=temp[7],m_MOV=temp[8];
UpdateData(FALSE);
RMy776.Close();
}
注:★表添加,☆表改动。
判断选项对应数据有多种,此处用一种最容易理解的,意思就是当m_CHAR这个组合框中为利夫时,变量ID就被赋值0x31C38,当为菲恩是,赋值0x31C68,当......
如此,编译运行,在组合框中选利夫,读出试试,再选其他试试,比较结果是否正确。
(C语言中,==表判断是否相等,=表示赋值)
写入部分的改动相信大家一定会了吧,其实和读出基本一模一样
void CFixDlg::OnWrite()
{
BYTE temp2[9];int ID2=0;☆
if(UpdateData()==TRUE)★
{★
if(m_CHAR=="利夫")★
ID2=0x31C38;★
else if(m_CHAR=="菲恩")★
ID2=0x31C68;★
else if(m_CHAR=="奥辛")★
ID2=0x31C98;★
else if(m_CHAR=="哈鲁巴恩")★
ID2=0x31CC8;★
else if(m_CHAR=="艾维尔")★
ID2=0x31CF8;★
temp2[0]=m_HP,temp2[1]=m_STR,temp2[2]=m_MAG,temp2[3]=m_SKL,temp2[4]=m_SPD,temp2[5]=m_DEF,
temp2[6]=m_BLD,temp2[7]=m_LUK,temp2[8]=m_MOV;
}★
CFile WMy776;
WMy776.Open(FilePath,CFile::modeWrite);
for(int i=0;i<9;i++)
{
WMy776.Seek(ID2+i,CFile::begin);
WMy776.Write((void*)&temp2,1);
}
WMy776.Close();
}
编译运行,你的修改器就完成了。


15.几个问题。
(1)组合框中排列问题
在Properties中的styles选项卡中,有项sort,选中时为自动排列。
(2)每选一个人物就要单击一下读出太麻烦,可否省略
可以,首先,将组合框Properties中的styles选项卡中的Type改为Drop List;类向导中设置成员函数m_CHAR,Gategory类型为Control,Type:CComboBox;类向导中选IDC_CHAR,为其添加CBN_SELCHANGE消息处理函数。
(3)用以下程序取代读出部分函数,别忘将IDC_READ:BN_CLICKED消息处理函数从类向导中删除。
void CFixDlg::OnSelchangeChar() ☆
{
BYTE temp[9];int ID=0;
switch(m_CHAR.GetCurSel())☆
{☆
case 0:☆
ID=0x31C38;break;☆
case 1:☆
ID=0x31C68;break;☆
case 2:☆
ID=0x31C98;break;☆
case 3:☆
ID=0x31CC8;break;☆
case 4:☆
ID=0x31CF8;break;☆
default:☆
MessageBox("Not Choose Any Char,As LiFu");☆
ID=0x31C38;break;☆
}☆
CFile RMy776;
RMy776.Open(FilePath,CFile::modeRead);
for(int i=0;i<9;i++)
{
RMy776.Seek(ID+i,CFile::begin);
RMy776.Read((void*)&temp,1);
}
m_HP=temp[0],m_STR=temp[1],m_MAG=temp[2],m_SKL=temp[3],m_SPD=temp[4],m_DEF=temp[5],
m_BLD=temp[6],m_LUK=temp[7],m_MOV=temp[8];
UpdateData(FALSE);
RMy776.Close();
}
同理void CFixDlg::OnWrite()开头也要进行同样改动,不再赘述。

用列表框处理方法完全相同,添加成员时用到命令AddString。
怎样,做一个简单的火纹ROM修改器基本就是这样了,无非就是找地址,读出,转化,写入3个步骤(TRS还需要计算)。既然地址是现成的,做个简单的修改器就十分简单了,做好了别忘大家共享哦。
(估计GBA3代ROM格式基本相同,你可以做个烈火的模板,等圣魔出了,得到地址套进去,估计一个简单的圣魔修改器就诞生了。)

本文面向对想制作一个简单的火纹修改器并且对高级编程语言完全不懂或者稍懂一点而无从下手的朋友,文中只是提供了一点简单的思路,可能不很周到,解说不够明确,但希望对你能有点帮助。

leonhart 2004-09-07 18:19
好帖支持一下

rick2003 2004-09-07 21:56
太棒了
感谢老大

克瑞斯坦 2004-09-08 21:08
这对编程的初学者无疑有着很好的指导作用。

希罗 2004-09-11 20:12
等的就是这个啦!
上次请教 还以为…………
这样的话…………
776就…………
拉~!

suck 2004-09-12 04:58
[QUOTE]最初由 希罗 发表
[B]等的就是这个啦!
上次请教 还以为…………
这样的话…………
776就…………
拉~! [/B][/QUOTE]
虽然我看到也很高兴,但大哥你的反应似乎太…………

疾风之舞 2004-09-23 18:02
绝对好铁!!
支持!!

中草药 2004-09-23 22:15
楼主好强

shengxian 2004-09-26 05:09
楼主好强哥!

flame776 2004-09-28 19:40
楼主真的好强,以后希望能多发些关于可以运用于游戏的学习贴!!

Andony 2004-09-30 17:43
我开使纳闷了,楼主你是干什么的?
还有学习C++就能编这种修改器吗?
向你学习!

拎壶冲 2004-09-30 23:57
偶还是用FPE吧 头大

坦格尔斯 2004-10-06 12:44
超厉害,我只能用UE改改.

残酷天使 2004-10-07 01:08
。。。好多字+好多图=好头晕呀!。。。
小生愚钝 无奈 无奈。

blacklvan 2009-09-09 18:16
LZ 你这个帖子上的程序是有错误的
应该是显示的问题
比如说读取部分 My776.Read((void*)&temp,1);
这条程序应改为 My776.Read((void*)&temp[ i ],1);
否刚读取出的数据不对。
类似的写入部分也有同样的错误
我只是指出一条,还有其它一些错误

Dragon-Master 2009-09-09 20:59
下面是引用blacklvan于2009-09-09 18:16发表的:
LZ 你这个帖子上的程序是有错误的
应该是显示的问题
比如说读取部分 My776.Read((void*)&temp,1);
这条程序应改为 My776.Read((void*)&temp[ i ],1);
否刚读取出的数据不对。
.......


这还真没注意,实际上发帖时是正确的,但编辑的为什么和显示的居然不一样

blacklvan 2009-09-09 21:55
LZ 你试过这个程序没有?写入部分好像有问题?
给temp2[ i ]赋值好像不对
保存后的数后显示的数据是204(cc),是temp2的初始值

blacklvan 2009-09-10 12:13
void CFixDlg::OnWrite()   //写入部分
{
BYTE temp2[9];
int ID2=0;
switch(m_CHAR.GetCurSel())
{
case 0:
ID2=0x3B467;break;
case 1:
ID2=0x3B48D;break;
default:
MessageBox("Not Choose Any Char,As 辛格尔德");
ID2=0x3B467;break;

}
if(UpdateData()==TRUE)
{
  temp2[0]=m_HP,temp2[1]=m_STR,temp2[2]=m_MAG,temp2[3]=m_SKL,
  temp2[4]=m_SPD,temp2[5]=m_DEF, temp2[6]=m_MDF,temp2[7]=m_LUK;
}

CFile WMyFE4;
WMyFE4.Open(FilePath,CFile::modeWrite);
for(int i=0;i<8;i++)
{
WMyFE4.Seek(ID2+1+i,CFile::begin);
WMyFE4.Write((void*)&temp2[ i ],1);
}
WMyFE4.Close();
}

这是我参照LZ写的一个系谱的修改器
写入部分LZ最后略去了,我把写入部分在公布一下。

blacklvan 2009-09-13 22:19
LZ 向你请教一个问题
我建了一个主窗口和两个子窗口
如果我把OPEN(打开ROM)这个控件放在主窗口,SelchangeChar放在子窗口,如何把主窗口的消息传递给子窗口?

kobefu 2016-08-10 03:41
很久都没来论坛了。最近想自己做一个ROM修改器,只可惜图都看不到了……

dalaohu100 2019-10-06 21:37
经典,收藏,备用


查看完整版本: [-- 用VC++做最简单的静态修改器 --] [-- top --]


Powered by PHPWind v3.0.1 Code © 2003-05 PHPWind
Gzip enabled

You can contact us