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 Games

A KotOR save is a folder, not a single file. Inside it: one bundle archive (SAVEGAME.sav), three loose GFF sidecars, and a thumbnail. The bundle holds the per-module runtime state; the loose sidecars hold the global session state that the load menu and the engine read directly.

Each loose sidecar has its own page; the bundle archive is described below. These pages are the field reference the rakata-save and rakata-extract crates are written from. For the assembly flow, the nested per-module archive layout, the function addresses, and the per-object serialization quirks, see the Save Game Deep Dive.

Important

Three facts shape everything below.

  • A save is a folder, and SAVEGAME.sav accumulates the runtime state of every module you have visited, not just the current one. A module is snapshotted on the way out, before the next one loads.
  • The three loose .res files are plain GFFs holding the global session state (menu metadata, party, campaign globals). The per-module state (areas, live objects) lives nested inside SAVEGAME.sav.
  • Every GFF the engine writes is stamped V3.2 no matter what version the caller asks for (see GFF).

The save folder

FileFormatRole
SAVEGAME.savERF (MOD V1.0)The bundle: one nested ERF per visited module, plus the flat session resources (REPUTE factions, AVAILNPC companion snapshots, the party INVENTORY)
savenfo.resGFF (NFO )Menu metadata: name, area, last module, play time, portraits
PARTYTABLE.resGFF (PT )Party roster, gold, XP, journal, available companions, pazaak, galaxy map
GLOBALVARS.resGFF (GVT )Campaign global variables (booleans, numbers, locations, strings)
Screen.tgaTGASave-slot preview thumbnail

Tooling identifies each resource by the type tags above. Note two quirks: the PT party-table tag has two trailing spaces, and the ERF container’s tag is the full MOD V1.0. The sidecar filename casing is the engine’s own mixed pattern (savenfo.res lowercase, PARTYTABLE.res / GLOBALVARS.res uppercase, Screen.tga capitalized): read case-insensitively, write it exactly.

The three loose sidecars are save-only GFFs: they exist inside a save folder and nowhere else. The GFFs bundled inside SAVEGAME.sav (IFO, ARE, GIT) and the global REPUTE (FAC) also appear in ordinary modules, so they are documented under GFF, not here.


SAVEGAME.sav

The bundle archive (ERF, version tag MOD V1.0). Inside, every visited module is bundled as its own nested ERF (also MOD V1.0), keyed by the module resref and stored under resource type 2057 (sav). Each per-module ERF holds:

  • the module info (IFO , under the resref Module) carrying the saved module clock, runtime id counters, and the party/limbo creature lists (see IFO);
  • the area static (ARE , type 2012 / 0x7dc), skipped for modules flagged Mod_IsNWMFile;
  • the dynamic game-instance state (GIT , type 2023 / 0x7e7): the live creatures, doors, placeables, triggers, and so on.

Reading a module’s saved objects is a two-level walk: open SAVEGAME.sav, find the resource named after the module, parse that as an ERF, and read its GIT.

Note

Before packaging, each per-module ERF lives as a standalone <module>.rsv file in the GAMEINPROGRESS working directory. The .rsv extension maps to resource type 0x0bc1; the content is identical. The deep dive covers when each is written, how the engine prefers RSV over SAV at load time, and the implications for tooling.

Faction state is not per-module: a single global REPUTE resource (type FAC , see FAC) holds the whole-session faction table.

Two more flat resources sit beside the module ERFs and REPUTE:

  • AVAILNPCn (UTC, resource type 2027), one per recruited companion, where n is the companion’s npc.2da row (AVAILNPC0-AVAILNPC8 for a full late-game crew). Each is a full creature snapshot; the partytable availability flags decide whether the engine instantiates it.
  • INVENTORY, the party’s shared item stash: a GFF tagged INV holding a single ItemList of item snapshots. The INV tag appears only here, and the one list is its whole schema. Note it is stored under the generic resource type 0, not a dedicated type: find it by name, not by type.

