Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Save-game containers

SAVEGAME.SAV and per-map .TSG files store saved engine state in tagged chunks.

Overall Structure

[SVGM — save metadata]
[Image and state chunks — length-prefixed payloads]
[END — four-byte terminator]

Chunk lengths are big-endian. Numeric fields within the SVGM payload are little-endian. Chunks have no alignment padding.

OffsetSizeTypeEndianNameDescription
0x004[u8; 4]—nameFour-byte chunk identifier.
0x044u32BEdata_lengthPayload size, excluding the eight-byte header.
0x08data_length[u8]—dataChunk payload.

The terminal identifier is END , including its trailing space. It has no length field or payload. An empty chunk still has an eight-byte header with a zero length.

SVGM metadata

Version 1.31 uses a 272-byte metadata payload. Offsets below are relative to the payload, after the chunk header.

OffsetSizeTypeEndianNameDescription
0x004[u8; 4]—versionSave-format version text, 1.31.
0x044u32LEunknown_04Engine compatibility value.
0x084u32LEunknown_08Engine compatibility value.
0x0C128[u8; 128]—descriptionSlot display text, ending at the first NUL or the field boundary.
0x8C4u32LEworld_idCurrent world identifier, corresponding to WORLD.INI.
0x90128[u8; 128]—bitmap_filenameStored bitmap filename, ending at the first NUL or the field boundary.

The engine checks the version and compatibility values before restoring a save. An incompatible format version prevents state restoration. A mismatch in the two compatibility values displays a warning but does not prevent state restoration when the format version matches. The stored bitmap filename is separate from the THMB thumbnail and SCR3 screen-image chunks.

State chunks

Save containers include global state and per-world state. Object and script records depend on the corresponding RGM scene and runtime definitions. They are not replacement scene assets.

The world loader constructs object, rope and code owners before applying their saved records. The SMOB, SMRP and SMCO readers consume the number of records established by that initialized world, rather than deriving a count from the chunk length. SMSH includes a count, which must match the initialized shell count.

Each SMCO record starts with a four-byte little-endian code-object identifier followed by a 204-byte packed runtime body. The reader resolves that identifier against the initialized code owners. Nonzero saved script references are offsets rebased against the loaded world’s code and native-hook bases. Live list links and the code owner’s runtime-only pointer are retained instead of accepting the saved pointer values. Matching a world number alone therefore does not supply the owners and script bases needed to interpret these records.

SMOB, SMRP, SMCO, and SMSH hold object, rope, code, and shell state, respectively. SVCM holds camera records, and SMFM holds family references. Other state chunks include SVGF, SMGR, SVRG, SVGV, SVG2, SVIT, SVCB, SVMV, and SVMD.

Temporary .TSG snapshots use the same container and metadata layout. Their image chunks can be empty. The engine can restore these snapshots during a world reload after initializing the player.

Global flags

The SVGF payload stores the flag values and state bytes in installed global flag order. Its payload length is 4 + 5 × count. Numeric fields are little-endian.

OffsetSizeTypeNameDescription
04u32countNumber of global flags.
44 × count[i32; count]valuesCurrent global flag values.
4 + 4 × countcount[u8; count]statesNative flag kind and latch state.

General state and clock

Version 1.31 uses a 1,542-byte SVGV payload. It concatenates the retained global fields without alignment padding. The clock fields below are four-byte little-endian words. Offsets are relative to the payload.

OffsetSizeTypeNameDescription
0x0944u32measurement_baselineLast FPS measurement baseline in BIOS ticks.
0x0984i32measurement_remainderBIOS ticks retained toward the next measurement window.
0x09C4u32measured_fpsMeasured frame rate.
0x0A04u32next_scaleNext movement scale, with eight fractional bits.
0x0A44u32nominal_incrementLast computed whole nominal-tick increment.
0x0A84u32fine_incrementLast computed finer-clock increment; eight units per nominal tick.
0x0AC4u32warmupRemaining startup invocations.
0x0B44u32bios_sampleLast sampled BIOS tick.
0x0B84u32measurement_framesFrames counted toward the next FPS measurement.
0x0C04u32smoothed_fpsRetained smoothed frame rate.
0x0C44u32frame_scaleCurrently exposed movement scale, with eight fractional bits.
0x0C84u32exposed_nominal_ticksCurrently exposed whole nominal-tick increment.
0x0CC4u32nominal_totalWhole nominal-tick accumulator.
0x0D04u32nominal_scaled_totalNominal-tick accumulator with eight fractional bits.
0x0D44u32nominal_scaled_incrementLast change in the scaled nominal accumulator.
0x0D84u32exposed_fine_ticksCurrently exposed finer-clock increment; eight units per nominal tick.
0x0DC4u32fine_totalWhole finer-clock accumulator.
0x0E04u32fine_scaled_totalFine-tick accumulator with five fractional bits.
0x0E44u32fine_scaled_incrementLast change in the scaled fine accumulator.
0x0E84u32min_frame_rateRetained minimum FPS setting.
0x0EC4u32max_frame_rateRetained maximum FPS setting.
0x0F04u32use_smooth_fpsRetained smoothed-FPS selection switch.
0x0F44u32use_smooth_divisorRetained movement-scale smoothing switch.
0x0F84u32normal_rate_scaledRetained normal frame rate multiplied by 256.

