轩辕春秋文化论坛 » 步步为营 » DOS版三国志英杰传的研究心得——肆


2025-3-13 19:19 漫漫苦短
DOS版三国志英杰传的研究心得——肆

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

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

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

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

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。

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

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

[code]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 [/code]

[code]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[/code]  

seg002:6E83-seg002:6E86/seg002:6E9F-seg002:6EA2,与seg002:7746-seg002:774B的地址计算方法很相似吧,seg002:6E7F/seg002:6E9B都是获取战场数据的一个方法,可以见[url=http://xycq.org.cn/forum/viewthread.php?tid=310133#pid4097883]上一章[/url]回忆一下。

所以X对应的十六进制就是0x6816,Y对应的十六进制就是0x2C,这也是龙吟前辈曾经提到的[url=http://www.xycq.org.cn/forum/viewthread.php?tid=239493&#pid3276414]44字节[/url],对比战场数据的Y为0xE,每一个人物都差了44-14=30(0x2C-0xE)字节大小,Z相差更大,用Y×Z就可以算出人物数据占了多大的内存空间,这两块数据占用的内存大小差距相当大。

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

2025-4-14 12:35 神雕小侠
兄弟有没有看过之前龙吟前辈的分析,我感觉您的这些研究成功跟他的应该会有一些交集

2025-4-14 18:18 漫漫苦短
回复 #3 神雕小侠 的帖子

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

2025-4-19 19:19 漫漫苦短
二、人物数据的重要属性——兵种(1)

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

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

[code]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[/code]

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

[code]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[/code]

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

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

在此之前,封剑尘和孝直前辈都分别提到过——[url=http://xycq.org.cn/forum/viewthread.php?tid=109977&page=3#pid2508753]兵种顺序(及代码)[/url]、[url=http://xycq.org.cn/forum/viewthread.php?tid=172528]DOS版英杰传兵种攻防、兵力、移动力、策略修改[/url],可能大部分人只知道兵种名称,却不知对应的兵种代码,下一节就探究兵种名称和兵种代码的内在逻辑。

2025-4-23 19:19 漫漫苦短
三、人物数据的重要属性——兵种(2)

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

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

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

[code]
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
[/code]

光看这处内存数据是看不出有兵种代码对应的兵种名称,需要从原来的代码先研究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段内存的偏移地址处查看内存数据。
[code]
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                             ; "運輸隊"
[/code]

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

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

2025-4-26 19:19 漫漫苦短
四、人物数据的重要属性——兵种(3)

从前辈的总结和上一节的内容已经确定兵种代码已经对应的兵种名称,可以大致认为这个兵种代码与兵种名称的对应方式是索引方式,即先在一块地址放入兵种名称字符串的索引,再根据索引找到兵种名称字符串的地址,上一节已经找到了每个兵种名称字符串的地址(DS:0A28-DS:0A9A, MAIN.EXE文件位置0x0037BF8-0x0037C6A)以及兵种名称的索引地址(DS:0AA2-DS:0AC6, MAIN.EXE文件位置0x0037C72-0x0037C96),以下就是兵种代码以及对应的兵种名称。

[quote]00短兵
01長兵
02戰車
03弓兵
04連弩兵
05發石車
06輕騎兵
07重騎兵
08近衛隊
09山賊
0A惡賊
0B義賊
0C軍樂隊
0D猛獸兵團
0E武術家隊
0F妖術師
10異民族
11民眾
12運輸隊[/quote]

那么根据这些内容可以有怎样的修改达成改动兵种名称的位置?我总结了以下的几种方法。

[list=1]
[*]修改字符串
这一种就是最基础的修改方法,也就是直接找到MAIN.EXE文件位置0x0037BF8-0x0037C6A,直接将其中的兵种名称BIG5码进行替换即可。注意兵种名称的字符串长度可以改短,比如可以将"猛獸兵團"改为"猛獸團",改动后DS段内存空间数据变化如下所示。

[code]dseg:0A75 B2 72 C3 7E B9 CE 00       asc_41245       text "BIG5", '猛獸團',0                            ; DATA XREF: dseg:0ABC↓o
dseg:0A7C 00                             db    0
dseg:0A7D 00                             db    0[/code]

在这个例子中,将字符串长度为4的"猛獸兵團"改为字符串长度为3的"猛獸團"并且在dseg:0A7B也就是字符串的末尾修改为00表示字符串的结尾,不然如果只是将"兵"的BIG5码修改为"團",则会显示"猛獸團團"。同时修改后DS:0A7C和DS:0A7D这两个字节被"浪费"了,不过这两个字节可以重新利用,见下面的几个修改。

[*]修改索引
上文提到了,代码是根据索引对应的兵种名称,那么有没有可能实现改动索引改变兵种代码对应的兵种名称,来试试这个例子。
将DS:0AA2的28改为2D,DS:0AA4的2D改为28,如下所示DS段内存的变动。
[code]dseg:0A28 B5 75 A7 4C 00             asc_411F8       text "BIG5", '短兵',0                             ; DATA XREF: dseg:0AA4↓o
dseg:0A2D AA F8 A7 4C 00             asc_411FD       text "BIG5", '長兵',0                             ; DATA XREF: dseg:0AA2↓o
...
dseg:0AA2 2D 0A                                      dw offset asc_411FD                             ; "長兵"
dseg:0AA4 28 0A                                      dw offset asc_411F8                             ; "短兵"[/code]

可以看到DS:0A28的兵种名称依然是"短兵",DS:0A2D的兵种名称依然是"長兵",但是DS:0AA2的字符串索引对象变成了"長兵",DS:0AA4的字符串索引对象变成了"短兵",在游戏中本来兵种代码为00短兵的部队显示的兵种名称却是"長兵",本来兵种代码为01長兵的部队显示的兵种名称却是"短兵",这样的修改只会修改兵种代码对应的兵种名称,虽然游戏中一支显示为"長兵"的部队连火龙都不会用,无法斜向攻击,一支显示为"短兵"的部队却手持长枪,还能可以斜向攻击。

那么这种修改方式是否有其他的尝试,比如说将0C軍樂隊和0E武術家隊的索引对调,如下所示DS段内存的变动。

[code]dseg:0A6E AD 78 BC D6 B6 A4 00       asc_4123E       text "BIG5", '軍樂隊',0                            ; DATA XREF: dseg:0ABE↓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:0ABA↓o
...
dseg:0ABA 7E 0A                                      dw offset asc_4124E                             ; "武術家隊"
dseg:0ABC 75 0A                                      dw offset asc_41245                             ; "猛獸兵團"
dseg:0ABE 6E 0A                                      dw offset asc_4123E                             ; "軍樂隊"[/code]

根据上个例子,修改索引只会修改兵种代码对应的兵种名称,并不会修改兵种的性质。在这样的改动下,一支敲鼓的部队却叫"武術家隊",还是一支攻防弱血不多的部队,只能让一群人聚到它的四周回复策略,一支手持大刀的部队对外宣称自己叫"軍樂隊",然而是一支攻防强血厚的部队,砍人反击烧梅花五样样精通,这个场景颇有滑稽的感觉。

这个改动还有另一个特点,就是成功实现了兵种代码为0C軍樂隊这个兵种名称的扩容,普通的修改方式兵种代码为0C的部队修改名称只能保持字符串长度不大于3,如果强行增加字符串长度到4,会导致0D猛獸兵團显示异常,大部分修改者一时间找不到解决的方式只能得出了不能修改字符串长度这条规则,如果知道修改索引的方式,就不会无疾而终了。

修改索引之后,可以修改DS:0A6E和DS:0A7E的文字内容了,比如将DS:0A6E的"軍樂隊"修改为"武術隊",DS:0A7E的"武術家隊"修改为"軍鼓樂隊",就不会出现显示异常了,兵种代码为0C的部队名称修改成功由"軍樂隊"修改为"軍鼓樂隊",兵种代码为0E的部队名称修改成功由"武術家隊"修改为"武術隊",这里就不再演示了。

当然也可以举一反三地尝试其他的索引方式的修改。

[*]修改字符串和索引(字符串长度变化)
上个例子已经实现了字符串长度的变化,但是注意到原始的兵种名称只有0D猛獸兵團和0E武術家隊两个兵种名称的字符串长度为4,上个例子并不能在保留这两个兵种名称的字符串长度不变的情况下实现增加一个兵种名称的字符串长度为4甚至再增加一个,那么就需要本方法来实现了。

[*]一段文字两条索引
这种修改方法需要一定的巧合。

[/list]

当然方法远不止上述的几种,还可以使用MAIN.EXE文件中代码中DS段未使用的文件空间(MAIN.EXE文件位置0x00371D0以后的位置,有多少地方不确定程序是否使用),需要将MAIN.EXE文件中的地址对应DS段位置,这个就比较复杂了。

[color=Silver][[i] 本帖最后由 漫漫苦短 于 2025-4-26 23:51 编辑 [/i]][/color]

2025-4-29 06:22 哆子颜
楼主还是在用反汇编啊,现在有些工具都可以转化成C语言编程了

2025-4-29 20:40 漫漫苦短
回复 #8 哆子颜 的帖子

你说的工具是什么?我目前用的是IDAPro,其实是有反汇编转成C语言功能的,限于32位和64位程序才能使用,不过DOS版三国志英杰传是16位程序,IDAPro并不能将其转成C语言,可能需要其他办法。你如果有其他思路也可以说一下。

还有另外一点,工具也不是万能的,就算把反汇编出来的汇编语句交给ChatGPT和DeepSeek这些AI工具,它也只能转换少量的语句对应C语言,而且没有把最重要的逻辑呈现出来,甚至不一定比自己理解的好。最重要的是其实反汇编研究虽然难度大且"痛苦",但其实就我本人而言我是乐在其中的,感受到自己在其中分析研究后的收获很大,不只是三国志英杰传和反汇编的知识积累,解决问题的能力有所提升,也会让自己更有信心面对这样的问题。

即使换了个超级智能的AI软件能把上述问题解决,把整个游戏给分析研究透彻,但是并不能替代人的思考过程,而且要是这个研究简单到不用思考也只会感觉索然无味而已。就假如有个AI软件直接把全兵种战后2999的规划以及每关每回合行动直接草拟出来,月亮这样的大神会放弃自己思考如何规划以及每关的布阵思路吗?其中这是同一个道理。再想想英杰传前辈,当时没有现在这么强的工具,战前2099到2699的战绩以及打法规划的进步还不是前辈们前赴后继一步一个脚印走出来的吗?

[color=Silver][[i] 本帖最后由 漫漫苦短 于 2025-4-29 20:59 编辑 [/i]][/color]

2025-5-1 19:19 漫漫苦短
五、人物数据的重要属性——兵种(4)

本节介绍部队兵种的衍生的方法,计算部队移动力、获取兵种移动类型、获取兵种攻击范围类型这三个方法,之所以称它们为衍生的方法就是因为这三个方法都是先得出部队的兵种,再根据兵种得出对应的类型。

sub_33D26函数是计算部队移动力的方法,arg_0为部队战场数据的首地址。

[code]seg002:6E06                sub_33D26 proc far
seg002:6E06
seg002:6E06                var_A           = word ptr -0Ah
seg002:6E06                var_7           = byte ptr -7
seg002:6E06                var_4           = byte ptr -4
seg002:6E06                var_2           = byte ptr -2
seg002:6E06                var_1           = byte ptr -1
seg002:6E06                arg_0           = word ptr  6
seg002:6E06
seg002:6E06 C8 0A 00 00                    enter   0Ah, 0
seg002:6E0A 56                             push    si
seg002:6E0B 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:6E0E 6B 37 2C                       imul    si, [bx], 44
seg002:6E11 81 C6 16 68                    add     si, 6816h
seg002:6E15 56                             push    si
seg002:6E16 9A CC 6A F6 1C                 call    sub_23A2C                 ; 获取部队兵种代码
seg002:6E1B 8A D8                          mov     bl, al
seg002:6E1D 2A FF                          sub     bh, bh
seg002:6E1F 8A 87 EA 31                    mov     al, [bx+31EAh]                          ; 获取兵种移动力
seg002:6E23 88 46 F9                       mov     [bp+var_7], al
[/code]

看到了熟悉的[bx+xxxxh]的汇编代码语句,于是到DS:31EA处寻找数据。

[code]dseg:31EA 04                 db    4                             ; 00短兵 4 
dseg:31EB 04                 db    4                             ; 01长兵 4
dseg:31EC 05                 db    5                             ; 02战车 5
dseg:31ED 04                 db    4                             ; 03弓兵 4
dseg:31EE 04                 db    4                             ; 04连弩兵 4
dseg:31EF 03                 db    3                             ; 05投石车 3
dseg:31F0 06                 db    6                             ; 06轻骑兵 6
dseg:31F1 05                 db    5                             ; 07重骑兵 5
dseg:31F2 06                 db    6                             ; 08近卫队 6
dseg:31F3 04                 db    4                             ; 09山贼 4
dseg:31F4 04                 db    4                             ; 0A恶贼 4
dseg:31F5 04                 db    4                             ; 0B义贼 4
dseg:31F6 04                 db    4                             ; 0C军乐队 4
dseg:31F7 04                 db    4                             ; 0D猛兽兵团 4
dseg:31F8 05                 db    5                             ; 0E武术家 5
dseg:31F9 04                 db    4                             ; 0F妖术师 4
dseg:31FA 05                 db    5                             ; 10异民族 5
dseg:31FB 03                 db    3                             ; 11民众 3
dseg:31FC 03                 db    3                             ; 12运输队 3[/code]

seg002:6E26-seg002:6E6B是计算道具带来的移动力加成的计算并且保存到var_4中,由于涉及到道具的相关知识,本处省略其中的计算过程。

[code]seg002:6E6D 8A 46 FC                       mov     al, [bp+var_4]                          ; 获取道具加成移动力
seg002:6E70 02 46 F9                       add     al, [bp+var_7]                          ; 与兵种移动力相加即为部队最终的移动力
seg002:6E73 5E                             pop     si
seg002:6E74 C9                             leave
seg002:6E75 CA 02 00                       retf    2
seg002:6E75                sub_33D26 endp[/code]

兵种移动类型,这个在番外1也出现过,现在正式介绍sub_342B4这个函数,arg_0为部队战场数据的首地址。

[code]seg002:BBEF 56                             push    si
seg002:BBF0 9A 94 73 F2 2C                 call    sub_342B4
seg002:BBF5 88 46 F4                       mov     [bp+var_C], al[/code]

[code]seg002:7394                sub_342B4 proc far
seg002:7394
seg002:7394                arg_0           = word ptr  6
seg002:7394
seg002:7394 55                             push    bp
seg002:7395 8B EC                          mov     bp, sp
seg002:7397 FF 76 06                       push    [bp+arg_0]
seg002:739A 9A 6C 93 F2 2C                 call    sub_3628C                ; 获取部队兵种代码
seg002:739F 8A D8                          mov     bl, al
seg002:73A1 2A FF                          sub     bh, bh
seg002:73A3 8A 87 56 32                    mov     al, [bx+3256h]                          ; 兵种移动类型
seg002:73A7 C9                             leave
seg002:73A8 CA 02 00                       retf    2
seg002:73A8                sub_342B4 endp
[/code]
还是到DS:3256处寻找数据。

[code]dseg:3256 00                 db    0                             ; 00短兵 
dseg:3257 00                 db    0                             ; 01长兵
dseg:3258 00                 db    0                             ; 02战车
dseg:3259 00                 db    0                             ; 03弓兵
dseg:325A 00                 db    0                             ; 04连弩兵
dseg:325B 00                 db    0                             ; 05投石车
dseg:325C 01                 db    1                             ; 06轻骑兵
dseg:325D 01                 db    1                             ; 07重骑兵
dseg:325E 01                 db    1                             ; 08近卫队
dseg:325F 03                 db    3                             ; 09山贼
dseg:3260 03                 db    3                             ; 0A恶贼
dseg:3261 03                 db    3                             ; 0B义贼
dseg:3262 02                 db    2                             ; 0C军乐队
dseg:3263 03                 db    3                             ; 0D猛兽兵团
dseg:3264 03                 db    3                             ; 0E武术家
dseg:3265 00                 db    0                             ; 0F妖术师
dseg:3266 03                 db    3                             ; 10异民族
dseg:3267 00                 db    0                             ; 11民众
dseg:3268 02                 db    2                             ; 12运输队[/code]

0类型的兵种有00短兵01长兵02战车03弓兵04连弩兵05投石车0F妖术师11民众,1类型的兵种有06轻骑兵07重骑兵08近卫队,2类型的兵种有0C军乐队12运输队,3类型的兵种有09山贼0A恶贼0B义贼0D猛兽兵团0E武术家10异民族,根据英杰传的常识可以分析出,0类型的兵种移动类型为无法进入山地等等特点,1类型的兵种移动类型为无法进入森林山地以及荒地移动为2等等,2类型的兵种移动类型为无法进入山地以及森林荒地移动为2等等,3类型的兵种移动类型为就比012全能,可以进入山地等等。

获取兵种攻击范围类型这个方法都是融合在函数中的,也可以理解为程序员在定义函数的时候设置为inline函数,于是在转成汇编后就没有单独成为一个函数(这只是本人的脑补),好在使用到的地方不多,修改的话也没有那么麻烦。

[code]seg002:B4B4                sub_383D4       proc far                                        ; CODE XREF: sub_38F66+8A↓P
seg002:B4B4
seg002:B4B4                var_A= word ptr -0Ah
seg002:B4B4                var_8           = byte ptr -8
seg002:B4B4                var_7           = byte ptr -7
seg002:B4B4                var_6           = word ptr -6
seg002:B4B4                var_4           = byte ptr -4
seg002:B4B4                var_2           = byte ptr -2
seg002:B4B4                var_1= byte ptr -1
seg002:B4B4                arg_0= word ptr  6
seg002:B4B4
seg002:B4B4 C8 0A 00 00                    enter   0Ah, 0
seg002:B4B8 57                             push    di
seg002:B4B9 56                             push    si
seg002:B4BA 8B 76 06                       mov     si, [bp+arg_0]
seg002:B4BD 56                             push    si
seg002:B4BE 9A 6C 93 F2 2C                 call    sub_3628C                ; 获取部队兵种代码
seg002:B4C3 8A D8                          mov     bl, al
seg002:B4C5 2A FF                          sub     bh, bh
seg002:B4C7 8A 87 42 32                    mov     al, [bx+3242h]                          ; 兵种攻击范围类型[/code]

2025-5-1 22:40 神雕小侠
回复 #9 漫漫苦短 的帖子

嗯嗯,16位程序无法转换成C语言,这个是关键问题

页: [1]


Powered by Discuz! Archiver 5.0.0  © 2001-2006 Comsenz Inc.