The deep dive covers when each is written and read.

Note

This list is the resources you can rely on, not a closed inventory. The archive is built by sweeping an entire working directory wholesale, so an unrelated leftover file can occasionally ride along – a PC (UTC) resource in particular, a party-leader-swap artifact with nothing to do with saving. It’s covered as its own case on the deep dive so it doesn’t get mistaken for a fourth deliberate flat resource.

Templates versus snapshots

The same GIT schema is read two ways, chosen by a single UseTemplates BYTE in the GIT’s top-level struct:

UseTemplates = 1 (static .git)UseTemplates = 0 (savegame)
Object formsparse placementfull self-contained snapshot
TemplateResRefpresent; engine loads the blueprintabsent; no blueprint loaded
A missing field resolves tothe UTC/UTD/UTP/UTT blueprintthe engine’s hardcoded default

So a savegame object is the whole truth: the engine reads it directly, and a template lookup would supply data the engine never uses.

Object position and orientation fields

Position and orientation field names in the GIT depend on the object type, not the file. There are three position-naming styles and four orientation-naming styles; no single field name is shared across all object types:

ObjectPositionOrientation
Door, PlaceableX, Y, ZBearing (single angle)
Creature, Trigger, Waypoint, StoreXPosition, YPosition, ZPositionXOrientation, YOrientation, ZOrientation (vector)
Sound, EncounterXPosition, YPosition, ZPositionnone at object level
Area-of-effectPositionX, PositionY, PositionZOrientationX, OrientationY, OrientationZ (vector)

Naming and storage quirks:

  • Bearing is two things. A door stores its scalar verbatim; a placeable’s is derived from its orientation yaw at save time (lossy).
  • Geometry coordinate space differs by type. Trigger Geometry vertices (PointX/PointY/PointZ) are stored relative to the trigger position; encounter Geometry vertices (X/Y/Z) are absolute.
  • Trigger orientation re-bakes geometry. Supplying a trigger orientation re-rotates its geometry by the yaw delta on load.

See the Save Game Deep Dive for the full treatment.

Tip

Rust integration. The rakata-save crate is where this structure is modelled for parsing, validation, and writing without managing the ERF layer by hand. Check its Rustdocs for the current types: the crate is mid-refactor, so this page describes the on-disk format rather than a specific API.


Engine behaviour

A few behaviours span the whole save rather than any single sidecar. All are read from Ghidra decompilation of swkotor.exe (K1 GOG build); the Save Game Deep Dive lists the function addresses, and the Provenance Policy covers how engine evidence is gathered.

Character loading order

LoadCharacterFromIFO takes a Mod_PlayerList slot index and normally reads that slot from the active module’s Module IFO.

Important

0xffffffff is a mode selector, not a member index. It switches the load source to the transient pifo party-info file and reads back the slot each player recorded on itself when StorePlayerCharacters packed the party into pifo. A reader that treats it as a literal slot reads the wrong record.

This is the module-transition path. The party is staged to pifo on the way out of a module and restored on the way in, before the destination module’s roster exists, then rebuilt by CreateParty. pifo is a GFF tagged IFO carrying the same Mod_PlayerList of full creature snapshots as a module roster.

There is no separate .bic reader. A standalone Player record rides this same path, keyed by ObjectId.

Module inclusion

IncludeModuleInSave gates which visited modules are written into the bundle. It reads an INT entry from modulesave.2da for the module and excludes it only when that row exists and is 0. A missing row, or a modulesave.2da that cannot be loaded, includes the module (fail-open).

Orientation storage

Creatures, waypoints, and stores load their orientation as a full (X, Y, Z) vector. CSWSObject::SetOrientation stores those three floats verbatim on the object and flags it dirty; it does not reduce the vector to a yaw.

Area restoration

To rebuild the dynamic state of the room you were standing in (open or locked doors, moved placeables), the engine restores the area by targeting the GIT resource type (0x7e7) matched against the module’s core resref.