WAD是id Software为Doom定制的无压缩、无校验、无加密的线性二进制容器,结构为12字节头(含"IWAD"/"PWAD"标识、lump数量、目录偏移)+ lump目录(每项16字节:偏移、大小、8字节右补空格ASCII名)+ 原始数据块;解析须校验directory_offset与文件大小关系,防范恶意超大num_lumps,并依据Doom源码理解lump间隐式依赖。

WAD文件结构到底是什么样的
WAD不是标准格式,是id Software为Doom定制的打包容器,本质是“头+目录+数据块”的线性二进制布局。解析失败绝大多数是因为误把WAD当ZIP或LZMA——它不压缩、无校验、不加密,纯靠偏移和长度定位内容。
关键字段只有三个:header(12字节,含标识符"IWAD"或"PWAD"、lump数量、directory offset),后面紧跟着lump directory(每个lump条目16字节:offset、size、name),最后才是所有lump原始数据。名字是8字节ASCII,右补空格,不是C字符串,不能直接std::string(lump.name)。
用C++读取lump目录时常见的内存越界
常见错误是没检查num_lumps是否过大,或没验证directory_offset是否在文件末尾之前。Doom原版WAD最大lump数约4096,但自制WAD可能故意写超大值触发解析器崩溃。
- 先用
fseek(fp, 0, SEEK_END); long size = ftell(fp);拿到真实大小 - 确认
directory_offset + num_lumps * 16 ,否则拒绝加载 - 每个lump的
offset和size也必须满足:offset >= directory_offset + num_lumps * 16且offset + size - name字段用
std::string_view(name, strnlen(name, 8))安全截取,避免strlen越界读
如何正确提取MAPxx或S_START这类地图/图形lump
WAD里没有“类型声明”,lump用途全靠名字约定。比如"MAP01"开头表示地图,"S_START"到"S_END"之间是精灵图,"F_START"到"F_END"是平铺纹理。解析器必须按名字范围扫描,而不是依赖固定索引。
立即学习“C++免费学习笔记(深入)”;
注意大小写:Doom WAD全部大写,"map01"或"Map01"不会被引擎识别。实际提取时建议先构建std::unordered_map<:string lumpinfo></:string>,key统一转大写再查。
-
"THINGS"、"LINEDEFS"、"SIDEDEFS"等是地图核心结构,需按固定二进制结构解析(例如LINEDEFS每条14字节) -
"PLAYPAL"是256×3 RGB调色板,直接读256×3字节即可 -
"COLORMAP"是4个256字节映射表,不是单个
为什么用fread比std::ifstream更稳妥
std::ifstream默认启用locale和宽字符转换,在Windows上可能把0x0D 0x0A自动转成0x0A,破坏二进制偏移。Doom WAD对字节位置极其敏感,差1字节整个lump就读歪。
坚持用C FILE API:FILE* fp = fopen("game.wad", "rb");,配合fseek/fread,全程禁用std::endl和任何流操作符。如果非要用C++流,必须显式设置file.rdbuf()->pubsetbuf(nullptr, 0)并file.unsetf(std::ios_base::skipws),但不如C API直白可靠。
真正难的不是读,而是理解lump之间的隐式依赖——比如"SEGS"引用"VERTEXES"索引,而索引又依赖"LINEDEFS"顺序。这些逻辑不在WAD格式里,得对照Doom源码的doomdef.h和p_setup.c来对齐字段定义。


















