ref: 8cdf6b06b9805fe39e72410549175e37415eb056
dir: /man/6/wl3d/
.TH WL3D 6 .SH NAME wl3d \- Wolfenstein 3-D data formats .SH DESCRIPTION Several different types of data are used in Wolfenstein 3-D, each stored in specific ways. In the description of the data files below, version-specific extensions are omitted. These files are: .TF gamemaps .TP .B audiohed PC Speaker, Adlib, digital effects and music offsets in .BR audiot . .TP .B audiot Uncompressed PC Speaker, Adlib, digital effects and music lumps. .TP .B config Saved game settings and highscores. .TP .B gamemaps Map lumps. .TP .B maphead Map offsets in .BR gamemaps . .TP .B savegam? Saved games. .TP .B vgadict Huffman dictionary for lumps contained in .BR vgagraph . .TP .B vgagraph Fonts, pictures, tiles and screens, encoded for .SM VGA graphics cards. .TP .B vgahead Graphics offsets in .BR vgagraph . .TP .B vswap Wall textures, sprites and raw pcm audio. .PD .PP Integers are stored in little-endian byte order. In the notation for file and lump formats below, the number of bytes in a field is given in brackets after the field name. The notation .IR a [ s ] denotes an unterminated array .I a of .I s .SM ASCII characters. Empty brackets denote a variable-length field. Single-field n-dimensional arrays are defined using n sets of brackets containing the number of array elements, then the size of an element in bytes also between brackets. Curly braces surrounding multiple fields denote arrays of multiple fields. These are adjoined by the number of elements in brackets. In the interest of clarity, complex fields are usually denoted as two-dimensional arrays of variable-length element size, and defined subsequently. .PP In addition, game versions are denoted as .B wl1 for Wolfenstein 3-D 1.4 shareware, .B wl6 for Wolfenstein 3-D 1.4 retail, .B sdm for Spear of Destiny 1.0 demo and .B sod for Spear of Destiny 1.0 retail (including mission packs). Other versions are outside the scope of this document. .SH SOUND EFFECTS AND MUSIC .SS Audiohed .RS .IR start [4] { .IR end [4] .IR starts [4] .RI }[ nsfx*3+nimf-1 ] .IR end [4] .RE .PP .B Audiohed contains .I start and .I end offsets into .B audiot for sound effects and music. These are stored contiguously, and the .I end offset of one lump is the .I start offset of the following, such as the last offset is the .I end offset of the last lump. .PP The order and number of offsets is significant. The offsets reference an identical number of PC Speaker, then of Adlib, then of digital sound lumps. Any following offsets reference music lumps. .PP The number of sound effects and music lumps, as well as their references, are hardcoded per-version in the engine: .TF wl1/wl6 .TP .B wl1/wl6 87 sound effects, 27 music lumps .TP .B sdm/sod 81 sound effects, 24 music lumps .PD .PP No checking of their correct order or number is done, and any extra offsets are ignored. Zero-sized lumps are valid and are simply never read. Digital sound effect offsets are always ignored, but must exist. Some sound effects and music music tracks are never used. .SS Audiot .RS .IR pc [ nsfx ][] .IR al [ nsfx ][] .IR dig [ nsfx ][] .IR imf [ nimf ][] .RE .PP .B Audiot contains .I nsfx uncompressed sound effects, and .I nimf music lumps in .SM IMF format. Each sound effect has a PC Speaker, Adlib and digital version. .PP Each type of lump is stored in a specific format detailed below. No digital sound is ever stored in .BR audiot , having raw pcm lumps in .B vswap instead. .SS PC sound effect .RS .IR size [4] .IR priority [2] .IR freq [ size ] .IR ignored [1] .RE .PP Undocumented here. Note that .I priority isn't necessarily the same as that of the Adlib version. .SS Adlib sound effect .RS .IR size [4] .IR priority [2] .IR cfg [10] .IR ignored [6] .IR octave [1] .IR pitch [ size ] .IR tag [] .RE .PP The sampling rate is 140 Hz. .I Pitch is an array of 1-byte values to be written to an .SM OPL2 chip's first channel registers, of size .IR size . A value of zero corresponds to writing a zero to .SM OPL2 register .LR 0xb0 . Any different value is written to register .L 0xa0 before writing .I octave to its corresponding 3 bit field in register .LR 0xb0 . The key is enabled simultaneously. .PP .I Cfg is a byte array of values to be written to a list of registers, configuring the first channel's operators. These registers are, in order: .LR 0x20 , .LR 0x23 , .LR 0x40 , .LR 0x43 , .LR 0x60 , .LR 0x63 , .LR 0x80 , .LR 0x83 , .LR 0xe0 , .LR 0xe3 . .PP Since only one Adlib sound effect can play at any one time, if an effect is already being played and its .I priority is lower than the new one, it is interrupted, then substituted. .PP At the end of playback, the engine resets the values of the instrument registers and writes a zero to register .LR 0xb0 . .PP .I Ignored contains 1 byte intended to be written to register .LR 0xc0 , but never is, then 5 fields only used by Muse. .I Tag is a variable-length field suffixed by Muse and is also ignored. .SS Digital sound effect No such lump is ever stored in .BR audiot . Regardless, the appropriate number of references in .B audiohed must exist (as zero-sized lumps). .PP Instead, sound effects are stored as raw pcm in .BR vswap , in a format detailed in a different section. .SS Music .RS .IR size [2] { .IR register [1] .IR value [1] .IR delay [2] .RI }[ size ] .IR tag [] .RE .PP Music is stored in the .SM "id software Music Format", or .SM IMF for short. .PP .I Size is the total length of actual music data and must be a multiple of 4. The data is a series of commands to be written to an .SM OPL2 compatible chip. Each associates a .I register and .I value pair with a .IR delay , expressed in multiples of 1/700 seconds. A zero delay means to write the next command immediately. .I Tag is a variable length field suffixed by Muse, and is ignored. .SH MAPS .SS Maphead .RS .IR tag [2] .IR off [60][4] .RE .PP .B Maphead is an array of file offsets into .BR gamemaps . .I Tag is a special value used during .SM RLEW decompression, usually .LR 0xabcd , and thus also contained in the decompressed map lump. .PP While the number of offsets is constant, the number of maps actually contained in .B gamemaps is version dependent: .TF wl1 .TP .B wl1 10 maps .TP .B wl6 60 maps .TP .B sdm 2 maps .TP .B sod 21 maps .PD .PP Missing maps have an offset of zero. As zero offsets are not handled specially, the references to these maps will point to the first map. .PP An offset of 0xffffffff marks the map lump as sparse. In this case, the engine will not initialize the map's reference, resulting in a crash should it be accessed. .IR wl3d (1) exits if it reads such an offset. .SS Gamemaps .RS { .IR pls [3][4] .IR pll [3][2] .IR dx [2] .IR dy [2] .IR name [ s ] .RI }[60] .IR planes [3][ dx * dy ] .RE .PP Maps are decomposed into three planes. .B Gamemaps holds an array of map headers, followed by doubly-compressed plane data. .I Pls and .I pll are respectively arrays of offsets and lengths for each plane. Only the first two planes are ever used. .I dx and .I dy are the planes' dimensions, and must both be 64. .I Name is an unused unterminated .SM ASCII string. .I Planes stores contiguously each plane's data. .PP Each map plane is first compressed using .SM RLEW, then further using what is eponymously referred to as .SM Carmack compression. .SS RLEW Compression [words] .SS Carmack Compression [words] .SS Plane 0 The first plane is an array of [words]. .SS Plane 1 The second plane is an array of [other words]. .PP There are static limits for objects on the map: .TF "static objects" .TP .B actors 150 (including the player) .TP .B doors 64 .TP .B static objects 399 .SH GRAPHICS Graphics are either static data loaded in the executable, or huffman-compressed lumps contained in .BR vgagraph . .B Vgahead is used in conjunction with .B vgadict to read and uncompress each lump upon retrieval. Pixel data is usually encoded as a palette index, and is used in conjunction with a 256 color palette. .SS Vgahead .RS .IR off [ np ][3] .RE .PP .I Vgahead is an array of 3 byte offsets into .I vgagraph for all lumps contained therein. .SS Vgadict .RS { .IR a [2] .IR b [2] .RI }[256] .RE .PP This file contains a dictionary used for decompression of Huffman-encoded lumps in .IR vgagraph . .SS Huffman Compression [words] .SS Vgagraph .RS .IR pt [ np ][] .IR fnt [2][] .IR pic [ np ][] .IR t8 [] .IR scr [ n ][] .RE .PP .I Vgagraph contains a series of Huffman-encoded graphics lumps of various types. Each lump is prefixed by its uncompressed size. The first lump, .IR pt , contains the sizes for .I np pictures, retrieved from .IR pic . .I Np can be obtained from .IR pt 's uncompressed size. The following sections detail each lump type. .SS Fonts .RS .IR dy [2] .IR off [256][2] .IR dx [256] .RE .PP Fonts are a collection of 256 variable-width .SM ASCII characters. .I Dy is the global height in pixels of each one. Every character, indexed 0-255, also has a corresponding width in pixels in .I dx and a byte offset into the lump in .I off containing its pixel data. The data is an array of bytes of size .I dy .SM * .IR dx [ n ], to be translated to the character's location on the screen. A pixel's color is overwritten with the engine's current foreground color when a non-zero byte in the character's pixel data occurs. .PP Exactly two fonts are always defined. .SM ASCII characters within the printable range have valid definitions at the same locations. Some characters for special use are defined beyond, such as fixed-width numerals at index 129. .SS Pictures .RS .BR pt : .IR dx [2] .IR dy [2] .br .BR pic : .IR p [dy][dx] .RE .PP .I Pt is a separate lump containing an array of paired integers, specifying the width and height in pixels of each picture. The number of records in that lump corresponds to the number of picture lumps in .IR pic . Each picture is then a Huffman-encoded .I dx by .I dy graphic in .SM VGA format, that is, with pixels stored in 4 .SM VGA color planes rather than contiguously. .SS Tiles These tiles are exclusively of size 8x8, and thus stored as arrays of 64 bytes. All tiles are stored contiguously in a single lump following the last pic lump. Their number is version dependent, but only the first 8 tiles are ever used. .PP This lump decodes incorrectly in all game versions and results in reads past the allocated buffer (or past the end of the lump). .IR Wl3d (1) skips it entirely. .SS Screens and demos .RS .IR misc [ ns ][] .IR demo [ nd ][] .IR end [ ne ][] .RE .PP .I Misc contains fullscreen graphics and additional palettes used in specific hardcoded and version dependent instances. .I Demo is an array of 4 demos (1 for .BR sdm ). Finally, .I end contains other fullscreen graphics for specific uses, notably in epilogue sequences. .PP .RS .BR demo : .IR map [1] .IR size [2] .IR pad [1] { .IR bt [1] .IR dx [1] .IR dy [1] .RI }[ size-4 ] .RE .PP The only metadata contained in demo lumps is .IR map , the 0-indexed map number. Game version is not recorded. Game difficulty is always set to the hardest. .I Map is followed by the total .I size of the lump and an unused byte. The original .SM DOS binaries do not record demos bigger than 8192 bytes. .PP Demos are recorded and played back at a fixed rate of 4 tics per frame. .IR Bt , .IR dx , and .I dy contain player input for the duration of the frame and correspond respectively to pressed game keys, total horizontal movement (turning) and total vertical movement (forward displacement) deltas. .I Bt is an 8-bit array of the main game keys, regardless of input method. These are, from least to most significant bit: fire, strafe, run, open, knife, pistol, machine gun and gatling gun. The movement deltas are bounded from -100 to 100. .SS Static data Some of the graphics data is stored in the executable and thus cannot be altered without modifying it or the loaded object files. These are the base color palette and intro screen. Each have a .B wl1/wl6 and a .B sdm/sod version, but only one is present. .B Sdm has its own intro screen as well. .PP The base color palette is stored as any other palette, and is the one used in most circumstances. Several other palettes are generated based on it, notably for screen flashes and fade effects. .PP The intro screen is a static screen displayed on startup while verifying available memory and hardware, then changed depending on the results. It is an uncompressed 320x200 graphic lump. .BR wl3d (1) separates the intro screen from the binary as a distinct file formatted as a 320x200 byte array, and loaded on startup. .SS Palettes .RS { .IR r [1] .IR g [1] .IR b [1] .RI }[256] .RE .PP Palettes in Wolfenstein 3-D are arrays of 768 bytes arranged in triplets of red, green and blue values. Besides the palette contained in the executable and other generated ones, pic lumps may be palettes used in specific, hard-coded instances. .SH VSWAP .RS .IR nch [2] .IR so [2] .IR po [2] .IR off [ nch ][4] .IR sz [ nch ][2] .IR wl [ so ][] .IR sp [ po-so ][] .IR pcm [ nch-po ][] .RE .PP .B Vswap is divided into three sections, each subdivided into lumps, and then again into chunks. .I Nch is the total number of chunks in the file. .I So and .I po are then the chunk indices denoting the start of respectively the sprite and pcm sections. Immediately following are two arrays describing each chunk, .IR off for file offsets, and .IR sz , for lengths in bytes. .PP A chunk of size 0 is skipped. This is permitted when the specific lump is never referenced, which can occur in the .B wl1 and .B sdm versions, where only a limited number of maps and resources are used. Attempting to load a sparse lump will cause a crash. .PP The rest of the file contains section data. .SS Wall textures Wall tiles are 64x64 byte arrays of palette indices. Because they are drawn vertically, they are stored as an array of coloumns. In other words, if drawn as is using a given palette, the tile would appear rotated by -90° then flipped along its vertical axis. The last 8 wall textures are used for doors. .SS Sprites .RS .IR lx [2] .IR rx [2] .IR cofs [ rx-lx+1 ][2] .IR pad [] .IR cmd [] .IR pad [] .IR data [] .br .BR cmd : { .IR se [2] po [2] ss [2] }[] .IR nul [2] .RE .PP Sprites are stored as variable-length arrays of offsets and visible pixel data, with an implicit maximal size of 64x64. .IR Lx and .IR rx define the 0-indexed left and right-most coloumns containing visible pixels, and must respect the following restrictions: .PP .RS .BR lx \ ∈\ {0,1,...,63} .br .BR rx \ ∈\ {32,33,...,63} .br .BR lx \ ≤\ rx .RE .PP Sprites are drawn centered on the 32nd coloumn. A left bound equal to or greater than 32 will offset the sprite to the right. Because of a bug in the original .SM DOS binaries, such a left bound results in reads past the lump and excess pixels may show up as garbage on the screen. .PP The following variable-length array, .IR cofs , contains one offset into sprite data for each visible coloumn. Each offset is set from the beginning of the lump, and points to a .I cmd array. .I Ss / 2 and .I se / 2 are, respectively, an upper and lower bound, defining a contiguous vertical strip of pixels to draw within a coloumn, with .I po + ss / 2 an offset into the sprite lump pointing to the strip's palette indices. The .I cmd array is terminated by a 16-bit wide zero. Since .I ss and .I se are used as word table offsets, they are multiplied by two. .SS Raw pcm .RS { .IR chunk [ (size-1)/4096+1 ][] .RI }[ npcm ] { .IR index [2] .IR size [2] .RI }[ npcm ] .RE .PP The sampling rate for this data is 7 kHz. Each sample is a 8 bit unsigned integer. Pcm lumps are segmented into chunks of 4096 bytes or less. .PP The very last chunk in .B vswap is a table of two 16 bit integers per pcm lump. .I Index is the zero-based index of the lump's first chunk relative to the first pcm chunk. .I Size is the sum in bytes of the lengths of the lump's chunks. .PP The valid references for each pcm sound effect (different than those for regular sound effects), as well as their number, are hardcoded in the engine: .TF wl1 .TP .B wl1 46 pcms (20 non empty) .TP .B wl6 46 pcms .TP .B sdm 40 pcms (25 non empty) .TP .B sod 40 pcms .PD .PP However, neither .I npcm nor the pcm table actually reflect this number. The number in parentheses indicates the actual number of valid pcm lumps for versions which may contain empty chunks. These are not contiguous, and care must be taken to skip all zero-sized chunks referencing each individual sparse pcm lump, as indicated in the pcm table. .SH "CONFIGURATION FILE AND HIGHSCORES" [words] .SH "SAVED GAMES" .RS .IR name [32] .IR game [] .IR map [] .IR chksum [4] .br .PP .BR game : .IR gm1 [] .IR score [] .IR plr [] .IR gm2 [] .IR stat [] .IR misc [] .IR epstat [] .br .BR gm1 : .IR difc [2] .IR map [2] .br .BR score : .IR oldpt [4] .IR pt [4] .IR to1up [4] .br .BR plr : .IR lives [2] .IR hp [2] .IR ammo [2] .IR keys [2] .IR bestw [2] .IR wep [2] .IR lastw [2] .br .BR gm2 : .IR facefrm [2] .IR atkfrm [2] .IR wfrm [2] .IR ep [2] .br .BR stat : .IR sp [2] .IR tp [2] .IR kp [2] .IR stot [2] .IR ttot [2] .IR ktot [2] .IR tc [4] .br .BR misc : .IR killx [4] .IR killy [4] .IR won [2] .br .BR epstat : { .IR k [2] .IR s [2] .IR t [2] .IR tm [4] .RI }[ nmap ] .br .PP .BR map : .IR tilemap [] .IR obj [] .IR stats [] .IR doors [] .IR pwall [] .br .BR tilemap : .IR tl [64*64] .IR tlo [64*64][2] .br .BR obj : { .IR on [2] .IR tc [2] .IR type [2] .IR stt [2] .IR f [1] .IR Δr [4] .IR dir [2] .IR x [4] .IR y [4] .IR tx [2] .IR ty [2] .IR aid [1] .IR vwx [2] .IR vwh [2] .IR trx [4] .IR try [4] .IR θ [2] .IR hp [2] .IR v [4] .IR tmp [3][2] .IR node [2][2] .RI }[ nobj+1 ] .br .BR stats : .IR statse [2] { .IR tx [1] .IR ty [1] .IR vis [2] .IR spr [2] .IR f [1] .IR itm [1] }[400] .br .BR doors : .IR dopen [64][2] { .IR tx [1] .IR ty [1] .IR vert [2] .IR lck [1] .IR φ [2] .IR tc [2] }[64] .br .BR pwall : .IR φ [2] .IR x [2] .IR y [2] .IR dir [2] .IR dopen [2] .RE .PP Savegame files contain game and map state used to regenerate a live game. It can be split into lumps containing variable-size fields. .PP A checksum of each written byte is calculated and appended. It is tested upon subsequent loading to guard against tampering. Its formula is: .RS .BR chksum \ = \ ∑\ n[i]\ XOR\ n[i+1] .RE .PP A save file does not contain the entire state necessary to restore a game. The map must first be loaded and initialized, then the information from the save file is used to overwrite some of the resulting state. .IR Wl3d (1) uses a different save game format, containing a complete state. Converting between the two formats is possible. .PP .I Name contains a nul-terminated ascii string, which may be empty. It is the savegame's name displayed ingame using the second game font, and may be truncated depending on its total on screen width. .SS Game state .PP The first two fields are the game difficulty (0-3, from easiest to hardest), and a zero-indexed map number (0-59). .PP Then follow three scoring fields: the points at the start of the level, current points, and the points needed for another 1-up reward. .PP Next are seven player state fields: number of lives (0-9), hit points (1-100), ammo points (0-99), collected keys bitfield, best available weapon (1-3), currently equipped weapon, and last switched from weapon. .PP The subsequent four fields store frame counters for face and firing animations, and an episode index (0-5 or 0 for .IR sod/sdm ), derived from the map index. .PP Seven map statistics fields follow: current secrets, treasure and kills counts, then their respective maximums, and a level time in 70 Hz tics. .PP The ensuing three fields are used when a victory condition is triggered, and contain the coordinates of a reference object and a victory flag. .PP The last lump is an array of scoring information for each of the current episode's maps: percentage of kills, secrets found and treasure collected, and level time in 70 Hz tics. .I Nmap is 8 for .I wl1/wl6 and 20 for .IR sdm/sod . .SS Map state The .I tilemap is an array of byte-sized tile numbers, followed by an array of object pointers. Values lower than 256 in .I tlo denote item numbers. .PP The subsequent lump is a variable-size array of objects, defined by the following fields: active flag, state timer, object type, state pointer, object flags, displacement counter, direction, global and tile coordinates, area number, rendering dimensions, angle (used for player and projectiles), hit points, speed, three temporary variables used for timing and drawing, and next and previous node pointers. The .I obj lump is terminated by a trailing object definition with .LR 0xffff in its .I on field. .PP Afterwards is stored a fixed-size array of static objects, preceded by an end pointer, referencing past the last element. Each element is defined by tile coordinates, a pointer to an array of visible elements, a sprite number, a flags field, and an item number. .PP Next is stored a fixed-size array of doors, prepended by an array of door positions (0-0xffff, from closed to open). Each is ascribed tile coordinates, a vertical map position flag, a lock bitfield, an open/close phase, and a timer. .PP The last lump describes an active pushwall: opening phase, global map coordinates, direction, and fine position. .SH "SEE ALSO" .IR opl2 (1) , .IR pcmconv (1) , .IR wl3d (1) .PP The YMF262 (OPL3) programming manual.