quake的pak文件是扁平索引+原始数据拼接的归档格式:开头为68字节/项的文件表,末尾紧随原始数据;路径名56字节、小端4字节偏移与大小;读取需注意二进制模式、字节序、结构体对齐及边界校验。

PAK文件结构到底长什么样
Quake的PAK文件不是压缩包,而是一个扁平索引+原始数据拼接的归档格式:开头是固定长度的文件表(每个条目68字节),末尾紧跟着所有文件的原始字节。整个文件没有校验、无加密、无嵌套目录——路径名就是纯char[56],以\0结尾;文件偏移和大小各占4字节小端整数。读取失败往往不是因为解析逻辑错,而是字节序或偏移计算偏差了1个字节。
用C++读取文件表必须注意字节序和对齐
C++默认结构体有内存对齐,直接fread进struct会因填充字节导致偏移错乱。正确做法是用uint8_t数组逐字段读,或用#pragma pack(1)强制紧凑布局。同时,Quake用小端序,x86_64上可直接转uint32_t,但跨平台时务必用le32toh()(Linux)或_byteswap_ulong()(Windows)。
常见错误现象:file_offset读出来是巨大正数(如0x80000000),其实是符号扩展+字节序反了;或者file_size为0,说明文件表指针没定位到末尾前的固定位置(文件总长 − 4 − 表长度)。
- 先
fseek(fp, 0, SEEK_END)拿到文件总长 - 减去4(文件表长度字段自身)再减去表长度,得到文件表起始偏移
- 用
fseek(fp, table_offset, SEEK_SET)跳转,再读表长 - 每个表项严格按56+4+4字节顺序读,不依赖
struct自动对齐
提取单个文件内容只需两步:查表 + 偏移读取
查表时字符串比较必须用strncmp()而非strcmp(),因为name字段可能未填满56字节且不保证末尾<p>查表时字符串比较必须用<code>strncmp()而非strcmp(),因为name字段可能未填满56字节且不保证末尾\0;读取数据前要验证file_offset + file_size不超过文件总长,否则是损坏或恶意PAK。
file_offset + file_size不超过文件总长,否则是损坏或恶意PAK。
示例关键片段:
uint32_t offset = le32toh(entry.offset);
uint32_t size = le32toh(entry.size);
if (offset + size > file_size) { /* 拒绝读取 */ }
fseek(fp, offset, SEEK_SET);
fread(buffer, 1, size, fp);
Windows下打开PAK文件要加binary模式
用fopen("pak0.pak", "rb"),漏掉b会导致CR/LF转换,使后续所有偏移计算全错——尤其在读取文件表长度字段(最后4字节)时,若被误转成\n,fread会提前结束,整个解析崩盘。这是最隐蔽也最高频的问题,连不少开源PAK工具早期版本都栽在这儿。
另一个易忽略点:PAK路径名区分大小写,但Quake引擎本身在Windows上不区分;你的代码若做精确匹配,就该按原样比(比如models/objects/gibs/gib1.mdl不能转小写去查)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











