标题: DOS版三国志英杰传的研究心得——叁, 部队在战场的数据
性别:未知-离线 漫漫苦短

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


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

想必大家都曾经看过下面的分析,说实话迫切地想弄清楚下面内容在代码中的具体细节也是我当初想进行反汇编研究的研究方向之一,这一章内容就是在龙吟前辈在写出下面分析之后,时隔20余年之后再次让它们展示出来。

QUOTE:
偏移+00  占2字节  人物代码(刘备=0000,关羽=0001)
偏移+02  占1字节  战场代码(00=我军第1只部队、0E=我军第15只部队,0F=敌军第1只部队)
      (注:00-0E是我军,共15个位置,但不一定用完,0F-2C是敌军,共30个位置,但不一定用完)
偏移+03  占1字节  部队横坐标(以左上角为0,0)
偏移+04  占1字节  部队纵坐标
偏移+05  占1字节  仇人代码(FF表示没有仇人)
偏移+06  占2字节  目标横纵坐标(0000表示没有目标)
偏移+08  占1字节  不明
偏移+09  占1字节  部队撤退标志(00=消失 01=未出场 02=正常 04=撤退)
偏移+0A  占1字节  部队状态(从低位开始:第2位为1表示混乱,第3位为1表示全屏移动,第6位为0表示伏兵,第8位为1表示行动结束)
偏移+0B  占1字节  部队AI类型(见后)
偏移+0C  占1字节  部队士气
偏移+0D  占1字节  部队策略值

根据前面的分析,不难看出这也是存储在DS段的一块内存空间,可是龙吟前辈并没有写出这块内存空间的具体地址,还有空间究竟有多大,还有这些偏移以及数据的内容是如何得出的,这章就简单来分析一下这些内容。


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

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


发表于 2025-2-22 19:19 资料 短消息 只看该作者
一、战场数据所在位置

这块地址的起始地址是DS: D076,每只在战场上的部队使用14字节的大小,一共45只部队,所以一共是14*45=630字节大小,从DS: D076到DS: D2EB这部分长达0x276大小的空间,存储的都是上面龙吟分析的内容。
接下来我们简要把上述的数据内容进行初步解释,比如说D076, 14, 45这样的数据都是怎么来的。
如何证明是上述的内容保存在D076是不容易的,因为需要花费大量的时间进行研究,而我们可以推测以DS: D076为起始,由此往后的0x276的大小的内存空间被占用,这是目前能分析出来的。
在IDAPro搜索常量(按下Alt+I)0xD076,可以在其中看到Instruction这一列代码中包含D076的代码,出现的比较频繁的语句一条语句是add ax, 0D076h。


图片附件: 搜索常量.png (2025-2-22 19:44, 7.47 K)




图片附件: 搜索结果.png (2025-2-22 19:44, 26.86 K)


我们在Address这一列中找到seg002:774B,点击就会弹到下面代码的位置。

seg002:7742 C6 46 FF 00                    mov     [bp+var_1], 0                           ;var_1=0...2C
seg002:7746                             loc_34666:                                                 ; CODE XREF: sub_3460C+98↓j
seg002:7746 B0 0E                          mov     al, 0Eh
seg002:7748 F6 66 FF                       mul     [bp+var_1]                              ; 部队战场代码
seg002:774B 05 76 D0                       add     ax, 0D076h
...
seg002:777D FE 46 FF                       inc     [bp+var_1]
seg002:7780 80 7E FF 2D                    cmp     [bp+var_1], 2Dh
seg002:7784 72 C0                          jb      short _for_loc_34666

从中不难看出这段代码是印证龙吟的部分分析,var_1代表循环的战场代码(0...2Ch)共45个战场代码,而从中的mov al, 0Eh可以看出,每个战场代码需要占用14字节大小的空间,而add ax, 0D076h表示这些数据都是从D076开始的。
seg002:7746 — seg002:774B就是其中计算部队战场数据的首地址的过程,可以列2个16进制的公式:
部队战场数据的首地址 = D076+部队战场代码*E
部队战场数据占用地址 = (D076+部队战场代码*E — D076+部队战场代码*E+D)
根据计算,部队战场代码为0部队占用地址是(D076 — D083 ),部队战场代码为1部队占用地址(D084 — D091 ),部队战场代码为F部队数据的首地址为D148,而部队战场代码为C部队占用地址(D2DE— D2EB ),可能有人会想要扩容其中的数据,但是DS: D075以及前面的地址和DS: D2EC以及后面的地址都保存数据,如果想扩容需要把其中的数据进行“搬移”,而且所需要修改的代码量相当大,并且有可能会有遗漏并且出现BUG。

