Saved games in Doom are stored in a binary format with the DSG extension. There is a total of six slots for saved games, each with the corresponding doomsav*.dsg file, numbered from 0 to 5.

Because Doom originally had to work in rather constrained memory conditions, it had to do writing and reading saved games in a simplistic and straightforward way. For this task, it does not make use of its Zone memory allocation, instead employing a static buffer (in fact, it even reuses a portion of the screen buffers memory) and writing/reading into it byte-by-byte. While it works much faster than the direct file input and output on an old system like DOS, it has an inherent problem of possibly overrunning the static buffer. The buffer size is set at 180,244 bytes (0x2c000) and, unlike with the demo recording, cannot be increased by the user. With large and complex levels the amount of data that must be saved can easily exceed that amount, causing a savegame buffer overflow. In this case the game exits with an error message.

The following are the details of the savegame format. Because most of its data is interpreted by the engine in a programmatical way (it loads the level and restores the game state by going through its aspects), this virtually nullifies the feasibility of the manual hacking of saved games, one exception being the player data.


40 bytes in size.

Offset Length Name Description
0x00 24 savename Savegame description string, padded with zeroes if less than 24 bytes.
0x18 16 version Game version string, in format "version %i". This stores the internal engine version, e.g. 109 for 1.9, or 110 for LinuxDoom. If savegame version differs from that of the engine, the loading is aborted, though no error message is displayed.

General level parameters[edit]

10 bytes in size.

Offset Length Name Description
0x28 1 gameskill a skill of the game, 1-5
0x29 1 gameepisode an episode of the game, 1-4
0x2a 1 gamemap current level, 1-32
0x2b 1x4 playeringame[] boolean byte flag for each connected player.
0x2f 3 leveltime Number of tics since the level was started. (least significant byte last, first is unused, so max stored time must be 0xFFFFFF?)

Players block[edit]

Each player block is 280 bytes in size, padded to 4 bytes. First block is always present; the rest are saved only if corresponding player is currently in game. When loading player data, Doom does not actually check for connected network nodes, simply placing and setting up map things according to the saved game's contents.

TODO: what actually happens after loading when there are not enough players connected?

The following is the binary contents of the player_t structure for the first player. Following player blocks, if any, are offset by 0x118 each.

Offset Length Name Description
0x32 4 mo pointer to the player's mobj_t body
0x36 4 playerstate alive/dead/respawning
0x3e 4 cmd ticcmd_t (contains player input)
0x42 4 viewz absolute height of the player's view point (viewheight + mo->z)
0x46 4 viewheight relative height of the player's view
0x4a 4 deltaviewheight delta to apply to the player's view for temporary changes such as hitting the floor
0x4e 4 bob weapon sprite bobbing amount
0x52 4 health mirror of mo->health, used by status bar and during intermissions
0x56 4 armorpoints amount of armor possessed
0x5a 4 armortype 0 for none, 1 for green, 2 for blue
0x5e 4 powers[] remaining time of the invulnerability powerup (tics)
0x62 4 true if the berserk powerup is active (also used for the red haze transitions)
0x66 4 remaining time of the partial invisibility powerup (tics)
0x6a 4 remaining time of the radiation suit powerup (tics)
0x6e 4 true if the player has the computer map powerup
0x72 4 remaining time of the light amplification visor powerup (tics)
0x76 4 cards[] blue keycard
0x7a 4 yellow keycard
0x7e 4 red keycard
0x82 4 blue skull
0x86 4 yellow skull
0x8a 4 red skull
0x8e 4 backpack true if the player has the backpack
0x8e 4x4 frags[] frags for each player; actual frag counts are calculated on the fly by adding these numbers together in a loop
0xa2 4 readyweapon weapon currently equipped
0xa6 4 pendingweapon pending weapon (10 if not changing to a new weapon)
0xaa 4x9 weaponowned[] weapons owned (1-9)
0xce 4x4 ammo[] ammo counts (1-4)
0xde 4x4 maxammo[] max ammo capacities
0xee 4 attackdown attack button held down
0xf2 4 usedown use button held down
0xf6 4 cheats bitmask for cheats currently active
0xfa 4 refire 1 if the player has been firing for some time (diminished accuracy)
0xfe 4 killcount number of monsters killed
0x102 4 itemcount number of items collected
0x106 4 secretcount number of secrets discovered
0x10a 4 message pointer to the last displayed message
0x10e 4 damagecount number used to calculate index into PLAYPAL for damage
0x112 4 bonuscount number used to calculate PLAYPAL index for powerup pickup flash
0x116 4 attacker pointer to last attacker mobj_t
0x11a 4 extralight Amount to add to sector light levels for gun flashes
0x11e 4 fixedcolormap powerup COLORMAP
0x122 4 colormap used to remap the colors of the player's sprite
0x132 16x2 psprites[] player weapon sprite and flash sprite data
0x146 4 didsecret true if the player has visited the secret level

Sectors block[edit]

