4/1/94 Chris Thomas DOOM WAD File Format -------------------- ********** DISCLAIMER ******************** DISCLAIMER *********************** In no event shall I be liable for any errors contained herein or for incidental or consequential damages in connection with the furnishing, performance, or use of this information. Feel free to do what you want with this information. Copy it freely, in any media you desire, provided that this disclaimer notice and my name remain intact. * 4/1/94 Chris Thomas, CSID [75410,2627]. ********** DISCLAIMER ******************** DISCLAIMER *********************** This is an attempt to write down everything I have learned/guessed about the DOOM WAD file format. I don't know how correct some of this is, as I have yet to construct new levels . I am writing this in a hurry, so please ignore spelling misteaks . BTW, I bought DOOM. Wouldn't consider mucking with it otherwise. Also I must thank Id for their willingness to let people hack. Thank you Id. I starting figuring out the format with the goal of writing a DOOM level editor, with a raycasting level previewer. This is an ambitious goal. I have since shelved the project, hopefully someday I will start it up again, but I doubt it. However I am becoming bored with DOOM, so perhaps this boredom might spark some renewed interest in a level editor. I shelved it because it is a lot of work for little return. Sure, I could see my name up in lights on CompuServe, as the "Level Editor Guru", and I would be famous with all 5 of the forum regulars for about 15 minutes. Fame does not interest me. Money might, but this is Id's product and I wouldn't feel right about making money off their product (without their endorsement ). Charging money for it implies tech support etc. Blah. Anyhow, I started this effort by downloading some of the existing DOOM tools from Cserve. WADTLS.ZIP and IWADDU.ZIP stick out as being particularly helpful. IWADDU (I think that's it...) contained a .H file that the author had used for that tool, and I was able to use that information for the WAD file. WADTLS (I think...) contained a .DOC file with even more bits of information. Enough background blather, here it goes.... The basic WAD format -------------------- The WAD file is referred to as a "resource file". It contains a list of resources, stored as chunks of binary data, and a directory table listing the name and location of each of these resources. The WAD file starts out with a 4 character signature, followed by the number of directory entries (a long), followed by the directories file offset. For the registered real version the signature is IWAD. I don't know if the shareware version has a different signature. typedef struct { char signature[4]; //DOOM WAD signature long num_entries; //number of directory entries long offset; //file offset of directory } WAD_Header; The resource data starts immediately after this header. Directory Table --------------- The directory table is list of structures, each consisting of a file offset, byte size, and a resource name. typedef struct { long offset; //file offset of this resource long size; //byte size of this resource char name[8]; //this resource name, always 8 //chars, padded out with zeros. Can //be exactly 8, in which case there //is no trailing zero. } DirectoryEntry; That's all to this. BTW, I just malloc() and read in the entire directory, as it is not all that big. I'm using large model for my C programs. In fact, there appear to be no resources greater than 64k, so you can malloc and read all the resources you want, until you run out of memory. If you were to use a byte editor to view the directory, you would see something like this (showing only the resource names): PLAYPAL COLORMAP ENDOOM DEMO1 DEMO2 DEMO3 E1M1 <- game 1, level 1 THINGS LINEDEFS SIDEDEFS VERTEXES SEGS SSECTORS NODES SECTORS REJECT BLOCKMAP E1M2 <- game 1, level 2 THINGS LINEDEFS SIDEDEFS VERTEXES SEGS SSECTORS NODES SECTORS REJECT BLOCKMAP . . <- skip a whole bunch . SECTORS REJECT BLOCKMAP <- tail end of last game, last level TEXTURE1 <- start of non-level data. TEXTURE2 PNAMES GENMIDI . . <- rest of non-level data. all . bitmaps are listed here. Each level is defined by the resource sequence: ExMy <- game x, level y THINGS LINEDEFS SIDEDEFS VERTEXES SEGS SSECTORS NODES SECTORS REJECT BLOCKMAP Let's examine each of these resource types in more detail. Global Data ----------- PLAYPAL COLORMAP ENDOOM DEMO1 DEMO2 DEMO3 What else to call these? I haven't figured out these yet, but I think that PLAYPAL is a list of palettes (256 red,green,blue triplets?), each used for various situations in the game (ie, radiation suit, beserker mode, gun flashes?). COLORMAP is a list of color index permutations, one list for different game situations (?). Many thanks to Mike Rice for posting the PLAYPAL and COLORMAP info. ENDOOM? Beats me. DEMO1, 2, 3? Must be recorded demos, I guess. Level Data ---------- A level is defined with these resources: ExMy THINGS LINEDEFS SIDEDEFS VERTEXES SEGS SSECTORS NODES SECTORS REJECT BLOCKMAP Let's dissect each of these resources. They are too complex to explain with one sentence each, so I will explain each in turn. If I leave a structure's field uncommented that usually means I don't know what the field means. I'm sorry for the haphazard arrangement of this information. This is very much like a chicken-egg thing, in that many of these resources are intertwined and linked in interesting ways. ExMy Marker for game x level y. I don't know if this is just a placeholder in the directory list, or if this is a real resource with real data. THINGS Resource consists of a list of "sprites", one for each enemy. They are defined with a x,y coordinate, a facing angle, and some flag data. typedef struct { short x,y; //not VERTEX indicies! short angle; //facing angle. degrees not fractional. short type; short options; } THINGS; I don't know how sequences of animated things are specified with this scheme. Obviously something like an imp is just a sequence of bitmaps, with animation accomplished by drawing at various zoom levels and mirrored to walk the other way if needed. These can also be transparent bitmaps. I don't know if DOOM just uses color 0 for the transparent key, or if it is programmable via the bitmap resource format. There also might be a mask table associated with each bitmap. LINEDEFS Resource consists of a list of lines used to define walls. typedef struct { short from,to; //indices into VERTEX list. short flags,special,tag; short side1,side2; //indices into SIDEDEFS list. } Line; flags might be for doors, etc. side1, side2 are indices into the SIDEDEFS list. One for each "side" of the wall? side1 is always filled, but side2 sometimes has a -1, meaning no bitmap on that side? Perhaps walls with filled side1 and side2 are connecting walls, perhaps connecting sectors (and nodes?)? See below. SIDEDEFS Resource consists of a list of wall texture definitions. typedef struct { short flags1; short flags2; char texture1[8]; //name of first bitmap resource char texture2[8]; //name of second bitmap resource char texture3[8]; //name third bitmap resource short sector_index; //index into SECTOR list. sector_index specifies the sector for which this SIDEDEF belongs to. The SECTOR specifies the floor and ceiling attributes of an area bounded by SIDEDEFS. See SECTOR below. VERTEXES Resource consists of x,y coordinates. typedef struct { short x,y; } Vertex; SEGS Resource consists of a list of walls. typedef struct { short from,to; //indices into VERTEX list. unsigned short angle; //fractional angle, wall is facing. short linedef; //LINEDEF index for this line. short flag; //0 for visible, 1 for invisible. //or perhaps 0 for solid, 1 for open? short flag_unknown; //doors? (usually zero). } SEGS; Angles in DOOM are 16-bit words representing a 16-bit fraction (with 180 degrees being 0.5, 90 degrees being 0.25, etc). Facing the screen (in a normal sitting position ), 0x0000 is down, 0x4000 is right, 0x8000 is up, 0xC000 is right. Therefore 0x4000 is really 0x4000/0x10000 = 0.25. 0.25 * 360.0 = 90.0. QED. SSECTORS Resource consists of a list of sectors. Sectors are areas that share a floor and ceiling textures. Open areas in DOOM, such as the acid pit room in game 1 level 1, are modeled using multiple sectors. This resource identifies sectors by specifiying the sector starting index (in SEGS) and the number of "edges" (counting from the starting index in SEGS). typedef struct { short count; short index; } SSECTORS; This table is used by the NODES resource, see NODES below. NODES Resource consists of a list of structures that collectively define a hierarchical "space" tree. typedef struct { short x,y; short dx,dy; short bound[8]; //maxy,miny,minx,maxx, maxy,miny,minx,maxx unsigned short index1,index2; } NODES; This resource is quite complex to explain, so I will defer explaining it until later (see "More about NODES" later). SECTORS Resource consists of a list of sectors. Each entry describes the properties of a single sector. These properties include floor height, ceiling height, floor bitmap texture, ceiling bitmap texture. typedef struct { short floor_height; // short ceiling_height; // char floor_texture[8]; //bitmap resource for floor char ceiling_texture[8]; //bitmap resource for ceiling short light; //initial palette AND mask for this //sector area? short flag1; //crushing ceilings, etc? (mostly zeros). short flag2; //the exit flag? (in E1M1, all zeros except for 1 entry). } SECTORS; This describes an area with a floor and ceiling with certain attributes, including an initial lighting factor. REJECT Unknown. I have no idea. Using my byte editor I filled this entire resource with 0x00. No effect on DOOM. I also tried 0x30, 0xFF, also no effect. However, network play seems to be affected, so perhap this stores the DEATHMATCH teleport coordinates, etc? BLOCKMAP Resource is in two parts. The first is a 2d array: { short w,h; short x[w*h]; } Following this array is a list of numbers. The array represents a grid for the level, on a 128 pixel grid size. Each element in the array is a near pointer to a list of LINEDEF indices that originate or cross that particular 128x128 block in the map. The LINEDEF lists always start with a 0 and end with a 0xffff, even for empty lists. Therefore DOOM's empty lists are always 0, 0xffff. A non-empty DOOM list might be 0, 100, 101, 0xffff. This structure appears to be used only for clipping. Modifying it has no effect on the display of the model, only in the clipping. One can walk through walls, and bullets pass right through. Using BLOCKMAP's linedefs DOOM might figure the players position before the move, then figure the position after the move. If the player crosses a wall, or comes too close (a more likely criteria), the move is prohibited. It could use fixed point math with a 7-bit fraction, which would automatically index them into this array. But then a single unit move would be 1/128 of the grid size, which actually seems to be a reasonably small unit given that bitmaps are 64x64. But a better choice would be 8-bit, or better yet 16-bit, for ease of programming. The array "near pointer" elements are actually WORD indices from the start of the array, so they have to munged slightly before use. More about NODES ---------------- [note: I had written this long before I started this DOC file, so if it appears a little out of place that's because it is!] This is how I think it works. The problem for DOOM is to quickly generate a view given the players coordinates, a problem that screams for a hierarchical "quadtree" type of approach. What the tree helps find is the "SSECTOR" information for whatever sectors the player is in or near. SSECTOR is a list of these structures: struct { short count; //number of sides short index; //index in SEGS of the first side. } Using this DOOM can render the image in "sector" chunks, using the view angle info in SEGS to reject walls that are not facing the player. Anyway, the sectors are organized using the NODES structures. Node element index1 is associated with the bounds[0..3] and element index2 is associated with the bounds[4..7]. If an index high bit (0x8000) is set, then the corresponding bounds[] is a tree leaf, representing the bounding box for the sector SSECTOR[index]. If the high bit is clear, then the corresponding bounds[] is a tree node, representing the bounding box for the entire referenced NODE[index] (that is the min/max for all bounds[0..8] in NODE[index]). The last node is the root node in the tree. Using game 1, level 1, here is the first node from NODES: Node index 0: x,y,dx,dy: 2632 -3792 56 -128 bounds[0..8]: -3792 -3920 2435 2688 -3776 -3920 2632 2720 index1,index2: 8001 8002 This a pure leaf node, with bound[0..3], -3792 -3920 2435 2688, the bounding box for SSECTOR[1], and with bound[4..7], -3776 -3920 2632 2720, the bounding box for SSECTOR[2]. Now here's node 3, index 2, from NODES: Node index 2: x,y,dx,dy: 2688 -3776 32 -128 bounds[0..8]: -3776 -3920 2435 2720 -3776 -3904 2688 2784 index1,index2: 0 1 This is a pure node node. Note that index1 has the high bit clear, indicating bounds[0..3] is the bounding box for all of node index 0. Sure enough, -3776 -3920 2435 2720, is the bounding box for all of node 0's bound[0..8], -3792 -3920 2435 2688 -3776 -3920 2632 2720. If you checked index2, 1, you'd see the same thing. And there are also mixed nodes, with the one child a leaf and the other a node. NODE index 3 is such a mixed node, with index1==8000 and index2==2. BTW, I wrote a program to verify this, and it verified 100%. I'm not sure about the node x,y,dx,dy stuff. x,y appears to be a point on the intersection of bound[0..3] and bound[4..7], inside the bounds of the entire node. I don't know about dx and dy. Here are some more observations about NODES: * each x,y entry is also defined as a VERTEX. * it appears, and I have not really verified this, that x,y,dx,dy defines a SECTOR wall that is marked as invisible. This would link "visible" sectors together by their common "invisible" walls (ie, two rooms connected by a window would have the common window wall being a "node" in this tree). This is all rather nebulous at this point. Game 1 Level 1 Information -------------------------- All of my data gathering has been done using E1M1. My initial work was done using the shareware version, and then I moved up to the retail version as soon as it arrived. Here are some numbers from this level, for the number of entries in each resource. I might be off a little, but these will give you a rough idea. THINGS 130 LINEDEFS 452 SIDEDEFS 622 VERTEXES 437 SEGS 695 SSECTORS 212 NODES 211 SECTORS 83 REJECT no data collected on this puppy. BLOCKMAP a 34x23 block array, followed by mucho data. Curios and Other Cool Stuff --------------------------- Some of the DOOM cheat keys are really interesting: IDMYPOS - DOOM displays current angle and x,y information. IDSPISPOPD - disables clipping. interesting when trying to figure out what DOOM is drawing (or not drawing!) What I don't Know ----------------- Here is a list of what I don't know about the WAD format: * how would I insert my own level data? I know I could just simply replace an entire level with my own, but how would I add another level. * how does one determine if DOOM is shareware or not? Id doesn't want people providing level editors for the shareware version, and I certainly agree with that. * More about PLAYPAL, COLORMAP resources. * The D_E1M1,D_E1M2,etc, resources. More level information? These appear shortly after the end of the last game's level's resources. * The exact format of a bitmap resource. I'm assuming something like the following: short image_w; short image_h; byte image_data[]; Data stored in column major format. But I haven't even looked. What Now? --------- There! That's everything I know about the WAD format. Now What? Well, I just don't know. Perhaps one of you will take this to the next level (!). Good Luck!