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.savaccumulates 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
.resfiles are plain GFFs holding the global session state (menu metadata, party, campaign globals). The per-module state (areas, live objects) lives nested insideSAVEGAME.sav.- Every GFF the engine writes is stamped
V3.2no matter what version the caller asks for (see GFF).
The save folder
| File | Format | Role |
|---|---|---|
SAVEGAME.sav | ERF (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.res | GFF (NFO ) | Menu metadata: name, area, last module, play time, portraits |
PARTYTABLE.res | GFF (PT ) | Party roster, gold, XP, journal, available companions, pazaak, galaxy map |
GLOBALVARS.res | GFF (GVT ) | Campaign global variables (booleans, numbers, locations, strings) |
Screen.tga | TGA | Save-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 resrefModule) carrying the saved module clock, runtime id counters, and the party/limbo creature lists (see IFO); - the area static (
ARE, type2012/0x7dc), skipped for modules flaggedMod_IsNWMFile; - the dynamic game-instance state (
GIT, type2023/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>.rsvfile in theGAMEINPROGRESSworking directory. The.rsvextension maps to resource type0x0bc1; the content is identical. The deep dive covers when each is written, how the engine prefersRSVoverSAVat 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 type2027), one per recruited companion, wherenis the companion’snpc.2darow (AVAILNPC0-AVAILNPC8for 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 taggedINVholding a singleItemListof item snapshots. TheINVtag appears only here, and the one list is its whole schema. Note it is stored under the generic resource type0, 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 form | sparse placement | full self-contained snapshot |
TemplateResRef | present; engine loads the blueprint | absent; no blueprint loaded |
| A missing field resolves to | the UTC/UTD/UTP/UTT blueprint | the 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:
| Object | Position | Orientation |
|---|---|---|
| Door, Placeable | X, Y, Z | Bearing (single angle) |
| Creature, Trigger, Waypoint, Store | XPosition, YPosition, ZPosition | XOrientation, YOrientation, ZOrientation (vector) |
| Sound, Encounter | XPosition, YPosition, ZPosition | none at object level |
| Area-of-effect | PositionX, PositionY, PositionZ | OrientationX, OrientationY, OrientationZ (vector) |
Naming and storage quirks:
Bearingis 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
Geometryvertices (PointX/PointY/PointZ) are stored relative to the trigger position; encounterGeometryvertices (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-savecrate 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
0xffffffffis a mode selector, not a member index. It switches the load source to the transientpifoparty-info file and reads back the slot each player recorded on itself whenStorePlayerCharacterspacked the party intopifo. 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.