Browsing qwx/wl3d at commit 20c37be7a91df8e4311c69f7ef3e5b8d0448636e: /man/6/wl3d

view raw
.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/3-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.
.IR 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.