The two scaled totals advance by next_scale; their low eight and five bits retain the fractional remainders. Exposed timing can differ from the last computed timing. Save restoration copies these fields directly, then captures the partial clock checkpoint used during subsequent resource preparation. The saved clock therefore includes fields omitted by a menu checkpoint, including smoothed FPS, the measurement remainder and startup countdown. It also retains the FPS settings rather than storing only their derived scale. See FPS-derived frame scale for measurement and checkpoint behavior. Other SVGV fields are retained state and must be preserved when editing a save.

Rope jump latch

The selected player’s rope jump latch is a four-byte little-endian word at SVGV payload offset 0x06C. The global-state writer stores the latch directly, and the global-state reader copies the saved word back before restoring the per-world object and rope records. It is separate from the SMRP rope records. See rope control while attached for its input behavior.

SMOB restores an actor’s rope attachment by resolving the saved rope identity and constructing a fresh attachment. The actor receives that fresh pointer; the saved attachment pointer is not reused. The saved attachment node and motion word are then restored into the fresh attachment.

When restoring SMRP, the rope’s list references are resolved by rope identity. The current link-array and end-model pointers are retained. If the selected player has an attachment, the rope’s current attachment-list head is retained as well. If the selected player has no attachment, the current head is freed and the restored head is null. The rope jump latch is restored through SVGV independently of these attachment pointers.

Actor script-state ownership

SMOB retains the actor’s existing script-state pointer instead of restoring the saved pointer value. When that actor has script state, its saved script-state record is copied into the existing allocation. The main save reader restores object and rope records before inventory, then reads SVCB into the selected player’s existing script state. These records depend on initialized actor script owners; they do not allocate a replacement selected-player script owner.

The script-state copy preserves the live taunt-pointer array, all nine attack references and all four defence references. Saved pointer values do not replace these dependencies of the initialized owner.

Engine globals

The main save (SAVEGAME.SAV, not the per-world .TSG files) also writes an SVG2 chunk after SVGV. Its payload is a fixed 1,666-byte concatenation of retained engine globals without padding or per-field headers, in this order:

SizeContents
4Retained player health, copied into both player records at load.
4Health-candle feedback state.
2Retained state word, written only by the save loader.
12Retained state block.
1600The journal tag table: 400 four-byte tags, zero marking an empty entry.
4Selected-player switch.
4 × 5Power-up slots: the two slots’ frame values, their two retained frame indices (-1 empty), then the slot alternation (see power-up slots).
4 × 4Four retained world-state words written when a world loads.
4Retained HUD state word.

Inventory

SVIT contains 22-byte records in initialized item-list order, followed by a signed 32-bit selected-item ID. All numeric fields are little-endian. The payload contains neither an item count nor per-record IDs. Its length is 22 × saved_record_count + 4. The save reader and writer traverse one fewer record than item initialization creates. The final initialized item remains at its initialized values when loading; it has no record in SVIT.

OffsetSizeTypeNameDescription
0x001u8hideCurrent item visibility field.
0x011u8pushedNonzero while a pushed value is retained.
0x024i32totalCurrent item total limit.
0x064i32player_maxCurrent player ownership limit.
0x0A4i32player_totalCurrent owned count.
0x0E4u32unknown_0eRetained item-state word.
0x124u32saved_player_totalCount saved by the first push and restored by a pop.

The pushed byte and saved count are restored directly, without normalizing the byte or replacing the saved count with the current count. PushItem and PopItem use these fields: the first push saves the current count, later pushes retain that saved count, and a pop clears the pushed byte and restores the saved count.

The selected-item value -1 means no selection. Other values are looked up as full item IDs after the records have been restored. An absent ID leaves no selection. Restoration directly replaces the saved fields in initialized item records. It does not invoke acquisition, requirements, drop lists or pickup effects.

See ITEM.INI for the initialized limits and acquisition rules. These save records can replace limits as well as owned counts.

Save-slot directories

[SAVEGAME.NNN — slot directory]
  [SAVEGAME.SAV — current saved state]
  [Map-name.TSG files — other per-world snapshots]
  [LOGBOOK.TXT — journal content, when present]

The main save identifies the current world. The accompanying .TSG files retain state for other worlds. LOGBOOK.TXT contains game journal content; it is separate from the historical TXT tool reports described in Bundled support resources.

External References