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

基本上,静态事件图我们已经可以解出来了,不过在那之前,我想再聊一聊调色板

目录

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

前情提要

我们在本系列「十九」期看了下程序是如何处理调色板的,我们知道,程序读取调色板后, 做了一次转换,然后结合一个字典来记录调色板的颜色。

为什么要用字典,之前我们说了,因为有一些过渡特效,是直接修改调色板实现的。 而为什么说做了一次转换,因为我们没有在 Redevent.paf 中找到内存中的调色板数据。 今天我们就看看这个转换在做什么。

读取调色板

完成读取调色板的还原后,我觉得还有点值得回味的,调色板的数据是这样排列的, 首先我们知道,调色板中的一个颜色占 2 个 byte,在文件中,三原色的排列方式是这样的:

RRRRRGGGGG0BBBBB

16 个 bit 没办法平均分给三个颜色,所以有 1 个 bit 没有使用, 但是我想可能很多朋友和我一样没有想到,这个 bit 会放在中间。

因为我们直接给出结论了,所以解出调色板的代码很好写:

fn extract_palletes(&self) -> Vec<Pallete> {
    let pal_item = &self.item_vec[self.get_pallete_item_index()];
    let data_len = 520;
    let count = pal_item.body_len / data_len;
    let mut pal_vec = vec![];
    for i in 0..count {
        let offset = pal_item.body_offset + 8 + i * data_len;
        let mut current_pal = Pallete { color_vec: vec![] };
        for j in 0..256 {
            let pal_data = u16_from_vec_u8(&self.content, offset + j * 2);
            let r = ((pal_data & 0xf800) >> 11) as u8;
            let g = ((pal_data & 0x07c0) >> 6) as u8;
            let b = (pal_data & 0x001f) as u8;
            current_pal.color_vec.push(Rgb([r << 3, g << 3, b << 3]));
        }
        pal_vec.push(current_pal);
    }
    pal_vec
}

同样注意调色板三原色的最大值是 31,所以我们需要进行放大。

那么调色板的位置是哪里来的呢,在 Redevent.paf 存储的第 0 个子项 PAT2000 中(请习惯从 0 开始计数):

这个 169h 的意思是,Redevent.paf 存储的 169h 子项,就是所有的调色板,我们可以看一下对应文件位置:

每组调色板都是 8 个 byte 头,加上 512 bytes 数据,所以程序写调色板长度是 520。

PAF 文件

由于讲到了一些 PAF 的子项,那我们也简单介绍一下这种文件格式,和 DOS 的 PAT 文件一样,PAF 文件也是一个小型数据的列表文件,不过结构完全不同。总体上,PAF 文件分成三个部分:

  1. 头部,记录了一些关键数据的位置
  2. 数据项集合,每个数据项也是分为两个部分
    1. 数据头,固定为 64 个 byte,包含了数据的位置和大小
    2. 实际数据,其中保存了多种类型的数据,比如图像、调色板、图像和调色板的对应关系,等等。
  3. 尾部,数据表,其实也就是第二部分中的所有数据头重新再列一遍。

总结

使用上面处理好的调色板,就可以直接画图了,因为我们不需要做什么特效,所以之前的字典我们可以不用,直接用这份画图就好。总结这次调色板的处理方式,有很多特别的地方:

  1. 一次处理两个颜色:这恐怕还是最容易理解的部分,因为每个颜色是 16 位,这样可以把循环次数降低一半,不晓得这样能快多少。
  2. 实际颜色需要补位查表:这个应该是为了做特效。
  3. RRRRRGGGGG0BBBBB:??????

本系列「二十一」期我们有一个结论,图像序号和调色板数据序号相同,对于事件图一和二是这样,但是后面就有比较大的偏差了,有时间的话我们放到下期完整解析静态图时再说吧。