标题: DOS版三国志英杰传的研究心得——肆, 另一块记录人物数据的地址
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 47
编号 545816
注册 2023-12-25


发表于 2025-3-13 19:19 资料 短消息 只看该作者
DOS版三国志英杰传的研究心得——肆

上一章简单分析了部队在战场上的数据,先回顾一下。

QUOTE:
以D076为首项,D(14)为公差,2D(45)为项数的一个十六进制下的等差数列中的一个地址值

按照上一章的分析这其中记录了人物代码、战场代码、仇人代码、撤退标志、AI类型、士气值、策略值等等信息,但显然,这不能完全概述其中的任一人物的全部信息,例如一个人物的等级经验兵种、还有武力智力统御力这三个一直被玩家拿来比较综合素质的数值,还有这个部队的名字等等信息,这些都没有在部队战场上的数据中,但实际上又不能不在战场上使用,例如部队在战场使用策略获得经验升级,不能不依靠等级经验兵种,部队也需要用武力智力统御力这三者计算在战场上的攻击力防御力和策略值的数据,那么这些数据又是记录在哪里?

可以想象应该是有新的一块数据是记录于此的,这块数据记为人物的固有数据(虽然等级经验兵种等等这些数据都是有变动的),如果依照部队在战场上的数据记录的特点,那么每一个人物的都是有对应的一个的首地址,也就是说这些地址的应该也是以X为首项,Y为公差,Z为项数的一个十六进制下的等差数列中的一个地址值。那么XYZ对应的十六进制值究竟是什么?


顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 47
编号 545816
注册 2023-12-25


发表于 2025-4-13 19:19 资料 短消息 只看该作者
一、人物数据所在位置

根据上一章的内容,战场数据共630字节大小,并且分成14×45字节,战场数据这块上最多只能存下45个人物的数据,但英杰传的人物远远不止45个,实际上在BAKDATA.R3中一共保存了384个人物的初始数据(包括以兵种命名的部队或只在剧情中才出现的人物),也就是说在DS段还有一块能保存384个人物数据的内存空间,而对于每个人物都对应唯一的人物代码。

并且上一章还粗略分析了每只部队战场数据的各个偏移的含义,偏移+0的就是保存2字节大小的人物代码,换句话说,就是每只部队战场数据偏移+0的人物代码的来源就是人物数据所在内存空间对应人物代码,而这人物代码的分配也是和每只部队战场数据偏移+2的战场代码一样是按照顺序进行分配了,但在人物数据这部分的人物代码并不是从0开始,而是从0x200(512)开始的,而战场数据的那块数据的人物代码确实是从0开始的,也就是说,人物数据的人物代码 = 战场数据的人物代码 + 0x200(512),至于具体的细节后文再解析。

扯远了,本章开头的XYZ对应的值是什么才是本节的重点,Z其实已经透露出来了,就是0x180(384),而对应的人物代码的范围就是0200 - 0379,0200就是劉備,0201是关羽,0204是曹操,0208是吕布,0200 - 02FF一共256个人物都是具体姓名的人物,从0300 - 035F是以兵种命名的部队,而0360 - 0379是这部分人物就很杂乱了,比如道具屋0376,李明0377,总之一共384个。

而XY,还是在汇编中找这类似战场数据中这部分代码的,从而对应人物数据的XY。

seg002:7746 B0 0E                          mov     al, 0Eh
seg002:7748 F6 66 FF                       mul     [bp+var_1]                              ; 部队战场代码
seg002:774B 05 76 D0                       add     ax, 0D076h

在代码找到了这两个部分的汇编代码,相似度很高吧,当然只是举出两个例子,arg_0的值还是以0xD076为首项,0xD(14)为公差,0x2D(45)为项数的一个十六进制下的等差数列中的一个地址值,两个部分的汇编代码sub_22F6A和sub_23098这两个函数的具体内容这章也会大致介绍。

seg002:6E7C 8B 76 06                       mov     si, [bp+arg_0]
seg002:6E7F 8A 44 0C                       mov     al, [si+0Ch]
seg002:6E82 50                             push    ax
seg002:6E83 6B 04 2C                       imul    ax, [si], 2Ch                      ; [si]获取战场数据的首地址对应的人物代码,并且乘0x2C(44),并且传给AX
seg002:6E86 05 16 68                       add     ax, 6816h
seg002:6E89 50                             push    ax                                      ; 人物数据的首地址
seg002:6E8A 9A 0A 60 F6 1C                 call    sub_22F6A



