标题: 光荣三国志2的相关分析、逆向与重写
性别:未知-离线 juqiang
(方枪枪)

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 546898
注册 2024-7-23


发表于 2024-7-25 14:31 资料 短消息 只看该作者
光荣三国志2的相关分析、逆向与重写

94年我上大二,开始玩三国志2,到今年已经过去了整整30年的时间。这是人生的第一个游戏,时不时就会拿起来玩一玩。毕业设计,写的也是它的内存修改器和存档修改器,当时用的TSR技术。

一直念念不忘,要看这个游戏的所有细节,以至于成为了一个执念。

最近两年,断断续续查找资料,最终还是走上了disassembly的道路,用debug.exe + IDA死抠。gamespot上有大神,大概四五个人,也是几十年的执念。从他们那里借鉴了很多,尤其是IDA的分析,把我领进了门。

我的最终愿望,是用python把这个游戏完整的用python写出来,理论上可以一直到手机、PC和MAC上(因为我只依赖于pygame这个module)。

程序做了一大半,内政几乎都OK了,外交、间谍、打仗,还没有抠。半拉子工程在这里:http://github.com/JuQiang/Rotk2_Python,希望我能在2年内写完这个程序。

当年搜索资料,看到VAN大神对于头像的分析,对于调色板的分析,醍醐灌顶,受益匪浅!

乱七八糟记录一下,作为我在这个站的第一个帖子,情绪有些小激动。我会先把我的发现,都post在这个主贴里面。


顶部
性别:未知-离线 juqiang
(方枪枪)

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 546898
注册 2024-7-23


发表于 2024-7-25 14:34 资料 短消息 只看该作者
将领的信息

将领总数:255之内
将领偏移起始地址:0x20
每个将领大小:0x2B
0x00~0x01:此值减去0x38,是同城市下一个将领的偏移量
0x02~0x02:高4个字节是否死亡,低4个字节是否可以移动
0x03~0x03:高4个字节是否内应,低4个字节是否生病
0x04~0x04:智力
0x05~0x05:战力
0x06~0x06:号召
0x07~0x07:义理
0x08~0x08:仁德
0x09~0x09:野望
0x0A~0x0A:主公编号,0x20+此值乘以0x2B就是主公地址
0x0B~0x0B:忠诚
0x0C~0x0C:侍卫
0x0D~0x0D:埋伏势力
0x0E~0x0E:埋伏城市
0x0F~0x0F:相性,给了貂蝉后,相性从0f变成了08
0x10~0x11:血缘
0x12~0x13:士兵
0x14~0x15:武器
0x16~0x16:训练
0x17~0x18:【未知】17给了青龙刀后,从0变成6,18从ff变成0。给了赤兔马后,18从ff变成了0,17没变。给了书之后,18从ff变成了7A,17从6变成了2。17给了刀,从6变成0?与前面矛盾?

0x19~0x19:出生年份
0x1A~0x1B:此值减1是头像偏移。如果小于219,则是标准头像;否则是拼接头像
0x1C~0x1D:姓名第一个字索引,如果是英文则对应为ASCII码
0x1E~0x1F:姓名第二个字索引,如果是英文则对应为ASCII码
0x20~0x21:姓名第三个字索引,如果是英文则对应为ASCII码
0x22~0x23:姓名第四个字索引,如果是英文则对应为ASCII码
0x24~0x25:姓名第五个字索引,如果是英文则对应为ASCII码
0x26~0x27:姓名第六个字索引,如果是英文则对应为ASCII码
0x28~0x29:姓名第七个字索引,如果是英文则对应为ASCII码
0x2A~0x2A:【未知】

文件taiki.dat

0x06开始,每个将领长度为0x2E
0x00~0x00:出生年份
0x01~0x01:血缘
0x02~0x02:登录郡
0x03开始,与上面将领资料相同了。


顶部
性别:未知-离线 juqiang
(方枪枪)

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 546898
注册 2024-7-23


