相信很多朋友等这一天很久了,是的,我们这次一口气把 Redevent.paf 讲完。
本系列已完结,以下是各章节说明,17 之前是 dos 版相关,之后是 2001 版:
如同大宇的 MKF,同门的 PAT,总体上 Redevent.paf 也是一个小文件集合,包含了什么, 我们稍后就讲。
文件头以 MGFILE 开头,记录了一些概要信息,比如 0ch-0fh
记录了「小文件」个数,
20h-23h
记录了文件表的偏移量,文件表中,每一项的长度是固定的四行(40h
),
我们姑且称为文件索引项。 有这些信息,我们就可以把文件大卸八块:
let mut items = Vec::new();
let offset = u32_from_vec_u8(&self.content, 0x20);
let count = u32_from_vec_u8(&self.content, 0xc);
let item_len = 0x40;
for i in 0..count {
let start = (offset + i * item_len) as usize;
let end = start + item_len as usize;
let item = IndexItem::from(&self.content, start);
println!(
"index: {:03}, type: {}, {}, head: {:08x}, body: {:08x}, len: {:08x}",
i,
item.type_str,
item.body_head_string,
item.head_offset,
item.body_offset,
item.body_len
);
items.push(item);
}
大卸八块之后,根据小文件的头部标识,大概有这么几种,我简单摘抄一下:
index: 000(000), head offset: 00000400, body head: 50415432, len: 240
index: 001(001), head offset: 00000840, body head: A1CB8C8C, len: 232588
index: 002(002), head offset: 00039880, body head: A1CB8C8C, len: 232588
index: 036(024), head offset: 007cc100, body head: A1CB5C8A, len: 232028
index: 123(07b), head offset: 01b2c2c0, body head: A1CB8C8C, len: 232588
index: 124(07c), head offset: 01b65300, body head: 80020000, len: 11316
index: 125(07d), head offset: 01b68340, body head: A1CB691C, len: 7273
index: 126(07e), head offset: 01b6a380, body head: A1CB691C, len: 7273
index: 339(153), head offset: 01d1c4c0, body head: A1CBD70A, len: 2775
index: 340(154), head offset: 01d1d100, body head: 63000000, len: 19780
index: 341(155), head offset: 01d22140, body head: ACF5BCD3, len: 336
index: 342(156), head offset: 01d22580, body head: 39298800, len: 0
index: 343(157), head offset: 01d345c0, body head: A1CB1EBB, len: 310046
index: 344(158), head offset: 01d80200, body head: A1CB70BB, len: 310128
index: 356(164), head offset: 02110500, body head: A1CB5CBB, len: 310108
index: 357(165), head offset: 0215c140, body head: 80020000, len: 1472
index: 358(166), head offset: 0215c980, body head: 39298800, len: 0
index: 359(167), head offset: 021705c0, body head: A1CB5CBB, len: 310108
index: 360(168), head offset: 021bc200, body head: A1CB7FBB, len: 310143
index: 361(169), head offset: 02207e40, body head: 39298800, len: 81120
index: 362(16a), head offset: 0221be80, body head: illegal, len: 3452816845
A1CB 开头的档都是图档,每一串图档后,都会跟一个数字开头的小文件,
这个文件用来说明前面图档的信息,比如对应哪个调色板,有些长度为 0 的,
我们就不看了,还有两个 167h
、168h
只有图像,没有调色板,我们也放一放,
剩下比较特殊的,还有第一个,第 155h
个,第 169h
个,我们一个一个来。
PAT2000 是第一个小文件,有一个明显的 PAT2000 的字样,
45ch-45fh
记录的是调色板的小文件序号,
顺带一提 169h
也是文件中的最后一个有效小文件。
我们之前提过,文件索引项,除了在末尾的文件表中有,
每个小文件的开头也会重复记一次,所以 PAT2000 的实际开始位置在 400h
,
但是内容在 440h
。
PAT2000 中 458h-45bh
记录的是另外一个有意思的小文件(155h
),
为了展示它的意思,我甚至要换一个编辑器:
好了,我们有官方的命名了,这个其实也剧透了一些内容,文件包含了三种类型的资源, 事件图、眼睛图、结局图,现在叫眼睛不合适了,我们改叫动态图吧, 不过 DOS 是叫 EYE 的。
169h
的调色板,之前我们也分析过,简单总结就是,每套调色板 520 个 byte,
前 8 个 byte 似乎没什么用,后面 512 个 byte,每两个 byte 代表一个颜色,
我们之前分析过,是 RRRRRGGGGGOBBBBB 格式。
图档以 A1CB
开头,接下来 4 个 byte 是长度,
后面的 280h
和 168h
就是图档的长和宽(640x360),有多组,
从 89ch
开始就是第一张图的的数据了,
前 6 个 byte 代表一行像素点的起始位置和长度,看代码可能更清楚一些:
let start = image_item.body_offset + 0x1c;
let mut current = start;
loop {
let y = u16_from_vec_u8(&self.content, current) as u32;
let start_x = u16_from_vec_u8(&self.content, current + 2) as u32;
let current_width = u16_from_vec_u8(&self.content, current + 4) as u32;
/*
println!(
"image id: {}, x: {}, y: {}, width: {}",
image_id, start_x, y, current_width
);
*/
for x_offset in 0..current_width {
let pix_offset = current + 6 + x_offset as usize;
let pix_val = self.content[pix_offset] as usize;
let pixel = image_buffer.get_pixel_mut(start_x + x_offset, y);
*pixel = pal.color_vec[pix_val];
}
current += current_width as usize + 6;
if y >= height - 1 {
break;
}
}
一串图档后,都会有一个小文件来记录前面图档的信息,这个文件没有明显的标识, 我们只能根据之前档案是否为图档来推测,记录的内容是图像的长宽、大小、 以及最关键的调色板对应关系:
let width = u32_from_vec_u8(&self.content, offset);
let height = u32_from_vec_u8(&self.content, offset + 4);
let len = u32_from_vec_u8(&self.content, offset + 0x1c);
let image_id = u32_from_vec_u8(&self.content, offset + 0x24) as usize;
let pal_id = u32_from_vec_u8(&self.content, offset + 0x28) as usize;
println!(
"image width: {}, height: {}, len: {}, image: {}, pal: {}",
width, height, len, image_id, pal_id
);
有了以上这些信息,相信解出 2001 版的资源已经不难了, 但是似乎我们还没办法画出动图,因为虽然我们有了动态资源,但是, 其实我们没有眼睛的位置,和对应关系,文件里似乎也没有这些信息, 这些我们就下次再聊吧。
前面我们提到的,167h
、168h
只有图像,没有调色板,
其实这两张也已经算进结局图里了,这样结局图多了两张,是不是很激动,
其实没什么好激动的,因为就是这种:
说个题外话,这两个人物可能是画出来的,并不是真人出演, 这点我还真是看走眼了这么多年,后来看了原画作者的一些其他图,感觉非常相似, 不晓得有多少玩家和我一样眼拙。