seg002:6E98 8B 76 06                       mov     si, [bp+arg_0]
seg002:6E9B 8A 44 0C                       mov     al, [si+0Ch]
seg002:6E9E 50                             push    ax
seg002:6E9F 6B 04 2C                       imul    ax, [si], 2Ch                   ; [si]获取战场数据的首地址对应的人物代码,并且乘0x2C(44),并且传给AX
seg002:6EA2 05 16 68                       add     ax, 6816h
seg002:6EA5 50                             push    ax                                      ; 人物数据的首地址
seg002:6EA6 9A 38 61 F6 1C                 call    sub_23098

seg002:6E83-seg002:6E86/seg002:6E9F-seg002:6EA2,与seg002:7746-seg002:774B的地址计算方法很相似吧,seg002:6E7F/seg002:6E9B都是获取战场数据的一个方法,可以见上一章回忆一下。

所以X对应的十六进制就是0x6816,Y对应的十六进制就是0x2C,这也是龙吟前辈曾经提到的44字节,对比战场数据的Y为0xE,每一个人物都差了44-14=30(0x2C-0xE)字节大小,Z相差更大,用Y×Z就可以算出人物数据占了多大的内存空间,这两块数据占用的内存大小差距相当大。

总结人物数据的首地址为以0x6816为首项,0x2C(44)为公差,0x180(384)为项数的一个十六进制下的等差数列中的一个地址值。


顶部
性别:男-离线 神雕小侠

Rank: 3Rank: 3Rank: 3
组别 士兵
级别 忠义校尉
功绩 2
帖子 245
编号 44352
注册 2005-7-27


发表于 2025-4-14 12:35 资料 短消息 只看该作者
兄弟有没有看过之前龙吟前辈的分析,我感觉您的这些研究成功跟他的应该会有一些交集
顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 47
编号 545816
注册 2023-12-25


发表于 2025-4-14 18:18 资料 短消息 只看该作者
回复 #3 神雕小侠 的帖子

岂止说是有交集,龙吟前辈简直是我的领路人,我一开始就是对着他的《三国志英杰传分析结果》来分析,还有周瑜前辈的全补丁的修改方法等等资料,目前关于他们的这些研究成果大部分都找到了程序中对应的位置,不过还是有很多内容要分析的,其实我汇编分析能力也不强,也只是站在巨人的肩膀上。
顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 47
编号 545816
注册 2023-12-25


发表于 2025-4-19 19:19 资料 短消息 只看该作者
二、人物数据的重要属性——兵种(1)

毫无疑问,人物数据占用的内存空间远大于战场数据的空间,本章分析的内容的篇幅也会比上一章长,分析人物数据中的44字节数据中大部分内容代表的含义。

每个人物的人物代码都是占用2字节的,上一节已经说了战场数据和人物数据的人物代码上的联系,但我想先从其他的数据开始讲起。如果说想从这44字节中挑出最重要的一项属性来说明,我认为这项属性就是每个人物的兵种代码,兵种代码虽然只占用的1个字节,但还是至关重要的,它在偏移+020处,sub_23A2C函数就是它的Get方法,arg_0的值都是以0x6816为首项,0x2C(44)为公差,0x180(384)为项数的一个十六进制下的等差数列中的一个地址值。

seg001:6ACC                sub_23A2C proc far
seg001:6ACC
seg001:6ACC                arg_0           = word ptr  6
seg001:6ACC
seg001:6ACC 55                             push    bp
seg001:6ACD 8B EC                          mov     bp, sp
seg001:6ACF 8B 5E 06                       mov     bx, [bp+arg_0]
seg001:6AD2 8A 47 20                       mov     al, [bx+20h]
seg001:6AD5 C9                             leave
seg001:6AD6 CA 02 00                       retf    2
seg001:6AD6                sub_23A2C endp