[ 本帖最后由 漫漫苦短 于 2025-2-22 19:49 编辑 ]


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

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


发表于 2025-2-25 19:19 资料 短消息 只看该作者
二、战场数据的简单方法(一)

第一节说明了在D076到D2EB之间的14*45字节的内容保存了其中部队在战场上的一些数据,并且通过各自的战场代码可以找到其占用地址以及连续的14字节,而龙吟前辈已经将这14字节对应的含义列举出来了,那么先看看这些数据是怎么被使用的,也就是它们的一些方法。

首先最简单的,就是每一项数据需要拿到(Get)和设置(Set),下列介绍部分数据的这两个方法。

以下未说明,arg_0的值都是以D076为首项,D(14)为公差,2D(45)为项数的一个十六进制下的等差数列中的一个地址值,就是说arg_0的值并不是唯一的,不像前一章介绍的CF6A,它的值可以有变动,但是这些函数的意义都是类似的。

部队仇人代码的两个方法

seg002:93A6                sub_362C6 proc far
seg002:93A6                arg_0           = word ptr  6
seg002:93A6
seg002:93A6 55                             push    bp
seg002:93A7 8B EC                          mov     bp, sp
seg002:93A9 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:93AC 8A 47 05                       mov     al, [bx+5]
seg002:93AF C9                             leave
seg002:93B0 CA 02 00                       retf    2
seg002:93B0                sub_362C6 endp

seg002:93B4                sub_362D4 proc far
seg002:93B4
seg002:93B4                arg_0           = word ptr  6
seg002:93B4                arg_2           = byte ptr  8
seg002:93B4
seg002:93B4 55                             push    bp
seg002:93B5 8B EC                          mov     bp, sp
seg002:93B7 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:93BA 8A 46 08                       mov     al, [bp+arg_2]
seg002:93BD 88 47 05                       mov     [bx+5], al
seg002:93C0 C9                             leave
seg002:93C1 CA 04 00                       retf    4
seg002:93C1                sub_362D4 endp

部队撤退标志的两个方法

seg002:40D8                sub_30FF8 proc far
seg002:40D8
seg002:40D8                arg_0           = word ptr  6
seg002:40D8                arg_2           = byte ptr  8
seg002:40D8
seg002:40D8 55                             push    bp
seg002:40D9 8B EC                          mov     bp, sp
seg002:40DB 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:40DE 8A 46 08                       mov     al, [bp+arg_2]
seg002:40E1 88 47 09                       mov     [bx+9], al
seg002:40E4 C9                             leave
seg002:40E5 CA 04 00                       retf    4
seg002:40E5                sub_30FF8 endp

seg002:40E8                sub_31008 proc far
seg002:40E8                arg_0           = word ptr  6
seg002:40E8
seg002:40E8 55                             push    bp
seg002:40E9 8B EC                          mov     bp, sp
seg002:40EB 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:40EE 8A 47 09                       mov     al, [bx+9]
seg002:40F1 C9                             leave
seg002:40F2 CA 02 00                       retf    2
seg002:40F2                sub_31008 endp

部队AI类型的两个方法

seg002:410E                sub_3102E proc far
seg002:410E
seg002:410E                arg_0           = word ptr  6
seg002:410E
seg002:410E 55                             push    bp
seg002:410F 8B EC                          mov     bp, sp
seg002:4111 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:4114 8A 47 0B                       mov     al, [bx+0Bh]
seg002:4117 C9                             leave
seg002:4118 CA 02 00                       retf    2
seg002:4118                sub_3102E endp