发表于 2024-7-25 14:35 资料 短消息 只看该作者
城市信息

城市总数:41
城市偏移起始地址:0x2D8C
每个城市大小:0x23
0x00~0x01:同主公的下一个城市的偏移量
0x02~0x03:此值减去0x38是该郡太守的偏移量
0x04~0x05:此值减去0x38是在野武将的偏移量
0x06~0x07:此值减去0x38是新人武将的偏移量
0x08~0x09:黄金
0x0A~0x0D:粮食
0x0E~0x0F:此值乘以100是人口数
0x10~0x10:主公的编号,如果是FF,此郡为空郡。主公地址是0x2AFC+此值乘以0x29
0x11~0x11:正在战争的诸侯编号,如果是FF,没有战争。否则该诸侯地址是0x2AFC+此值乘以0x29
0x12~0x15:0x12:没有代理=0,全权代理=4,代理内政=5,代理军事=6,代理人事=7;0x13 if exist merchant;0x14:战争郡,从0开始;0x15:运送黄金和粮食的郡编号,从0开始
0x16~0x16:地价
0x17~0x17:民忠
0x18~0x18:治水
0x19~0x19:良驹
0x1A~0x1A:城堡
0x1B~0x1B:米价
0x1C~0x1F:mini map width & height。
0x20~0x20:郡的X坐标编号
0x21~0x21:郡的Y坐标编号
0x22~0x22:郡名称编号


         0         1         2         3         4         5         6         7
0                                                      2         1         
1                                    4         3         6                  
2                                    5         7         9         8         
3        15                          11        10        17        16        24
4        14        13        12        20        19        28        18        25
5                 30        29        31        21        22        27        26
6                 33        32        40        23        38        37         
7                 35        34        41        39                           
8                          36                                             

番号        00        01        02        03        04        05        06        07        08        09        0A        0B        0C        0D
州名        幽州        幷州        冀州        青州        兗州        司州        雍州        涼州        徐州        予州        荊州        揚州        益州        交州

(郡县的坐标,在这个编辑器里会错乱,我放一个图上来)。

[ 本帖最后由 juqiang 于 2024-7-25 14:38 编辑 ]


图片附件: provinces.png (2024-7-25 14:38, 73.96 K)

顶部
性别:未知-离线 juqiang
(方枪枪)

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 546898
注册 2024-7-23


发表于 2024-7-25 14:39 资料 短消息 只看该作者
国家信息

0x0f:诸侯数量

起始地址:0x2AFC,长度0x29
0x00~0x01:减0x38为主公偏移
0x02~0x03:减0x38为主公所在郡
0x04~0x05:减0x38为军师偏移
0x06~0x06:每个国家对我的信任度
0x0A~0x0B:0A的bit0,是国家0是否与我结盟;bit1,是国家1是否与我结盟。。。bit7是国家7是否与我结盟;0B的bit0,是国家8是否与我结盟。。。0B的bit7是国家15是否与我结盟。

0x0C~0x0D: magic
calculate:
magic = 0
iterate ruler's every province:
        a=int(troops/100), b=(number of officers)*10
        magic = a+b

0x0E~0x1C:每个国家对自己的敌对度
0x21~0x21: marriage
0x22~0x22: FF为正常,1E为流浪
顶部
性别:未知-离线 juqiang
(方枪枪)

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 546898
注册 2024-7-23


发表于 2024-7-25 14:40 资料 短消息 只看该作者
存档信息

0x335A~0x335A: 当前活跃诸侯编号
0x335C~0x335D:-0x38当前诸侯信息的地址
0x335E~0x335F:-0x38诸侯自身将领地址
0x3362~0x3363: -0x38为当前发令的城市地址
0x3365~0x3365: birthday month
0x3366~0x3366: birthday day
0x337B~0x337B:01=难度简单,02=难度中等,03=难度高级
0x337C~0x337C:低4位,03bit是否观战,02bit是否图示,01bit是否音乐,00bit是否音效。高4位,04bit=0史实方式,=1虚拟方式。
0x337D~0x337D:等待的信息速度
0x337E~0x337E:差使速度。0,无显示;2,高速,4标准,6低速。
0x33B4~0x33B4:第10郡宝物出现的次数,如果改为0,则会一直出。
顶部
性别:未知-离线 juqiang
(方枪枪)

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 546898
注册 2024-7-23