这是根据人物数据的首地址来获取对应的兵种代码,那么有没有方法根据部队战场数据的首地址来获取对应的兵种代码,其实根据上一节的计算我们就能看出,通过人物代码,可以将战场数据的首地址转化成人物数据的首地址,于是就有了sub_3628C函数,arg_0的值都是以0xD076为首项,0xD(14)为公差,0x2D(45)为项数的一个十六进制下的等差数列中的一个地址值。

seg002:936C                sub_3628C proc far
seg002:936C
seg002:936C                arg_0           = word ptr  6
seg002:936C
seg002:936C 55                             push    bp
seg002:936D 8B EC                          mov     bp, sp
seg002:936F 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:9372 6B 07 2C                       imul    ax, [bx], 44
seg002:9375 05 16 68                       add     ax, 6816h
seg002:9378 50                             push    ax
seg002:9379 9A CC 6A F6 1C                 call    sub_23A2C
seg002:937E C9                             leave
seg002:937F CA 02 00                       retf    2
seg002:937F               sub_3628C endp

从理论上来说sub_3628C这个函数是不需要调用sub_23A2C函数,因为在seg002:9375这句就已经求出人物数据的首地址,再根据之前的seg001:6ACF-seg001:6AD2的汇编代码稍改就可以求出兵种代码。

但实际上有可能人物数据的内存空间是甲设计结构,而战场数据的内存空间又是乙设计的,数据之间的逻辑是丙等人设计的,如果说甲设计的人物数据的内存空间结构发生变化,对应乙丙等人的汇编代码有可能出现多处改动,这是没有必要的,所以最好的方式就是就是提供一个类似接口的方法,比如一个调用函数(本例的sub_23A2C),这样无论甲设计的人物数据的内存空间结构发生怎样的变化,只要甲给定一个函数,乙丙等人根据这个函数,就是实现直接获取甲设计的的人物数据其中一项属性。

在此之前,封剑尘和孝直前辈都分别提到过——兵种顺序(及代码)DOS版英杰传兵种攻防、兵力、移动力、策略修改,可能大部分人只知道兵种名称,却不知对应的兵种代码,下一节就探究兵种名称和兵种代码的内在逻辑。
顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 47
编号 545816
注册 2023-12-25


发表于 2025-4-23 19:19 资料 短消息 只看该作者
三、人物数据的重要属性——兵种(2)

上节的只知道了可以获取兵种代码,而且内存中很多数据都是与兵种代码相关的,接下来我们就开始研究兵种代码对应的汇编代码,比如说根据兵种代码确定兵种名字。

英杰传中不止一处会显示人物对应的兵种名称,显然这是和兵种代码相关的,显示对应的兵种名称,可以看下面两处的代码,arg_0都是部队战场数据的首地址。

seg002:7978 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:797B 6B 07 2C                       imul    ax, [bx], 44
seg002:797E 05 16 68                       add     ax, 6816h
seg002:7981 89 46 F0                       mov     [bp+var_10], ax
seg002:7984 53                             push    bx
seg002:7985 9A 6C 93 F2 2C                 call    sub_3628C
seg002:798A 88 46 F7                       mov     [bp+var_9], al
...
seg002:7B2A 8A 5E F7                       mov     bl, [bp+var_9]
seg002:7B2D 2A FF                          sub     bh, bh
seg002:7B2F 03 DB                          add     bx, bx
seg002:7B31 FF B7 A2 0A                    push    word ptr [bx+0AA2h]                     ; "兵种名称"的字符串地址
seg002:7B35 68 D9 33                       push    33D9h                                           ; 输出格式"%s"
seg002:7B38 68 26 51                       push    5126h
seg002:7B3B 9A 3E 0C F6 1C                 call    sub_1DB9E                   ; 打印"兵种名称"字符串
seg002:7B40 83 C4 06                       add     sp, 6



seg002:8D44 8B 76 06                       mov     si, [bp+arg_0]
seg002:8D4C 6B 3C 2C                       imul    di, [si], 44
seg002:8D4F 81 C7 16 68                    add     di, 6816h
seg002:8D53 56                             push    si
seg002:8D54 9A 6C 93 F2 2C                 call   sub_3628C
seg002:8D59 88 46 F7                       mov     [bp+var_9], al
...
seg002:8E5A 8A 5E F7                       mov     bl, [bp+var_9]
seg002:8E5D 2A FF                          sub     bh, bh
seg002:8E5F 03 DB                          add     bx, bx
seg002:8E61 FF B7 A2 0A                    push    word ptr [bx+0AA2h]                     ; "兵种名称"的字符串地址
seg002:8E65 68 3A 34                       push    343Ah                                           ; 输出格式"%s"
seg002:8E68 68 26 51                       push    5126h
seg002:8E6B 9A 3E 0C F6 1C                 call    sub_1DB9E                   ; 打印"兵种名称"字符串
seg002:8E70 83 C4 06                       add     sp, 6

