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

继大富翁之后,我打算开新坑了,这次我们来分析《红楼梦之十二金钗》中的资源文件, 这个系列会是我分析中的阶段性总结

目录

本系列已完结,以下是各章节说明,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. 动态图、鉴赏模式

背景知识

游戏的相关背景我应该无需多做介绍,大家可以自行查阅相关资料,另有一点,van 的 RPGViewer 似乎可以直接提取图片内容,但我只试出了一部分,关键内容无法提取,不确定原因,可能与游戏版本有关,我手上的版本应该是 DOS 版。比较着急的读者可以试着使用这个工具或咨询作者。

本游戏是 640 × 480 的 256 色游戏,对于 DOS 游戏来说这个分辨率已经相当不错了,不过 98 年 windows 已经流行,256 色 640 × 480 基本已是标配,注意,这个解析度已经不是 VGA 了,而且 256 色,比较直观的做法是一个 byte 直接保存调色板的索引,而不是用多个平面。

游戏有一个细节是,CG 鉴赏中,人物的眼睛是有动画效果的,我们通过观察游戏文件也可以发现,EVENT.PAT 对应还有一个 EVENTEYE.PAT 文件,我们也就大概猜出来两者的分工。

大致分析

音频文件

所有的音频文件为 DWD 后缀,一开始我以为是 DWord,打开文件后发现每个文件都以 「DiamondWare Digitized」开头:

原来是这个 DWD,DiamondWare Digitized 目前还搜得到一些内容,我们就放一放,后面再找找有没有工具可以直接打开。

图像文件

图像文件以 PAT 为后缀,每个文件头都是 「MGP2 MG Pattern」 的字样:

这个问搜索引擎就没有什么有用信息了,看来资料较少。

目标和思路

这次我把目标定在了 CG 鉴赏上,那么我们就看看 CG 鉴赏时,程序都做了什么,这里提醒一下,进入 CG 鉴赏最快的方法,是找个存档读取,打开卷轴,然后点右下第五个图标:

所以我们点击图标前,呼叫出 debugger,读取文件时中断(BPINT 21 3f),确认打开的文件:

几次检查 DS:DX 我们可以确定 EVENT.PAT 确实被读取了,读取到了 03BF:0000,不过并不是从头读取,接下来我们就要去看这部分数据是一步步写入缓存了,我们重新回到点击 CG 鉴赏之前,设置读取文件时中断,然后点击 CG 鉴赏,当在读取 EVENT.PAT 时,删除断点,开始记录 CPU log,log 数需要多一些,因为太少的话,可能屏幕还在黑屏时日志就结束了,我印象中是设了一千万。

寻找显存

接下来就是用 klogg 打开 cpu log,寻找es:a000,结果很失望,没有,看来 svga 已经完全舍弃了这个地址段,细想一下,这也是合理的,640 × 480 已经远超 8k 了,既然 256 色不再使用多平面存取,那么 a000 肯定也装不下整个屏幕的像素。

寻找文件的处理

显存那边的路已经断了,那我们就从另一头,文件读取下手,我们已经知道文件被加载到 03bf 段,那么如果能了解文件内容是如何被处理的,也能帮我们画出图形。

我们在 klogg 中搜索 es:\[.*=.*es:03bf,很快就可以确定处理文件内容的循环位置

0207:00003C36  cmp  byte [bp-05],00            ss:[0000573D]=0109     EAX:00030209 EBX:00000000 ECX:00000008 EDX:00000008 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:1 PF:1 IF:1
0207:00003C3A  ja   00003C3F ($+3)             (down)                 EAX:00030209 EBX:00000000 ECX:00000008 EDX:00000008 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C3F  mov  al,[bp-05]                 ss:[0000573D]=0109     EAX:00030209 EBX:00000000 ECX:00000008 EDX:00000008 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C42  cmp  al,[25FB]                  ds:[000025FB]=0000     EAX:00030209 EBX:00000000 ECX:00000008 EDX:00000008 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C46  jbe  00003C85 ($+3d)            (no jmp)               EAX:00030209 EBX:00000000 ECX:00000008 EDX:00000008 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C48  mov  al,[25FB]                  ds:[000025FB]=0000     EAX:00030209 EBX:00000000 ECX:00000008 EDX:00000008 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C4B  xor  ah,ah                                             EAX:00030200 EBX:00000000 ECX:00000008 EDX:00000008 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C4D  xor  dx,dx                                             EAX:00030000 EBX:00000000 ECX:00000008 EDX:00000008 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C4F  mov  cx,ax                                             EAX:00030000 EBX:00000000 ECX:00000008 EDX:00000000 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C51  mov  bx,dx                                             EAX:00030000 EBX:00000000 ECX:00000000 EDX:00000000 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C53  les  di,[2608]                  ds:[00002608]=0000     EAX:00030000 EBX:00000000 ECX:00000000 EDX:00000000 ESI:00000070 EDI:00000404 EBP:00005742 ESP:0000573C DS:0287 ES:0507 FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C57  mov  al,es:[di]                 es:[00000000]=3F00     EAX:00030000 EBX:00000000 ECX:00000000 EDX:00000000 ESI:00000070 EDI:00000000 EBP:00005742 ESP:0000573C DS:0287 ES:03BF FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C5A  xor  ah,ah                                             EAX:00030000 EBX:00000000 ECX:00000000 EDX:00000000 ESI:00000070 EDI:00000000 EBP:00005742 ESP:0000573C DS:0287 ES:03BF FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C5C  xor  dx,dx                                             EAX:00030000 EBX:00000000 ECX:00000000 EDX:00000000 ESI:00000070 EDI:00000000 EBP:00005742 ESP:0000573C DS:0287 ES:03BF FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0207:00003C5E  call 0277:0EFE                                         EAX:00030000 EBX:00000000 ECX:00000000 EDX:00000000 ESI:00000070 EDI:00000000 EBP:00005742 ESP:0000573C DS:0287 ES:03BF FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1

我们可以发现程序一直进入 3c36 行的循环,而文件内容不断从 3c57 行取出,而且根据搜索结果,文件只被读取了这一次,那么这显然就是文件的加载过程了。

不过在理解这段循环的时候,几次循环下来,我却越来越迷惑,因为似乎这段程序只是在不断根据文件内容在修改某几个固定位置的内存,比如 028f:573d028f:5740,让我又有点怀疑,难道这是在计算 hash?因为只是一直改写某几个固定位置,没办法生成显示数据。

总结

篇幅也比较长了,我们整理一下已知内容,休息一下:

  1. 音频文件为 DiamondWare Digitized 格式
  2. 图像文件为 MGP2,可能是自创格式
  3. EVENT.PAT 被加载到 03bf:0000
  4. 0207:3c36 开始为读取文件的过程,但是读取过程还有待确认。