发表于 2024-7-26 16:47 资料 短消息 只看该作者
汉字的显示

对于游戏中普通汉字,三国志2并没有用encoding模式,而是把需要的所有汉字,都存入到了msg.16p文件中,这也是当年没有汉字系统时,国内DOS时代显示点阵汉字的一个方法。只存需要的汉字的字模,无用的则不存。

msg.16p,每个汉字是14像素高*16像素宽,所以是28个字节代表一个汉字。这个汉字的第一行,是读byte0和byte1,分别对应左8个像素和右8个像素。再下一行,则是读byte2和byte3,分别对应左8个像素和右8个像素,以此类推。

细节1,第一个汉字是从第2个byte开始的,而不是从第0个byte开始。
细节2,虽然28个字节代表一个汉字,但实际上是30个字节,即后两个字节不用。所以读取的时候,要每30个字节读,而非每28个!

对于游戏开始时,自定义诸侯的汉字名字,索引是从0XD8F0到0XD8F5,这个索引在将领的名字区域。字模信息则是从name.16p中读取,并在游戏保存时,把字模信息存储到游戏存档的0X7778处。同理,也是28个字节代表一个汉字。

这样,代码分别是:
        # 自定义将领的汉字处理
        if index in [0xD8F0, 0xD8F1, 0xD8F2, 0xD8F3, 0xD8F4, 0xD8F5]:
            index2 = (index - 0xD8F0) * 28
            one = Data.BUF[0x7778 + index2 + 0x38:0x7778 + index2 + 28 + 0x38]
        else:
        #标准汉字的处理
            index2 = 30 * int(str(index).strip()) + 2
            one = Data.MSG16P[index2:index2 + 28]

这里的one,是指一个汉字对应的28bytes的字模信息。

那么这个字模的绘制,很简单:
        for i in range(0, 14):
            left = one[2 * i]
            right = one[2 * i + 1]

            for j in range(0, 8):
                if (left & (0x80 >> j)):
                    bmp.set_at((x + j, y + i), Helper.Palettes[palette_no])
                if (right & (0x80 >> j)):
                    bmp.set_at((x + j + 8, y + i), Helper.Palettes[palette_no])

[ 本帖最后由 juqiang 于 2024-7-26 16:50 编辑 ]
顶部
性别:未知-离线 likelove

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 36
编号 56074
注册 2005-12-27


发表于 2024-7-27 11:22 资料 短消息 只看该作者
动态跟踪用vm+softice. 缺点是有浮点指令时不好用
顶部
性别:未知-离线 juqiang
(方枪枪)

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 546898
注册 2024-7-23


发表于 2024-7-27 16:58 资料 短消息 只看该作者
回复 #7 likelove 的帖子

以前一直在dosbox里面弄,softice装上去只要ctrl+d就玩玩儿。所以我一直是debug.exe+ida这俩联合着用。
前段时间搞了一个工控机,装了dos系统,有空时用softice调试。

其实对我最困惑的,就是EGA的端口操作。我自己模拟了一套16位寄存器操作,然后无脑的把显存操作模拟了一遍。
顶部

正在浏览此帖的会员 - 共 1 人在线




当前时区 GMT+8, 现在时间是 2024-9-8 07:59
京ICP备2023018092号 轩辕春秋 2003-2023 www.xycq.org.cn

Powered by Discuz! 5.0.0 2001-2006 Comsenz Inc.
Processed in 0.010801 second(s), 9 queries , Gzip enabled

清除 Cookies - 联系我们 - 轩辕春秋 - Archiver - WAP