Outpost 2 文件格式 · bei.pm
此页面所描述的文件格式基于对Dynamix, Inc.和Sierra Entertainment知识产权的技术分析。
该知识产权现属于Activision Publishing, Inc. / Activision Blizzard, Inc.,目前由Microsoft Corp.持有。
这些信息是通过逆向工程和数据分析收集的,目的是为了档案保存和与历史数据的互操作性。
没有使用任何专有或机密的规格。
该游戏目前可以在gog.com上以下载形式购买。
以下的文章系列记录了我对实时战略游戏《Outpost 2: Divided Destiny》中数据格式的理解,该游戏于1997年由Sierra发布,并由Dynamix开发。
我主要在2015年11月1日至2015年11月14日期间,专注于分析游戏的数据以及如何处理这些数据。
根据我迄今为止获得的信息,Dynamix——和许多商业公司一样——并没有专门为《Outpost 2》开发某些数据格式,而是也在其他开发项目中使用了这些格式,例如《Mechwarrior》系列(经过修改)。
无论如何,还可以指出的是,数据格式的创新能力实际上是有限的,通常基于JFIF和RIFF等常见格式中的较长期存在的概念。
有关表格和数据格式的解释,请参阅什么是什么?中的更多信息。
这里提供的数据通常被理解为小端格式。
最后可以说,逆向工程非常有趣,尽管并不完全。
当然,我也推荐大家亲自玩一下游戏,因为它提供了有趣的游戏机制。
介绍
Outpost 2使用的数据格式具有类似于JFIF / PNG的结构 - 每个数据块都有一个8字节的头部。因此,我省略了在相应特定位置记录每个头部的细节,只记录偏差。
格式始终是以下内容;实际的有效数据嵌入其中:
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | 包含有关下一个数据块的预期信息。 已知值:
|
0x0004 | uint(24) | 区块长度 | 包含有关以下数据块大小(以字节为单位)的信息。 这里指的是纯有效载荷数据 - 不包括8个头字节。 |
0x0007 | uint(8) | 旗帜? | 目前尚不清楚这个区块的具体用途。 在卷中,这个值通常是0x80,而在其他文件中则常常是0x00。这表明这可能是一个标志位集。 |
卷
卷是游戏的数据容器,类似于档案格式,如 Tarball。 至少在 Outpost 2 中,该格式仅包含文件——没有文件夹。 不过,这些可能可以通过相应的文件名来模拟。
一个卷由卷头和多个卷块组成,这些卷块对应具体的文件。
“卷”是游戏目录中以 'vol'
结尾的文件。
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 56 | 4f | 4c | 20 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | V | O | L | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 |
音量标题
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 76 | 6f | 6c | 68 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | v | o | l | h | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 |
卷头本身不包含任何有效数据。
它仅仅作为一个容器。
卷头中的第一个数据应该是卷字符串;接下来是卷信息。
音量字符串
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 76 | 6f | 6c | 69 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | v | o | l | i | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 |
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 76 | 6f | 6c | 73 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | v | o | l | s | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 | |
0x0008 | uint(32) | 有效负载长度 | 指明以下数据中实际的有效字节数。 剩余的卷字符串列表数据显然被视为 垃圾。 在日期较晚的文件中,这些“剩余数据”为0x00,这可能暗示了在游戏开发过程中工具链的不足,也就是说,开发者很晚才关注到缓冲区的正确初始化,因为这些数据是否被初始化对游戏没有影响。 |
0x000c | uint(8)[] | 文件名列表 | 这是一份以0字节终止的文件名列表,至少在当前的数据成分中,仅期望ASCII字符。 在解析数据时,不需要更详细地评估这个数据块,因为在卷信息中已经直接引用了文件名的偏移量。 |
卷字符串是一个包含在卷内的文件名列表。
音量信息
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 76 | 6f | 6c | 69 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | v | o | l | i | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 |
卷信息包含了更详细的文件信息。在某种程度上,这是一种FAT目录项(FAT = 文件分配表)。
文件的数量是通过将块大小除以目录项的长度(14字节)得出的。
每个目录项的结构如下:
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 文件名偏移量 | 指明文件名在文件名列表(卷字符串)中的偏移量 (!)。 这与有效负载数据块的起始位置有关。 |
0x0004 | uint(32) | 文件偏移量 | 指示文件在整个卷文件中的偏移量。 |
0x0008 | uint(32) | 文件大小 | 指明文件的大小(以字节为单位)。 |
0x000c | uint(16) | 旗帜? | 显然提供了关于文件编码的额外信息。
|
音量块
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 56 | 42 | 4c | 48 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | V | B | L | H | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 |
卷块是一个容器,能够容纳文件。它仅仅是由于块格式而冗余地包含了文件大小,随后紧接着是实际数据。
瓷砖
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 50 | 42 | 4d | 50 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | P | B | M | P | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 |
这些图块是针对Outpost-2的特定位图图形格式。它们分为13个图块集,称为“wells”(well0000.bmp
到well0012.bmp
),这些文件位于maps.vol中。
这些图块集/Wells包含以下内容:
文件名 | 内容 |
---|---|
well0000.bmp | 一张32x32像素的蓝色图形 - 适合作为测试图像加载器是否正常工作 |
well0001.bmp | 包含浅色岩石、在浅色岩石上的山脉和无数种浅色岩石上的撞击坑变体 |
well0002.bmp | 包含浅色岩石的装饰物 - 即可以放置在浅色岩石上以增添装饰(或故意作为结构,如墙壁)的元素,包括植物 |
well0003.bmp | 包含在浅色岩石上的一种表皮状结构 |
well0004.bmp | 包含深色岩石、在深色岩石上的山脉和无数种深色岩石上的撞击坑变体 |
well0005.bmp | 包含深色岩石的装饰物 - 即可以放置在深色岩石上以增添装饰(或故意作为结构,如墙壁)的元素 |
well0006.bmp | 包含深色岩石上的表皮状结构,以及浅色岩石和深色岩石之间的过渡 |
well0007.bmp | 包含熔岩以及每个4-5帧的动画 |
well0008.bmp | 包含沙子和无数种沙子上的撞击坑变体 |
well0009.bmp | 包含沙子的装饰物 - 即可以放置在沙子上以增添装饰(或故意作为结构,如墙壁)的元素 |
well0010.bmp | 包含48个沙子与浅色岩石和深色岩石之间的过渡 |
well0011.bmp | 包含地图的极地冰盖,底下是深色岩石 |
well0012.bmp | 包含地图的极地冰盖,底下是浅色岩石 |
为了准确的实现,建议不要提前渲染瓷砖以进行缓存,因为日夜循环的数据仍需处理,并且会产生大量数据。
这些瓷砖是8bpp的图形,具有32x32像素分辨率的索引调色板,并且是相互排列的。然而,在这样形成的瓷砖集中,可以有更多的内容。
主容器由两个部分组成:head
和 data
。
瓷砖标题
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 68 | 65 | 61 | 64 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | h | e | a | d | . | . | . | . | . | . | . | . | . | . | . | . |
0x0010 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 | |
0x0008 | uint(32) | 版本 / 标志? | 这可能是文件格式的版本说明;在我所有的文件中,这里显示的值是 |
0x000c | uint(32) | 宽度(横向分辨率) | 指明图像文件的宽度(以像素为单位)。 在所有 Outpost 2 的井中,预计这里的值为 |
0x0010 | uint(32) | 高度(垂直分辨率) | 指明图像文件的高度(以像素为单位)。 在所有《前哨2》的井中,这里预计的值是 |
0x0014 | uint(32) | 色深? | 这个值的意义未知。 由于在所有检查过的文件中都包含值 |
0x0018 | uint(32) | 颜色深度 2? | 该值的含义未知。 可能是一个“目标”色深。 |
根据这些信息,还会提供一个以标准化RIFF格式存在的调色板文件。具体规格可以在这里找到 - 由于调色板在其他地方也出现过 - 在 调色板。
瓷砖数据
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 64 | 61 | 74 | 61 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | d | a | t | a | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 |
游戏引擎在需求时*可能*绘制瓷砖。
这似乎与昼夜循环有关,该循环有32个瓷砖的细分。在此过程中,亮度值显然会被“稍微”减少。确切的数值尚未确定,我正在基于以下计算进行工作。
v *= (daylight / 48) + 0.25;
使用像素的HSV数据,其中daylight是0到31之间的值,v是0到1之间的值。此外,还需考虑地图的左右各有16个瓷砖的边缘(用于单位的隐形生成)。
此外,昼夜循环似乎每个游戏周期只更新地图的一列。
加速的昼夜循环如下所示:
PRT
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 43 | 50 | 41 | 4c | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | C | P | A | L | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔术字节 | |
0x0004 | uint(24) | 托盘长度 | 与正常的块格式相反,指示在此文件中找到的调色板数量,而不是块的字节长度。 |
0x0007 | uint(8) | 旗帜 | 可能和往常一样,是标志。 不过我并不知道任何标志;因为我所知道的所有值都等于 |
我不知道PRT
具体代表什么;例如可以是'调色板和资源表'——因为这个文件可以在op2_art.prt中找到于maps.vol,它确实是这样的文件,或者说这种功能描述得很好。
该文件包含一个调色板列表、一个关于所有使用的位图的表格、所有动画定义以及一系列未知数据。它在一定程度上遵循了之前的容器格式,因为并非所有记录都遵循这个模式。
CPAL
部分(可能代表调色板容器)仅包含调色板数据,它说明了通常大小为1052字节的8位调色板有多少个。
1052字节的说明并不被视为强制性,因为调色板格式可能会有不同的调色板大小。它仅适用于Outpost 2交付的数据集。
在调色板列表之后,紧接着没有引导头部的位图列表;随后是动画列表。
这两者都由一个uint(32)(或者是uint24+uint8标志?)开始,包含记录的数量。
调色板
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 50 | 50 | 41 | 4c | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | P | P | A | L | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 托盘长度 | 与正常的块格式相反,指示此文件中找到的调色板数量 - 而不是块的字节长度。 |
0x0007 | uint(8) | 旗帜 | 可能和往常一样,是标志。 不过我并不清楚有什么标志;因为我所知的所有值都对应 |
调色板信息非常容易读取。
它们通常由一个头部和一个数据段组成。
托盘标题
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 68 | 65 | 61 | 64 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | h | e | a | d | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 托盘长度 | 与正常的块格式相反,指示此文件中找到的调色板数量 - 而不是块的字节长度。 |
0x0007 | uint(8) | 旗帜 | 可能和往常一样,是标志。 不过我并不清楚有什么标志;因为我所知的所有值都对应 |
0x0008 | uint(32) | Paletten格式版本? | 可能定义了调色板所遵循的调色板格式版本。 所有 Outpost2 调色板似乎都具有版本 |
托盘数据
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | 64 | 61 | 74 | 61 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | d | a | t | a | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 魔法字节 | |
0x0004 | uint(24) | 区块长度 | |
0x0007 | uint(8) | 旗帜 |
数据部分包含了各个托盘条目。 托盘条目的数量由块长度 / 4 得出。
每个条目的结构如下所示;
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | -- | -- | -- | 04 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(8) | 红色组件 | 表示颜色的红色成分 |
0x0001 | uint(8) | 绿色组件 | 表示颜色的绿色成分 |
0x0002 | uint(8) | 蓝色组件 | 表示颜色中蓝色的比例 |
0x0003 | uint(8) | 未知 - 旗帜? | 目前尚不清楚这个值的含义,因为它似乎基本上是 |
关于调色板,还有以下几点需要说明, 用于动画的调色板遵循以下规则:
- 第一个颜色始终是透明的,无论那里指定的值是什么。
-
调色板条目1-24在调色板1-8中作为玩家颜色使用。
除了玩家1以外的颜色具体来源我不太清楚。
我猜其余的颜色是硬编码的。
位图
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
0x0010 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 对齐宽度 | 给出像素数据行的宽度,以字节为单位 - 由于它们是按照4字节边界对齐的。 因此,快速跳转到特定的图像行是很容易的。 为什么这个值会单独存储,尽管可以计算出它,这一点尚不清楚。 |
0x0004 | uint(32) | 偏移量 | 指定位图中第一行的偏移量 |
0x0008 | uint(32) | 高度 | 指定图像的高度(以像素为单位) |
0x000c | uint(32) | 宽度 | 指定图片的宽度(以像素为单位) |
0x0010 | uint(16) | 类型 | 指示图像的类型。这里似乎是一个位掩码:
|
0x0012 | uint(16) | 调色板 | 定义要使用的PRT文件中的调色板 |
PRT文件的数据结构指示了用于精灵的位图是如何构建的。这些位图作为单个组件,用于组合多个精灵的动画帧。
具体的图像数据则隐藏在游戏目录中的
op2_art.BMP文件中。
为什么这个位图文件具有一个(大部分情况下正确的)RIFF位图头目前尚不清楚。Outpost 2可能使用系统API加载图形,暂时采用该头并覆盖相应的可变字段。
像素数据在BMP文件中的位置是 Offset + uint32-Offset, 这个Offset可以在BMP文件的地址0x000A找到(RIFF位图数据偏移),并且是从左上角到右下角的逐行排列。
单色1bpp图形可以被绘制成: 颜色0表示完全透明,而颜色1表示半透明的黑色/灰色,因为单色图形通常用于动画中的车辆和建筑阴影。
因此,可以组合许多图形。
动画
现在我们来到了Outpost 2数据格式中最高级别的学科:
动画。
动画列表以一个全球头部开始,该头部主要用于数据验证。 随后是具体的动画定义,这些定义分为3个层次:
-
动画
动画是最高级别的实例;它表示单位、建筑或“粒子动画”(陨石撞击、天气、爆炸)在特定起始状态下的动画。 -
帧
帧是动画中的单个图像。一段动画可以包含一个或多个帧。 -
子帧
子帧是关于在帧的特定位置以特定条件绘制特定位图的信息。一个帧可以包含一个或多个子帧。
接下来是各个动画定义。
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 动画数量 | 有多少个动画数据集 |
0x0004 | uint(32) | 帧数 | 总共应该有多少帧 |
0x0008 | uint(32) | 子帧数量 | 应该有多少个子帧总共存在 |
0x000c | uint(32) | 可选条目数量 | 有多少个"可选条目"。 |
动画
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
0x0010 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
0x0020 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(32) | 未知 1 | 未知信息 |
0x0004 | uint(32) | 边界框:左侧 | 指定边界框的左侧起始位置(以像素为单位)。 |
0x0008 | uint(32) | 边界框:上方 | 指定边界框的上边缘(以像素为单位)。 |
0x000c | uint(32) | 边界框:宽度 | 指定边界框的宽度(以像素为单位)。 |
0x0010 | uint(32) | 边界框:高度 | 给出边界框的高度(以像素为单位)。 |
0x0014 | uint(32) | 偏移量:X | 指定动画的水平中心点 |
0x0018 | uint(32) | 偏移量: Y | 指定动画的垂直中心点 |
0x001c | uint(32) | 未知 2 | 未知信息 |
0x0020 | uint(32) | 帧数 | 指明此动画包含多少帧动画 |
0x0024 | uint(32) | 窗口数量 | 指示在绘图时应使用多少个窗口 |
最高层的数据,即动画,主要是管理数据 - 边界框 指的是在选择车辆/建筑物时标记的坐标,并同时指明哪个区域可以点击。
偏移量主要确定“零点”;即需要在游戏内部坐标上进行加或减的点。可以更加数学地说:这里的偏移量指定了坐标原点。
窗口与偏移量一样,每个窗口都有4个 uint(32) 值,表示可用于单个子帧的区域。除非位图特别指定,否则不应在窗口外绘制。
框架
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(8) | 子框架数量和可选项 1、2 的切换 | 该值包含:
|
0x0001 | uint(8) | 未知 1 和 可选 3, 4 的切换按钮 | 该值包含:
|
0x0002 | uint(8) | 可选 1 | 未知 |
0x0003 | uint(8) | 可选 2 | 未知 |
0x0004 | uint(8) | 可选 3 | 未知 |
0x0005 | uint(8) | 可选4 | 未知 |
副框架
地址 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | 字符 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
偏移量 | 数据类型 | 名称 | 解释 |
---|---|---|---|
0x0000 | uint(16) | 位图ID | 指定要为此子框架使用哪个位图 |
0x0002 | uint(8) | 未知 1 | 未知 - 不过我强烈猜测这可能是一个渲染优先级(Z层)的问题。 |
0x0003 | uint(8) | 子框架ID | 指明我们所处的子框架 |
0x0004 | sint(16) | 偏移 - 水平 | 指定子框架在框架内的位置,以及位图水平移动的像素数 |
0x0006 | sint(16) | 偏移 - 垂直 | 指明子框架在框架内应放置的位置,或位图应垂直移动多少像素 |
这样我们就可以将单个帧以及完整的动画进行组合,下面以索引 500 的复杂动画为例进行演示。
动画 500
动画500展示了如何卸载一辆装载普通矿石的普利茅斯运输车。这是少数利用窗口功能的动画之一。
因此,完整的动画可以组合在一起。
遗憾的是,上方的装载舱门仍然存在问题,因为在图形类型信息中没有设置相应的位。
这里还有一些游戏中的其他美丽动画精灵:
用户界面
现在还缺少游戏的用户界面,采用了拉丝金属的外观设计。
但在这里也可以看出,Dynamix并不需要重新发明轮子;这里不仅仅是简单地使用了Windows提供的User32和GDI32 API,特别是还利用了User32的资源管理。
例如,可以通过Angus Johnson开发的免费软件Resource Hacker来提取这些资源,或者如果在Linux / Mac OS下不想使用Wine,可以利用icoutils中的wrestool来提取。
文件名 | 内容 |
---|---|
Outpost2.exe | 仅包含游戏的图标,显示的是位于新地球前的太空站 |
op2shres.dll | 除了包含用于控件的图形,如边框、按钮、单选按钮和复选框外,还包含对话框背景、故事任务文本的配图以及主菜单背景图形 |
out2res.dll | 包含游戏内窗口装饰、普通和特殊金属的图标、加载屏幕、对话图形以及其他光标图形,此外还有游戏目录中的动画图形 |