之前说到要来写一篇介绍光荣头像储存格式的文章,想不到工作一忙又拖了一个月左右。
趁著这波连假才有一点闲暇时间可以来写。
在进入正文之前先聊点历史。其实个人对于光荣游戏的头像格式研究可以追溯到上个世纪
末,听起来很遥远,不过那时候已经是三国志六代(1998 发行)的时候了。在 1999 年三
月,还是大学生的我用 Visual Basic 写了个可以看四代五代头像的“光荣三国志脸谱浏
览程式”,并且上传到巴哈姆特 BBS 站的 KOEI 板,没错,这个 BBS 站就是今天“巴哈
姆特电玩资讯站”的前身。几年前巴哈姆特 BBS 的资料还可以透过 Web 版查看,并且直
接连结到文章,可惜今天站内的文章都不能网址直连了,只能登入 BBS 后查看。
https://i.imgur.com/8HWIWfo.png
已经是 24 年前的文章了
这个小程式是我的第一个 Side Project,也就是并非为了工作或学校功课,纯粹出于自
己个人喜好的小 Project。可惜程式码已经遗失,也没有截图,只记得是个一次显示一张
头像,可以切换上一张下一张的小程式。这个小程式比大家熟悉 van 修改器还早出来,
也比 van 2004 年在轩辕春秋发的一篇〈三国志系列图像文件格式浅析〉还要早上许多。
接下来停滞了好一段时间,一直到 2013 年初(天啊已经十年前),那时候刚好有点空
闲,于是研究起三国志三代的游戏资料,特别是各剧本武将资料以及武将头像,并且也
解出了大众脸的产生规则,相关的文章版上还可以找到,算是解了小时候玩游戏的谜团
之一。
再来便是今年,年初的时候在整理电脑里的旧档案,意外发现之前 2013 年的研究资料,
但只有残余的部分结果,少了原始程式码与部分过程档案,于是决定重启计画,研究如何
导出三国志的历代头像,并将计划规模扩及到光荣其他的早期游戏,也顺便把这次的研究
结果纪录到 Github 上,方便日后有人有兴趣可以继续深入。
### 基本原理 ###
说回正题,光荣早期游戏的图片格式在不同的游戏里可能有些许差异,但基本原理相同。
图片格式的基本原理不难,难点在于没有线索的情况下找出其规律。当年也是因为四代五
代有了威力加强版,可以自订头像,于是灵机一动,想到可以借由更改头像造成的档案变
化,来反推出其规则。但即便如此,当年仍花了我不少时间研究,研究出来后写程式码反
倒不需要多少时间。
下面便开始说明其格式,往下看之前最好有些“位元(bit)”“字节(byte)”的基本概
念,并熟悉一些可以编辑二进制的编辑器。这些资料网络上都可以找到许多教学,这里就
不赘述。
https://i.imgur.com/AXSSUb2.png
三国志三代 `KAODATA.DAT` 以二进制编辑器打开后的样子
首先我们透过观察三代的头像,或是透过四代威力加强版的脸谱编辑功能,
不难发现,每个头像只用到了八种颜色,
在电脑中只需要 3 个 bit 便可以表现出 8 种变化(2的3次方),
所以头像中每个点只需要 3 个 bit。
但是一个 byte 有 8 个 bit,3 个 3 个一组放会无法对齐。
光荣的工程师在这边用的方式是,将 3 个 byte 当作一组,
每一组三个 byte 可以表示八个点,假设这三个 byte 分别是 A, B, C。
第一个点用 A, B, C 最高位的 bit 来组合,
第二个点用 A, B, C 第二高位的 bit 来组合,以此类推,
到第八个点用 A, B, C 最低位的 bit 来组合。
换个更容易理解的方式。
以三国志三代的头像档案 `KAODATA.DAT` 为例,用二进制编辑器打开后,
可以看到其前三个 byte 分别为 `FF 00 77`, 我们把它用二进制方式写下来:
```
FF -> 1111 1111
00 -> 0000 0000
77 -> 0111 0111
FF 00 77 二进制 颜色代码
== == == =========== ===========
1 0 0 -> 100(二进制) = 4(十进制), 第一个像素点
1 0 1 -> 101(二进制) = 5(十进制), 第二个像素点
1 0 1 -> 101(二进制) = 5(十进制), 第三个像素点
1 0 1 -> 101(二进制) = 5(十进制), 第四个像素点
1 0 0 -> 100(二进制) = 4(十进制), 第五个像素点
1 0 1 -> 101(二进制) = 5(十进制), 第六个像素点
1 0 1 -> 101(二进制) = 5(十进制), 第七个像素点
1 0 1 -> 101(二进制) = 5(十进制), 第八个像素点
```
对照三国志三代伊籍的头像,
我们便可发现一开始的确是一个蓝色像素,后面接着三个青色像素,
然后又一个蓝色像素,再接三个青色像素。
所以“颜色代码 4(十进制)”对应的是蓝色像素,
而“颜色代码 5(十进制)”对应的是青色像素。
https://i.imgur.com/UYUbKKT.png
三代伊籍的头像放大
接着只要按照以上方式,将所有颜色代码找出,并将代码替换成色码画在图片上即可。
三代四代五代的人物头像大小是 64x80 像素, 然后每 3 bytes 代表 8 个像素,
因此一个武将的头像只要 64x80/8x3 = 1920 bytes。
三国志三的 `KAODATA.DAT` 档案大小是 589,440,
故可以得知这个档案里面有 307 (589440/1920) 张头像。
### 其他变化 ###
不止头像,其实光荣早期的游戏都是透过这种方式储存图像资料,只是放在不同的档案中
。然而这些游戏的开发前后跨度长达十几年,每个游戏在实际储存资料上也会有些不一样
的差异。
首先是颜色数。更早期的光荣游戏只有四色,因此只要两个 bit 就够,也就是说每两个
byte 存放八个像素点的颜色。例如三国志初代的 DOS 版就是只有用到四色。
https://i.imgur.com/WaPFIwj.png
三国志一的孔明,只有黑黄红绿四色
再来是图像资料是否只有一半高度(Half Height, 以下简称“半高”)。例如三国志二
代,它每个头像只有 64x40 的宽高资料,但在游戏中的显示却是 64x80,也就是说它的
高度拉扯了两倍,每一横列的像素都重复画了两次。有这种特征的除三国志二,还有维新
之岚、大航海时代初代、拿破仑等早期游戏
https://i.imgur.com/hApfgX6.png
拿破仑,左上角是 4x4 的正方格,可以看出每两列的颜色是重复的
又或者是头像档案经过压缩,这种压缩方式是光荣自家的 `LS11` 压缩算法,不是我们
常见的 ZIP 或 RAR 等公开算法。像这类的档案就无法透过上面方式直接取得图像资料,
要先解压缩后才可进一步解析头像资料。像是大航海时代二代以及三国志十代就都有用到
`LS11` 压缩法。这类有压缩过的档案,可以在档案开头发现 `LS11` 或 `Ls12` 的字样
。
https://i.imgur.com/gOgkUWN.png
大航海时代二代的头像档,`LS11` 开头
最后,为了避免多年后档案遗失,又得从头研究,我这次把相关代码都放上 Github 了,
除了各个游戏的头像提取方式外,也包含了部分游戏的人物资料汇出,以及关于这些
DOS 游戏里使用的中文字型研究。只是目前同时要兼顾工作,时间有限,文件与说明都还
很不足,希望之后陆续有时间补上。有兴趣的人欢迎直接看程式码,有任何建议或是想法
也欢迎透过 Github 上的 issue 交流。感谢大家看到这边。
Github 专案页面,原始码
https://github.com/tzengyuxio/kaodata