seg002:7C4A                sub_34B6A proc far
seg002:7C4A
seg002:7C4A                arg_0= word ptr  6
seg002:7C4A                arg_2= byte ptr  8                       ;部队AI类型
seg002:7C4A
seg002:7C4A 55                             push    bp
seg002:7C4B 8B EC                          mov     bp, sp
seg002:7C4D 80 7E 08 07                    cmp     [bp+arg_2], 7
seg002:7C51 77 09                          ja      short locret_34B7C             ;arg_2大于7,是不合法的AI类型,跳出函数
seg002:7C53 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:7C56 8A 46 08                       mov     al, [bp+arg_2]
seg002:7C59 88 47 0B                       mov     [bx+0Bh], al
seg002:7C5C
seg002:7C5C                locret_34B7C:                                                   ; CODE XREF: sub_34B6A+7↑j
seg002:7C5C C9                             leave
seg002:7C5D CA 04 00                       retf    4
seg002:7C5D                sub_34B6A endp

部队策略值一个方法,还有两个方法,一个是计算最大策略值,就是导致94智力值的部队到99级策略值为0的那个,还有一个是补充策略值的方法,这两个方法到时候再介绍。

[code]
seg002:406E                sub_30F8E proc far
seg002:406E
seg002:406E                arg_0           = word ptr  6
seg002:406E                arg_2           = byte ptr  8
seg002:406E
seg002:406E 55                             push    bp
seg002:406F 8B EC                          mov     bp, sp
seg002:4071 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:4074 8A 46 08                       mov     al, [bp+arg_2]
seg002:4077 88 47 0D                       mov     [bx+0Dh], al
seg002:407A C9                             leave
seg002:407B CA 04 00                       retf    4
seg002:407B                sub_30F8E endp

很神奇的一点是,我暂时没有找到并没有部队策略值的Get方法,因为在程序的代码中都是直接获取的,并没有单独构造出一个函数,见以下两个例子。

seg002:8FF2 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:8FF5 8A 47 0D                       mov     al, [bx+0Dh]



seg002:A4D2 8B 76 06                       mov     si, [bp+arg_0]
...
seg002:A510 8A 44 0D                       mov     al, [_部队战场数据的首地址_si+0Dh]

部队战场坐标的Get方法,准确来说是取一个偏移地址。

seg002:409C                sub_30FBC proc far                               ; CODE XREF: _InitBattle_sub_2FB7A+25B↑P ...
seg002:409C
seg002:409C                arg_0           = word ptr  6
seg002:409C
seg002:409C 55                             push    bp
seg002:409D 8B EC                          mov     bp, sp
seg002:409F 8B 46 06                       mov     ax, [bp+arg_0]
seg002:40A2 05 03 00                       add     ax, 3
seg002:40A5 C9                             leave
seg002:40A6 CA 02 00                       retf    2
seg002:40A6                sub_30FBC endp

然后在函数外获得这个地址保存的具体坐标值数据,见以下两个例子

seg002:2EB2 68 76 D0                       push    0D076h
seg002:2EB5 9A 9C 40 F2 2C                 call    sub_30FBC                ; 获取我军主将战场坐标偏移地址
seg002:2EBA 8B D8                          mov     bx, ax
seg002:2EBC 8B 07                          mov     ax, [bx]



seg002:3008 B0 0E                          mov     al, 14
seg002:300A F6 66 FF                       mul     [bp+var_1]                  ; 部队战场代码
seg002:300D 05 76 D0                       add     ax, 0D076h
seg002:3010 89 46 F8                       mov     [bp+var_8], ax
seg002:3013 50                             push    ax
seg002:3014 9A 9C 40 F2 2C                 call    sub_30FBC                ; 获取部队横纵坐标偏移地址
seg002:3019 8B D8                          mov     bx, ax
seg002:301B 8B 07                          mov     ax, [bx]

注意D076地址放的不一定是劉備的数据,虽然这个地址战场代码是0,但是劉備也不是每一战都出,不过劉備出的每一战战场代码都是0。

[ 本帖最后由 漫漫苦短 于 2025-2-25 19:34 编辑 ]
顶部

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




当前时区 GMT+8, 现在时间是 2025-2-28 23:36
京ICP备2023018092号 轩辕春秋 2003-2023 www.xycq.org.cn

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

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