第 2 章:文件结构
VFP 1.3 是小端序的单文件二进制容器,不是 ZIP,也没有全局压缩层。读取器必须以文件末尾的 Footer 和其指向的活动 DIR0 为准;Payload 的物理排列不表达语义。
0 Header (64 B, VFPK)
64 已登记 Payload(连续写入;当前规范不要求对齐)
directoryOffset 活动 DIR0
fileSize - 64 Footer (64 B, VFPF)
2.1 Header(64 B)
布局为 <4sHHIQQ32s4s>。
| 偏移 | 类型 | 字段 | 规则 |
|---|---|---|---|
| 0 | char[4] | magic | ASCII VFPK |
| 4 | u16 | major | 1 |
| 6 | u16 | minor | 3 |
| 8 | u32 | headerSize | 64 |
| 12 | u64 | featureFlags | 提示性位图;目录才是实际依据 |
| 20 | u64 | directoryOffset | generation 0 的目录提示 |
| 28 | u8[32] | sourceHash | 权威源哈希 |
| 60 | u8[4] | reserved | 全零 |
Header 的 directoryOffset 不是活动目录的最终权威位置。追加保存后,Reader 必须使用最后一个 Footer。
2.2 Footer(64 B)
布局为 <4sHHQQQI28s>。
| 偏移 | 类型 | 字段 | 规则 |
|---|---|---|---|
| 0 | char[4] | magic | ASCII VFPF |
| 4 / 6 | u16 | major / minor | 1 / 3 |
| 8 | u64 | directoryOffset | 活动 DIR0 绝对偏移 |
| 16 | u64 | directoryLength | 完整目录长度 |
| 24 | u64 | generation | 初始值为 0 |
| 32 | u32 | directoryCrc32 | 完整 DIR0 的 CRC-32 |
| 36 | u8[28] | reserved | 全零 |
读取器必须拒绝目录超出 Footer 前边界、目录 CRC 错误或版本不是 1.3 的文件。VFP 1.3 不要求读取早期容器版本。
2.3 DIR0 与目录项
DIR0 头为 12 B:<4sHHI>,字段依次为 DIR0、major=1、minor=3、entryCount。其后恰好有 entryCount 个 48 B 项,目录总长度为 12 + entryCount × 48。
目录项固定布局为 <4sIiIQQQII>:
tag: char[4]
codec: uint32
chunkId: int32
flags: uint32
offset: uint64
length: uint64
rawLength: uint64
crc32: uint32
reserved: uint32
codec 是唯一载荷编码依据:0=RAW8,1=RLE8。flags=0、reserved=0 为强制规则;未知 codec、非零 flags 或 reserved 必须拒绝,不能从 flags 猜测 RLE。crc32 覆盖存储态 Payload;RAW8 通常要求 length==rawLength。全局区段的 chunkId=-1。
VOX0 是唯一允许 codec=1 的现行标准区段;其它标准区段必须 RAW8。所有 Payload 必须位于 Header 后、活动 DIR0 前,且不得交叠。
2.4 featureFlags
Header featureFlags 仅提示实际目录中的可选区段:bit 0 PRVW、1 MSH0、2 VOX0 可为 RLE8、3 VBUF、4 PMSH、5 RND0、6 ANM0、7 THMB、8 MATL、9 SKEL/VSKN[/ANIM]、10 XEXT。Reader 必须以 DIR0 为准。
2.5 最小与严格 Writer
最小可恢复资产至少有 META、PAL0、CHIX、所有 Chunk 的 VOX0、DIR0 与 Footer。VoxelKit 严格校验/编译档还会为每个 Chunk 写入 MSH0,并建议写入 SCNE。VBUF、PMSH、RND0、ANM0、PRVW、THMB、MATL、Rig 与 XEXT 均是可选能力。