seg002:7B38-seg002:7B3B/seg002:8E6B-seg002:8E6B都是同样的调用显示文字的函数,显示文字的内容是根据seg002:7B31/seg002:8E61的值来确定,这就是接下来的重点,要想知道显示文字的内容,那么我们需要找到DS段的内存偏移地址0AA2处看里面的内存数据是什么?

dseg:0AA2 28 0A
dseg:0AA4 2D 0A
dseg:0AA6 32 0A
dseg:0AA8 37 0A
dseg:0AAA 3C 0A
dseg:0AAC 43 0A
dseg:0AAE 4A 0A
dseg:0AB0 51 0A
dseg:0AB2 58 0A
dseg:0AB4 5F 0A
dseg:0AB6 64 0A
dseg:0AB8 69 0A
dseg:0ABA 6E 0A
dseg:0ABC 75 0A
dseg:0ABE 7E 0A
dseg:0AC0 87 0A
dseg:0AC2 8E 0A
dseg:0AC4 95 0A
dseg:0AC6 9A 0A

光看这处内存数据是看不出有兵种代码对应的兵种名称,需要从原来的代码先研究seg002:7B2A-seg002:7B31/seg002:8E5A-seg002:8E61的代码的意义。

seg002:7B2A/seg002:8E5A取出兵种代码,seg002:7B2F/seg002:8E5F将这个兵种代码自加(乘2),再取出DS:[兵种代码*2 + 0AA2h]的2字节值。接下来的问题就是seg002:7B31/seg002:8E61的word ptr [bx+0AA2h]这句汇编代码的意义。

当兵种代码的值从0到0x13(19)变化,word ptr [bx+0AA2h]的结果分别是0A28, 0A2D, 0A32, 0A37, 0A3C, 0A43, 0A4A, 0A51, 0A58, 0A5F, 0A64, 0A69, 0A6E, 0A75, 0A7E, 0A87, 0A8E, 0A95, 0A9A。

再从这几个值的DS段内存的偏移地址处查看内存数据。

