基本上,静态事件图我们已经可以解出来了,不过在那之前,我想再聊一聊调色板
本系列已完结,以下是各章节说明,17 之前是 dos 版相关,之后是 2001 版:
我们在本系列「十九」期看了下程序是如何处理调色板的,我们知道,程序读取调色板后, 做了一次转换,然后结合一个字典来记录调色板的颜色。
为什么要用字典,之前我们说了,因为有一些过渡特效,是直接修改调色板实现的。 而为什么说做了一次转换,因为我们没有在 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 的子项,那我们也简单介绍一下这种文件格式,和 DOS 的 PAT 文件一样,PAF 文件也是一个小型数据的列表文件,不过结构完全不同。总体上,PAF 文件分成三个部分:
使用上面处理好的调色板,就可以直接画图了,因为我们不需要做什么特效,所以之前的字典我们可以不用,直接用这份画图就好。总结这次调色板的处理方式,有很多特别的地方:
RRRRRGGGGG0BBBBB
:??????本系列「二十一」期我们有一个结论,图像序号和调色板数据序号相同,对于事件图一和二是这样,但是后面就有比较大的偏差了,有时间的话我们放到下期完整解析静态图时再说吧。