红楼梦十二金钗游戏资源分析(四)

我们离正确画出第一张图只剩下调色板了,让我们继续吧

目录

本系列已完结,以下是各章节说明,17 之前是 dos 版相关,之后是 2001 版:

  1. 背景、简单分析
  2. 显存位置
  3. 事件图保存算法: LZW
  4. 调色板
  5. MGP2
  6. 结局图
  7. 事件图中的眼睛
  8. 音频文件
  9. 按位读取
  10. 循环之前
  11. 读取循环
  12. 重现 LZW
  13. PAT 的图形格式
  14. STAFF 调色板
  15. 字体文件
  16. 脚本解密
  17. 版本比较
  18. 第一张图
  19. 调色板1
  20. 第二张图
  21. 调色板2
  22. 调色板处理
  23. 静态事件图、结局图
  24. 动态图、鉴赏模式

调色板的简易说明

我们知道每种颜色由红绿蓝三种颜色混合而成,这三种颜色值由六位二进制数字设定, 所以取值范围是 00h3fh,红绿蓝三色的数值确定后, 我们就可以知道这是什么颜色。由于程序同屏显示的最大颜色数是 256, 所以内存中一定会存储这 256 种颜色的三原色数值。

我查阅了一些 VESA 的资料,设置调色板基本还是靠调用寄存器, 但是这部分指令在日志中却没有找到, 所以我推测可能程序已经提前映射好了调色板的地址, 可以直接修改调色板而无需再调用寄存器,因为我的 cpu log 是从游戏中记录的, 而不是从头完整记录,从内存看来是不好下手了,我们看一看文件中是否有新的发现。

EVENT.PAT

我们通过搜索第一张图的读取内容,可以得知,图片保存的起始位置是 00071dh, 好巧不巧, EVENT.PAT 00033dh 附近有这样一串数字:

Untitled

因为前面有几行 00h,所以这个位置还比较明显,00071dh 正好是图片的起始位置, 而他的前面又是 3f 3f 3f,看到这里我们可以会心一笑,这是白色。

所以调色板到 033dh(不含)结束,256 色的调色板应该会占用 300h (注意是 16 进制)个字节,那么,调色板的起始位置应该是 003dh

Untitled

第一个颜色是 002100h,虽然跟我预期第一个应该是黑色 000000h(位置 003ah) 不太一致。简单扫了一下数据,没看到大于 3f 的数值, 那么这段数据应该就是调色板了,我们画一画试试:

test-origin-0x3d.png

人物是正确的,但是背景色一塌糊涂,难道调色板真的是从 003ah 开始, 虽然我不太相信,不过谨慎起见,从 003ah 画一次也不难:

test-origin-0x3a.png

这个错得就更多了,所以应该还是 003dh 开始, 而且程序要么对调色板进行了一些特殊处理,要么就是我们找错了调色板。

有个事情顺带提一下,之前在分析大富翁三时也讲过,因为现在我们表示颜色一般是用 8 位二进制显示了, 也就是 0-255,6 位颜色是 0-63,所以我们需要将 6 位颜色「放大」到 8 位, 不然画面会非常暗。

文件读取

前面也说了,直接通过寄存器的方式去查日志没有什么结果,所以这次我们搜搜看, 程序读取调色板后是如何处理的,我们在日志中搜索 int 21.*3f

Untitled

前面三条指令一共读取了 01b342h 个字节,回忆我们上一期的内容, 第一张图片读取的长度是 111426,也就是 01b342h,所以前三个读取了完整的图片, 那么为什么后面又读取了 150h 个字节呢。

回到 EVENT.PAT

我们回到 EVENT.PAT 的 00033dh 的位置:

Untitled

在这里我们还能发现更多对后续有帮助的信息, 比如 1d 07 00 00 其实是第一张图的起始位置,80 02 其实是图像宽度, 67 01 是图像高度,接下来这个 af bb 01 00 很可能就是第二张图的起始位置了, 这样我们可以算出第一张图的长度,01bbafh - 00071dh = 01b492h, 对比图片长度 01b342h,正好多了 150h 字节, 我们看看这 150h 个字节到底存了什么:

Untitled

我们又看到了 3f 3f 3f,看来这 150h 个字节就是我们要找的另一块调色板, 不过另外一个问题接踵而来。

两个调色板如何合并

144 + 112 = 256

150h 换成十进制是 336,只有 112 种颜色,那么如何跟第一块调色板合并, 这就变成了我们新的问题,一个近乎直觉的做法是:因为第一块调色板是完整的, 而且后面用 0 来填充了很多,所以这 112 色是第一块调色板后半块的补充。 这样的话,我们需要用第一块调色板的前 144 种颜色加上这 112 种颜色, 组成一块新的调色板,我们画画看:

test-144-112.png

还是不对,那么是不是应该把第二块调色板放在前面呢,也不对,这里就不再放图了。

回到日志

我们回到程序读取第二块调色板的日志:

0277:00000D0C  int  21                                                EAX:00003F50 EBX:0001000A ECX:00000150 EDX:1BF28180 ESI:00000070 EDI:0000578C EBP:00005738 ESP:00005734 DS:04FF ES:028F FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:1 IF:1

内容被读取到了 04FF:8180 的位置,然后搜索 rep.*8180.*04ff, 看有没有再被复制到哪里:

0277:00001D10  repe movsb                                             EAX:00030150 EBX:00005800 ECX:00000150 EDX:00000287 ESI:00008180 EDI:000022F8 EBP:0000580E ESP:00005800 DS:04FF ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:1 AF:1 PF:1 IF:1

复制到了 0287:22f8,那么索性我们搜程序有没有复制 300h 个字节, 我们在日志中搜 rep.*cx:....0300

Untitled

0287:21a8028f:556e 复制 300h 个字节,看上去和上面的内存地址有交集?

我们用 22f8h 减去 21a8h,正好也是 150h,这下似乎一切就明朗了, 在第二个调色板前面,还有 112 个颜色,我们也很好猜这 112 个颜色从哪里来。

总结

调色板似乎不是分两部分,而是三部分:

  1. 第一部分:EVENT.PAT 00003dh 开始,虽然完整定义了全部调色板,但是仅有前 112 色用于绘制人物。
  2. 第二部分:每个图片后定义了 112 色,用于绘制图片中的独有颜色。
  3. 第三部分:剩余 34 色,目前没有涉及,推测是用于绘制 UI 区域和鼠标。

最后,我们再次合并两个调色板,验证下我们的结论吧:

test.png