春节假的最后一天,抽出了一点时间,写一下之前分析一半弃坑的内容,大富翁 3 中的鼠标指针
本系列已完结,以下为整理目录方便查阅
鼠标指针的文件集合,同样符合 MKF 文件的打包规则,一共可以拆分出 14 组 smkf,smkf 又可以进一步拆分成多个文件,因为有些指针是有动画效果的
不过分析文件结构时遇到了些困难
从03开始是第一个鼠标指针,有了以前的经验,我们不难猜出 03
是平面宽度,1e
是平面高度,这样的话,鼠标的尺寸为 24 x 30
,但是后面的就不太好猜了,我们基本可以看出,每 15
个字节为一组,那么这可能是 3
个 byte 的 mask 外加 12
个 byte 的四平面数据,鼠标的尺寸这么小,应该不会有像前景图片一样的压缩算法,而且数据排列很规则,应该是原始的图片,不过到底哪几个 byte 是 mask,哪几个数据是图像,这就很难猜了,不过还好,现在我们会用 dosbox debugger 看汇编了。使用 debugger 的方法这次我会讲得粗略一点,控制一下篇幅。
老样子,我们可以使用 bpint 21 3f
,在读取文件时中断,然后使用 d ds:dx
来查看程序读取的文件内容,由于我们这次只能使用 rich3.exe 而不是 ss.exe,前者打开的文件更多,需要有些耐心,中间你会看到一个比较明显的文件特征是 ‘creative voice file’,这是音频文件的内容,鼠标还在这之后,注意 DS 值的变化,看到程序读取到 MOUSE.MKF 后,我们注意到文件内容被放到 DS = 9117
的位置。
放到 9117
后,这部分内容一直都没有变化,那么我们推测,基本上程序也是直接把这部分程序写入显存,接下来用断点看就比较累了,我们直接 dump 一段程序运行的 CPU 日志,来查找 9117
是如何写入 A000
的,还记得 A000
吗,对,显存的起始位置。
我们用 klogg 打开 logcpu.txt,直接搜索 DS:9117 ES:A000
首先我们看到
0003
,放到 ax
中001e
,放到 ax
中我们知道,这是第一个鼠标指针的平面宽和平面高,那么程序就是从这里开始要把鼠标画上屏幕的。
这里面还有个细节,就是程序把宽和高存储到了 cs:0002
和 cs:0004
,这里 cs
应该是 381c
,后面我们会用到,我们继续看下去。
cs:[0160~0162]
写入了 9117:[0010~0012]
的内容,我们可以知道写入的是 03FFFF
cs:[01b0~01b2]
写入了 9117:[0013~0015]
的内容,我们可以知道写入的是 000000
di
增加了 0x50
,cs:[0200~0202]
写入了 9117:[0016~0018]
的内容,我们可以知道写入的是 000000
,同时注意到 0385 行的 loop
,这个 loop
会执行四次,每次差别只有 di
增加 0x50
,都是复制 3
个 字节,为什么增加 0x50
,其实也很好理解,0x50 = 80,80 * 8 = 640,符合屏幕的宽度,正好是换了一行。
385 的 loop
后,很快我们看到了 程序执行了 38A 的函数,这个函数里我们也看到老朋友了:
out 03ce, 0004
out 03c4, 0102
我们之前有谈到过这两个
指令 | 说明 |
---|---|
3ce xx05 |
写入模式xx |
3c4 xx02 |
按位选择平面xx |
out 03ce, 0004
虽然具体是什么我们还不清楚,不过我们知道接下来就要把数据复制到显存了,复制的位置在 3ed 行,我们看复制前的操作:
es:di (A000:2670)
的值复制到 al
al
和 cs:0160 (3F)
执行与操作al
和 cs:01B0 (00)
执行或操作al
写入 es:di
简单总结的话,这个步骤相当于先取出原始位置的图像,与 3F 执行与操作,与 00 执行或操作,然后写回原位,通过上文我们可以知道 3F 和 00 是 哪里的数据,我们来标记一下它们的位置
这个 loop 执行了 3 次,差别在于 si 和 bx 自增 1,即
A000:2671
取出,与 381C:0161
,或 381C:01B1
,写回 A000:2671
A000:2672
取出,与 381C:0162
,或 381C:01B2
,写回 A000:2671
接下来程序又回到了 39A 和 3A2,有一点需要我们留意,3F3 行,bx
自增了 0x50
out 03ce, 0104
out 03c4, 0202
我们也轻车熟路了,换平面。然后后续的操作对比之前也不难看出
es:di (A000:2670)
的值复制到 al
al
和 cs:0160 (3F)
执行与操作al
和 cs:0200 (00)
执行或操作al
写入 es:di
与之前也是相同,差别就在于 bx
增加了 0x50
,后续这个循环也会执行三次,来写入 2671
和 2672
。
好了,到这里我们基本可以知道鼠标指针的基本结构了
长度 | 说明 |
---|---|
2 | 平面宽度 |
2 | 平面高度 |
平面宽度 | 第一行 mask |
平面宽度 | 第一行 第一平面数据 |
平面宽度 | 第一行 第二平面数据 |
平面宽度 | 第一行 第三平面数据 |
平面宽度 | 第一行 第四平面数据 |
后面就是每行的重复,我们来画个小天使庆祝一下吧