Where’s All the Data?

WAD (Where’s All the Data?) is the file format created by id Software and is utilized by the DOOM engine for storing game data. The primary concept behind the WAD is to allow users to easily modify game data. After the release of Wolfenstein 3D, id Software took note that many were modifying their game, but noticed the process was incredibly obtuse and actually involved modifying the engine directly. This idea of simplicity goes into the design of the format, being easily understandable and modifiable by DOOM users. A direct result of WAD’s beautiful simplicity is the ever growing and incredibly healthy modding community DOOM has, even 30 years later. It cannot be understated that the WAD is one of the reasons that DOOM has enjoyed such a long life, and why DOOM is revered as such an influential classic. So, the WAD’s simplicity is not a bad thing, and I feel it is a perfect example of what good engineering should be: simple, to the point, and tidy.

Although WAD is an easy format to understand, it wasn’t a perfect modding solution. Vanilla DOOM game behavior and sprite replacement cannot be modified directly with WADs, and instead had to be done using some sort of patching of DOOM.EXE or using a tool. Today, many source ports allow for even easier modification of the game.

There are two different types of WAD files, the IWAD, or Internal WAD, and PWAD, or Patch WAD. In the context of the DOOM engine, the IWAD is the main game data, or core WAD, that is needed to run the game. The PWAD is effectively a patch file that replaces some data in the IWAD. In many WAD editors, the IWAD can’t actually even be modified, as the editor will not allow you. Modification of an IWAD is not ideal. This is just a name though, and the engine itself does not actually check it when loading. So, loading an IWAD as a PWAD is possible, and loading a PWAD as an IWAD is possible. They are mainly just for modders.

The WAD is broken down into 3 parts, the Header, the Lump Data, and the Directory. A WAD looks something like this:

Example layout of a WAD file.

The Header

All WAD files start with a 12-byte header, which contains 3 values, each 4 bytes in length. A typical WAD file has the following details:

Bytes Possible Type Length in Bytes Name Purpose
0x00 [u8;4] 4 identification This represents the type of WAD, either IWAD or PWAD, in ASCII. This would be a four length array of chars in C.
0x04 i32 4 numlumps A signed integer specifying the number of total lumps in the WAD.
0x08 i32 4 infotableofs A signed integer specifying an offset to the location of the directory.

Some notes:

  • Because the integer is signed, the max value for numlumps and infotableofs is $2^{31} - 1$.
  • WAD is stored in little-endian order.

What exactly is a lump? A lump is a specific resource, and can really be anything as it’s just a collection of bytes. In DOOM this can be things like graphics, map data, text, sound, and markers. Sky is the limit!

In Rust, a possible struct definition (in Rust) for the header would be:

1
2
3
4
5
struct WadHeader {
	identification: [u8; 4],
	numlumps: i32,
	infotableofs: i32
}

A possible header example could look like this:

1
2
3
4
5
let wad_header = WadHeader {
    identification: [73, 87, 65, 68],
    numlumps: 16,
    infotableofs: 42172
};

The array of u8 for identification represent IWAD in ASCII, where each value is a character. The numlumps is 16, meaning that there are 16 total lumps in the WAD. The infotableofs is 47172, which corresponds to position 0xA4BC in hex as the starting position of the directory information.

Directory

infotableofs points to this section of the WAD via an offset. The directory is at the very end of the WAD and contains descriptors that describe each lump. Each descriptor is broken down as following:

Bytes Possible Type Length in Bytes Name Purpose
0x00 i32 4 filepos An integer holding the offset to the start of this lump’s data in the WAD
0x04 i32 4 size An integer containing the size of the lump in bytes.
0x08 [u8;8] 8 name Represents the name of the lump in ASCII. Limited to 8 characters. The name must be null terminated if it is less than 8 characters. Should pad with nulls.

A possible struct definition for would look like this:

1
2
3
4
5
struct LumpInfo {
    filepos: i32,
    size: i32,
    name: [u8; 8]
}

A possible descriptor example could look like this:

1
2
3
4
5
let lump_info = LumpInfo {
    filepos: 12,
    size: 345,
    name: [104, 101, 108, 108, 111, 0, 0, 0]
};

The filepos represents an offset of 12 bytes. Note, this means this is (possibly) the first lump to hold data, as the header discussed previously always takes the first 12 bytes. I say possible, because lump sizes can theoretically be zero. This is commonly done with map definitions, where the name is given but the size is zero. The size here is 345 bytes. This means that the next lump’s offset would be 357 bytes. Finally, the name array represents “hello” in ASCII, and notice the zeros at the end. This is the null padding discussed previously.

Conclusion

In summary, a WAD file is comprised of a header at the beginning of the file which details the type of WAD, the total number of lumps, and the offset to the directory listings. The directory listing is just a section of descriptors at the very end of the WAD that represents each lump. Each descriptor contains the position of the start of the lump in the WAD, the length of the lump in bytes, and the name of the lump. This makes a WAD an almost “sandwich” with the header coming first, then all the lump data in the middle, and then the directory containing the descriptors for ever lump coming last.

References