This (and subsequent) blocks may greatly vary in size according to the map data and current playsim state, thus their offsets must be calculated. This also makes manual hacking of these virtually impossible.

Each entry is 14 bytes in size, number of entries is the length of the SECTORS lump divided by the 26 bytes.

Offset (relative) Length Name Description
0x00 2 floorheight (in units)
0x02 2 ceilingheight (in units)
0x04 2 floorpic internal flat index for floor (not lump name)
0x06 2 ceilingpic internal flat index for ceiling
0x08 2 lightlevel
0x0a 2 special
0x0c 2 tag

Lines block[edit]

An entry's size is 16 bytes for one-sided lines, and 26 bytes for two-sided ones.

The number of entries is the length of the LINEDEFS lump divided by 14 bytes.

Offset (relative) Length Name Description
0x00 2 flags
0x02 2 special
0x04 2 tag
Note: the sidedef order is front, then back. A given side's data is not stored if the line does not have a valid one defined.
0x06 2 textureoffset horizontal offset (in units)
0x08 2 rowoffset vertical offset (in units)
0x0a 2 toptexture internal index of upper texture
0x0c 2 bottomtexture internal index of lower texture
0x0e 2 midtexture internal index of middle texture
0x10 2 textureoffset horizontal offset (in units)
0x12 2 rowoffset vertical offset (in units)
0x14 2 toptexture internal index of upper texture
0x16 2 bottomtexture internal index of lower texture
0x18 2 midtexture internal index of middle texture

Mobj block[edit]

Every mobj_t in the thinker list is saved. Each entry is 161 bytes, padded to 4.

Offset (relative) Length Name Description
0x00 1 thinkerclass 1 to continue reading, 0 to terminate the list
0x01 4x3 thinker thinker_t structure (contains linked list pointers and the address of the P_MobjThinker routine)
0x1d 4 x position
0x11 4 y
0x15 4 z
0x19 4 snext links in sector thinglist
0x1d 4 sprev
0x21 4 angle orientation as a binary angle measure (1 degree = 0xb60b60)
0x25 4 sprite sprite index
0x29 4 frame frame index (also stores fullbright as a bitflag)
0x2d 4 bnext links to next object in the same BLOCKMAP cell
0x31 4 subsector pointer to the object's subsector
0x35 4 floorz the closest floor level over all contacted sectors
0x39 4 ceilingz the closest ceiling level over all contacted sectors
0x3d 4 radius initially copied from mobjinfo_t; changes to 0 when a monster is crushed
0x41 4 height initially copied from mobjinfo_t; reduces when a monster dies, changes to 0 when crushed
0x45 4 momx component of speed along the x axis in units per tic
0x49 4 momy component of speed along the y axis in units per tic
0x4d 4 momz component of speed along the z axis in units per tic
0x51 4 validcount counter used to keep track of whether an mobj has already been touched during clipping operations
0x55 4 type MT_* type (index into mobjinfo[])
0x59 4 info pointer to mobjinfo_t structure (same as &mobjinfo[mo->type])
0x5d 4 tics tics left in current state; initially copied from state_t, counts down every call to P_MobjThinker
0x61 4 state pointer to current state_t
0x65 4 flags flags, initially copied from mobjinfo_t; may change at runtime in response to many different events
0x69 4 health current health; can be zero or negative if the thing is dead.
0x6d 4 movedir 0-7, representing movement direction for monsters
0x71 4 movecount when 0, a monster may select a new direction for movement
0x75 4 target pointer to the current enemy (or the owner for projectiles). Zeroed when save is loaded because there is no pointer swizzling scheme. This results in monsters becoming dormant after restoring a game.
0x79 4 reactiontime a delay before the next attack for monsters; delay until can move again when teleporting for players
0x7d 4 threshold a threshold time before a monster will change targets due to being hit; does not apply to arch-viles
0x81 4 player pointer to the player_t for player mobjs; NULL for any other type of object
0x85 4 lastlook player number last looked for by monsters
0x89 4x5 spawnpoint contents of spawn point mapthing_t structure, used for monster respawning
0x9d 4 tracer thing being tracked by a homing missile; also used by arch-vile fire. Zeroed when loading saves, nullifying all tracking missiles and arch-vile spells in progress.

Specials block[edit]



Offset (relative) Length Name Description
0x00 1 specials tc_ceiling


Offset (relative) Length Name Description
0x00 1 specials tc_door


Offset (relative) Length Name Description
0x00 1 specials tc_floor


Offset (relative) Length Name Description
0x00 1 specials tc_plat

Light flashes[edit]

Offset (relative) Length Name Description
0x00 1 specials tc_flash

Light strobes[edit]

Offset (relative) Length Name Description
0x00 1 specials tc_strobe

Light glows[edit]

Offset (relative) Length Name Description
0x00 1 specials tc_glow

Block terminator[edit]

tc_endspecials (0x07?)

Consistency Marker[edit]

To ensure consistency, the game expects the last byte of the savegame read into the buffer to be 0x1d. Otherwise it deems the file corrupt and bombs out with an error message.