dseg:0A28 B5 75 A7 4C 00                asc_411F8       text "BIG5", '短兵',0                             ; DATA XREF: dseg:0AA2↓o
dseg:0A2D AA F8 A7 4C 00                asc_411FD       text "BIG5", '長兵',0                             ; DATA XREF: dseg:0AA4↓o
dseg:0A32 BE D4 A8 AE 00                asc_41202       text "BIG5", '戰車',0                             ; DATA XREF: dseg:0AA6↓o
dseg:0A37 A4 7D A7 4C 00                asc_41207       text "BIG5", '弓兵',0                             ; DATA XREF: dseg:0AA8↓o
dseg:0A3C B3 73 A9 B8 A7 4C 00          asc_4120C       text "BIG5", '連弩兵',0                            ; DATA XREF: dseg:0AAA↓o
dseg:0A43 B5 6F A5 DB A8 AE 00          asc_41213       text "BIG5", '發石車',0                            ; DATA XREF: dseg:0AAC↓o
dseg:0A4A BB B4 C3 4D A7 4C 00          asc_4121A       text "BIG5", '輕騎兵',0                            ; DATA XREF: dseg:0AAE↓o
dseg:0A51 AD AB C3 4D A7 4C 00          asc_41221       text "BIG5", '重騎兵',0                            ; DATA XREF: dseg:0AB0↓o
dseg:0A58 AA F1 BD C3 B6 A4 00          asc_41228       text "BIG5", '近衛隊',0                            ; DATA XREF: dseg:0AB2↓o
dseg:0A5F A4 73 B8 E9 00                asc_4122F       text "BIG5", '山賊',0                             ; DATA XREF: dseg:0AB4↓o
dseg:0A64 B4 63 B8 E9 00                asc_41234       text "BIG5", '惡賊',0                             ; DATA XREF: dseg:0AB6↓o
dseg:0A69 B8 71 B8 E9 00                asc_41239       text "BIG5", '義賊',0                             ; DATA XREF: dseg:0AB8↓o
dseg:0A6E AD 78 BC D6 B6 A4 00          asc_4123E       text "BIG5", '軍樂隊',0                            ; DATA XREF: dseg:0ABA↓o
dseg:0A75 B2 72 C3 7E A7 4C B9 CE 00    asc_41245       text "BIG5", '猛獸兵團',0                           ; DATA XREF: dseg:0ABC↓o
dseg:0A7E AA 5A B3 4E AE 61 B6 A4 00    asc_4124E       text "BIG5", '武術家隊',0                           ; DATA XREF: dseg:0ABE↓o
dseg:0A87 A7 AF B3 4E AE 76 00          asc_41257       text "BIG5", '妖術師',0                            ; DATA XREF: dseg:0AC0↓o
dseg:0A8E B2 A7 A5 C1 B1 DA 00          asc_4125E       text "BIG5", '異民族',0                            ; DATA XREF: dseg:0AC2↓o
dseg:0A95 A5 C1 B2 B3 00                asc_41265       text "BIG5", '民眾',0                             ; DATA XREF: dseg:0AC4↓o
dseg:0A9A B9 42 BF E9 B6 A4 00          asc_4126A       text "BIG5", '運輸隊',0                            ; DATA XREF: dseg:0AC6↓o
dseg:0AA2 28 0A                          dw offset asc_411F8                             ; "短兵"
dseg:0AA4 2D 0A                          dw offset asc_411FD                             ; "長兵"
dseg:0AA6 32 0A                          dw offset asc_41202                             ; "戰車"
dseg:0AA8 37 0A                          dw offset asc_41207                             ; "弓兵"
dseg:0AAA 3C 0A                          dw offset asc_4120C                             ; "連弩兵"
dseg:0AAC 43 0A                          dw offset asc_41213                             ; "發石車"
dseg:0AAE 4A 0A                          dw offset asc_4121A                             ; "輕騎兵"
dseg:0AB0 51 0A                          dw offset asc_41221                             ; "重騎兵"
dseg:0AB2 58 0A                          dw offset asc_41228                             ; "近衛隊"
dseg:0AB4 5F 0A                          dw offset asc_4122F                             ; "山賊"
dseg:0AB6 64 0A                          dw offset asc_41234                             ; "惡賊"
dseg:0AB8 69 0A                          dw offset asc_41239                             ; "義賊"
dseg:0ABA 6E 0A                          dw offset asc_4123E                             ; "軍樂隊"
dseg:0ABC 75 0A                          dw offset asc_41245                             ; "猛獸兵團"
dseg:0ABE 7E 0A                          dw offset asc_4124E                             ; "武術家隊"
dseg:0AC0 87 0A                          dw offset asc_41257                             ; "妖術師"
dseg:0AC2 8E 0A                          dw offset asc_4125E                             ; "異民族"
dseg:0AC4 95 0A                          dw offset asc_41265                             ; "民眾"
dseg:0AC6 9A 0A                          dw offset asc_4126A                             ; "運輸隊"

此时便豁然开朗了,DS:0AA2到DS:0AC6保存的是"兵种名称"的字符串地址,而地址对应的内存空间保存的则是兵种名称的BIG5码,每个兵种名称BIG5码的末尾都是00代表字符串的末尾。
如果想直接在MAIN.EXE中修改兵种名称可以用UE等工具搜索兵种名称对应的BIG码,或者直接查找MAIN.EXE文件位置0x0037BC8处是短兵字符串dseg:0A28的位置,MAIN.EXE文件位置0x0037C72对应dseg:0AA2的位置。

很多修改MOD的爱好者都遵循一个原则,不要改动字符串的长度以免出错,然而事实真是如此吗?在修改兵种名称这块,有没有其他更好的办法?
顶部

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




当前时区 GMT+8, 现在时间是 2025-4-26 00:41
京ICP备2023018092号 轩辕春秋 2003-2023 www.xycq.org.cn

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

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