GFF File Format - OpenKotOR/PyKotor GitHub Wiki
The Generic File Format (GFF) is the structured binary container that the Odyssey runtime feeds into CResGFF readers. In all three analyzed binaries, a typed read follows the same high-level chain: resolve a field ordinal from a label, resolve that ordinal into a field record, validate the field type tag, then decode the referenced payload or copy the caller-supplied default. GetFieldByLabel @ (/K1/k1_win_gog_swkotor.exe @ 0x00411630, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00623a40, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fcc0) GetField @ (/K1/k1_win_gog_swkotor.exe @ 0x00410990, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x006238d0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fc20) ReadFieldCResRef @ (/K1/k1_win_gog_swkotor.exe @ 0x00411e10, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624fa0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a12d0) ReadFieldCExoLocString @ (/K1/k1_win_gog_swkotor.exe @ 0x00411fd0, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00625240, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0f80)
Representative callers show that this is not a niche format used by one subsystem. K1 callers include LoadAreaHeader @ 0x00508c50, LoadDialogBase @ 0x0059f5f0, and ReadStatsFromGff @ 0x005afce0; TSL callers include LoadAreaHeader @ 0x00718a20, LoadDialogBase @ 0x0074f4f0, and ReadStatsFromGff @ 0x006ec350; Aurora callers include LoadDialog @ 0x14041b5c0, LoadStore @ 0x1404fbbf0, and LoadWaypoint @ 0x140509f80. PyKotor models the same shared container through GFFContent tags such as UTC, UTI, ARE, DLG, IFO, and JRL. [gff_data.py]
The detailed byte layout below is parser-derived from PyKotor and corroborating open readers. PyKotor's GFFBinaryReader.load reads the type tag, version tag, and seven offset/count pairs in header order, then rejects declared versions other than V3.2, which matches the currently recovered KotOR-family usage described here. [io_gff.py, gffreader.cpp, xoreos gff3file.cpp]
Like all game resources, GFF files are resolved through the standard resource resolution order: the engine checks the override folder first, then the active MOD/ERF module capsule, then KEY/BIF base data. Modders can therefore shadow any vanilla GFF by placing a replacement in override or inside a module .mod file (see Mod Creation Best Practices). For merge-safe field-level edits, use TSLPatcher/HoloPatcher GFFList syntax instead of full-file replacement.
GFF files work alongside 2DA configuration tables, TLK localized strings, MDL/MDX 3D models, and NCS compiled scripts. Historical editors include K-GFF (LucasForums thread, Deadly Stream listing) and KotOR Tool; for current editing, use Holocron Toolset.
- GFF β Generic File Format
- Table of Contents
- File Structure Overview
- Binary Format
- GFF Data Types
- GFF Structure
-
GFF Generic Types
- ARE (Area)
- BIC (Character)
- BTC (Creature Template β BioWare)
- BTD (Door Template β BioWare)
- BTE (Encounter Template β BioWare)
- BTG (Random Item Generator β BioWare)
- BTI (Item Template β BioWare)
- BTM (Merchant Template β BioWare)
- BTP (Placeable Template β BioWare)
- BTT (Trigger Template β BioWare)
- CWA (Crowd Attributes)
- DLG (Dialogue)
- FAC (Faction)
- GIC (Game Instance Comments)
- GIT (Game Instance Template)
- GUI (Graphical User Interface)
- IFO (Module Info)
- ITP (Palette)
- JRL (Journal)
- PTH (Path)
- PTM (Plot Manager)
- PTT (Plot Wizard Template)
- QST2 (Quest β Odyssey)
- STO (Store β Odyssey)
- ULT (Light Template)
- UTC (Creature)
- UTD (Door)
- UTE (Encounter)
- UTG (Random Item Generator)
- UTI (Item)
- UTM (Merchant)
- UTP (Placeable)
- UTR (Tree Template)
- UTS (Sound)
- UTT (Trigger)
- UTW (Waypoint)
- WMP (World Map)
- Field Data Access Patterns
- Cross-reference: implementations
A GFF file is a tree of structs, fields, and lists, but the binaries do not walk that tree abstractly. They follow a concrete label-to-field-to-payload chain:
-
GetFieldByLabelnormalizes the requested label to a 16-byte temporary buffer and scans the current struct's candidate field slots until the corresponding 16-byte label-table record matches. K1 copies into a 16-byte stack buffer with_strncpy; TSL explicitly zeroes the buffer first and then calls_strncpy; Aurora usesstrncpyand adds anEmitdiagnostic path when caller state is invalid.GetFieldByLabel @ (/K1/k1_win_gog_swkotor.exe @ 0x00411630, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00623a40, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fcc0) -
GetFieldresolves the actual field record for that struct-relative ordinal. In all three binaries it rejects null struct/table pointers and out-of-range indices, then branches between the direct single-field encoding and the indirect multi-field encoding. K1 and TSL read 32-bit table pointers fromthis+0x40/0x44/0x4c/0x64; Aurora uses 64-bit equivalents atthis+0x78/0x88/0x98/0xc8.GetField @ (/K1/k1_win_gog_swkotor.exe @ 0x00410990, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x006238d0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fc20) - Typed readers validate the field tag and decode payloads.
ReadFieldCResRefrequires type0x0b;ReadFieldCExoLocStringrequires type0x0c;GetListCountrequires type0x0f. Failed lookup, failed bounds checks, or wrong field types clear the success flag and copy or synthesize the caller's default value instead of crashing through malformed data.ReadFieldCResRef @ (/K1/k1_win_gog_swkotor.exe @ 0x00411e10, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624fa0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a12d0)ReadFieldCExoLocString @ (/K1/k1_win_gog_swkotor.exe @ 0x00411fd0, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00625240, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0f80)GetListCount @ (/K1/k1_win_gog_swkotor.exe @ 0x00411940, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624970, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0370)
The recovered readers expose a few runtime properties that matter more than the usual high-level description:
-
Field tags are authoritative at read time: typed readers compare the first dword of the resolved field record against the expected type (
0x0bforResRef,0x0cforCExoLocString,0x0ffor list fields) before touching payload bytes.ReadFieldCResRef @ (/K1/k1_win_gog_swkotor.exe @ 0x00411e10, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624fa0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a12d0)ReadFieldCExoLocString @ (/K1/k1_win_gog_swkotor.exe @ 0x00411fd0, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00625240, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0f80)GetListCount @ (/K1/k1_win_gog_swkotor.exe @ 0x00411940, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624970, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0370) -
Unknown or missing labels fail soft:
GetFieldByLabelreturns-1when the label does not match any candidate field, and the typed readers then clear the success flag and return the default object instead of reading arbitrary bytes.GetFieldByLabel @ (/K1/k1_win_gog_swkotor.exe @ 0x00411630, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00623a40, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fcc0)ReadFieldCResRef @ (/K1/k1_win_gog_swkotor.exe @ 0x00411e10, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624fa0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a12d0) -
Single-field and multi-field structs are encoded differently: all three
GetFieldimplementations special-casefield_count == 1and use the struct record's second dword directly, but switch to an indirection table forfield_count > 1.GetField @ (/K1/k1_win_gog_swkotor.exe @ 0x00410990, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x006238d0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fc20) -
External payloads are length-checked before decode: KotOR I and TSL call dedicated helper functions to resolve field-data and list-data slices, while Aurora inlines the same size and remaining-byte checks inside the typed readers.
ReadFieldCResRef @ (/K1/k1_win_gog_swkotor.exe @ 0x00411e10, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624fa0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a12d0)ReadFieldCExoLocString @ (/K1/k1_win_gog_swkotor.exe @ 0x00411fd0, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00625240, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0f80)GetListCount @ (/K1/k1_win_gog_swkotor.exe @ 0x00411940, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624970, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0370)
PyKotor's in-memory model exposes the same shared container idea through GFFContent, while GFFBinaryReader.load accepts only V3.2 on input. That matches the family recovered here: one container layout reused by many content signatures rather than separate binary formats for creatures, areas, dialogues, and journals. [gff_data.py, io_gff.py]
Typical Applications:
-
Character/Creature templates:
-
Area definitions:
-
Dialogue trees (DLG)
-
Quest journals (JRL)
-
Module information (IFO)
-
User interface layouts (GUI)
Many KotOR file types are GFF containers with different signatures and field schemas, for example:
Dozens of other extensions are documented across this wiki.
Implementation (PyKotor):
- package:
resource/formats/gff/ - binary read
GFFBinaryReader.loadL82+ - write
GFFBinaryWriter.writeL355+ - data model
GFFL509+ GFFStructL689+- XML/JSON/Twine variants in
io_gff_xml,io_gff_json,io_gff_twine
Comparable open implementations include reone's reader, writer, and core GFF types (gffreader.cpp, gffwriter.cpp, gff.cpp), xoreos and xoreos-tools Aurora loaders (xoreos gff3file.cpp, xoreos-tools gff3file.cpp), KotOR.js's parser (GFFObject.ts L24+), Kotor.NET's managed reader/writer (GFF.cs L18+), KotOR-Unity's loader (GFFObject.cs), and the GFF Kaitai specifications in bioware-kaitai-formats.
- TSLPatcher GFFList Syntax - Modding GFF files with TSLPatcher
- 2DA File Format - Configuration data referenced by GFF files
- TLK File Format - Text strings used by GFF LocalizedString fields
- Bioware Aurora GFF Format - Official BioWare specification
The GFF file header is 56 bytes in size (0x38):
| Name | Type | Offset | Size | Description |
|---|---|---|---|---|
| File Type | char | 0 (0x00) | 4 | Content type (e.g., "GFF ", "ARE ", "UTC ") |
| File Version | char | 4 (0x04) | 4 | Format Version ("V3.2" for KotOR) |
| Struct Array Offset | UInt32 | 8 (0x08) | 4 | Offset to struct array |
| Struct Count | UInt32 | 12 (0x0C) | 4 | Number of structs |
| Field Array Offset | UInt32 | 16 (0x10) | 4 | Offset to field array |
| Field Count | UInt32 | 20 (0x14) | 4 | Number of fields |
| Label Array Offset | UInt32 | 24 (0x18) | 4 | Offset to label array |
| Label Count | UInt32 | 28 (0x1C) | 4 | Number of labels |
| Field Data Offset | UInt32 | 32 (0x20) | 4 | Offset to field data section |
| Field Data Count | UInt32 | 36 (0x24) | 4 | Size of field data section in bytes |
| Field Indices Offset | UInt32 | 40 (0x28) | 4 | Offset to field indices array |
| Field Indices Count | UInt32 | 44 (0x2C) | 4 | Number of field indices |
| List Indices Offset | UInt32 | 48 (0x30) | 4 | Offset to list indices array |
| List Indices Count | UInt32 | 52 (0x34) | 4 | Number of list indices |
This header layout is parser-derived rather than attributed here to one recovered named engine loader. PyKotor's GFFBinaryReader.load reads the type tag, version tag, and offset/count pairs in exactly this order, then seeks into the struct, field, label, field-data, field-indices, and list-indices tables. [io_gff.py, gffreader.cpp]
Labels are 16-byte null-terminated strings used as field names:
| Name | Type | Size | Description |
|---|---|---|---|
| Labels | Char | 16ΓN | Array of field name labels (null-padded to 16 bytes) |
The runtime view of a label is exactly this 16-byte padded record. GetFieldByLabel in all three binaries copies the requested label into a 16-byte temporary buffer and then compares the candidate label-table record as four 32-bit chunks before it returns a struct-relative field ordinal. K1 uses _strncpy; TSL zeroes the buffer first; Aurora uses strncpy and includes an explicit diagnostic path when caller state is invalid. GetFieldByLabel @ (/K1/k1_win_gog_swkotor.exe @ 0x00411630, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00623a40, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fcc0)
Each struct entry is 12 bytes:
| Name | Type | Offset | Size | Description |
|---|---|---|---|---|
| Struct ID | Int32 | 0 (0x00) | 4 | structure type identifier |
| Data/Offset | UInt32 | 4 (0x04) | 4 | field index (if 1 field) or offset to field indices (if multiple) |
| Field Count | UInt32 | 8 (0x08) | 4 | Number of fields in this struct (0, 1, or >1) |
GetField makes the important runtime distinction here: if Field Count == 1, the Data/Offset slot is already the field index; if Field Count > 1, the same slot is interpreted as an offset into the field-indices table. That direct-versus-indirect branch is present in K1, TSL, and Aurora, even though the pointer-sized members move in Aurora's 64-bit class layout. GetField @ (/K1/k1_win_gog_swkotor.exe @ 0x00410990, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x006238d0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fc20)
Each field entry is 12 bytes:
| Name | Type | Offset | Size | Description |
|---|---|---|---|---|
| Field Type | UInt32 | 0 (0x00) | 4 | data type (see GFF Data Types) |
| Label Index | UInt32 | 4 (0x04) | 4 | index into label array for field name |
| Data/Offset | UInt32 | 8 (0x08) | 4 | Inline data (simple types) or offset to field data (complex types) |
At runtime the first and third dwords matter most. ReadFieldCResRef compares the first dword against 0x0b, ReadFieldCExoLocString compares it against 0x0c, and GetListCount compares it against 0x0f; only after that type check do the readers treat the third dword as inline data or as an offset into the external data tables. ReadFieldCResRef @ (/K1/k1_win_gog_swkotor.exe @ 0x00411e10, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624fa0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a12d0) ReadFieldCExoLocString @ (/K1/k1_win_gog_swkotor.exe @ 0x00411fd0, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00625240, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0f80) GetListCount @ (/K1/k1_win_gog_swkotor.exe @ 0x00411940, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624970, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0370)
Complex field types store their payloads in the field data section, and the typed readers do explicit byte-budget checks before decoding them. KotOR I and TSL resolve those slices through dedicated helpers; Aurora performs the same offset and remaining-byte checks inline inside the typed readers. ReadFieldCResRef @ (/K1/k1_win_gog_swkotor.exe @ 0x00411e10, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624fa0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a12d0) ReadFieldCExoLocString @ (/K1/k1_win_gog_swkotor.exe @ 0x00411fd0, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00625240, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0f80)
| Field Type | Storage Format |
|---|---|
| UInt64 | 8 bytes (uint64) |
| Int64 | 8 bytes (int64) |
| Double | 8 bytes (double) |
| String | 4 bytes length + N bytes string data |
| ResRef | 1 byte length + N bytes ResRef data (max 16 chars) |
| LocalizedString | 4 bytes total payload size + 4 bytes StrRef + 4 bytes substring count + repeated (substring ID, byte length, bytes) records |
| Binary | 4 bytes length + N bytes binary data |
| Vector3 | 12 bytes (3Γfloat) |
| Vector4 | 16 bytes (4Γfloat) |
Two runtime-verified examples matter most to KotOR tooling:
-
ReadFieldCResRefrequires at least one payload byte, treats that byte as the ResRef length, then requires at leastlength + 1total bytes before constructing the result. K1 and TSL do that afterGetDataField; Aurora performs the same checks inline and then callsInitFromCharArray.ReadFieldCResRef @ (/K1/k1_win_gog_swkotor.exe @ 0x00411e10, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624fa0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a12d0) -
ReadFieldCExoLocStringrequires at least 4 bytes for the outer size field, then walks a byte-budgeted blob containing aStrRef, a substring count, and repeated(substring ID, string length, bytes)entries. All three binaries derive language/gender from the substring ID and callAddStringfor each decoded substring; K1 and TSL use helper-based payload access, while Aurora inlines the loop over the substring records.ReadFieldCExoLocString @ (/K1/k1_win_gog_swkotor.exe @ 0x00411fd0, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00625240, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0f80)
When a struct has multiple fields, the struct's data field contains an offset into the field indices array (also called the "Multiple Element Map" or "MultiMap" in xoreos-docs specs/torlack/itp.html), which lists the field indices for that struct.
Access Pattern: GetField in all three binaries implements the same branch. When a struct has exactly one field, the struct record's second dword is used directly as the field index. When a struct has more than one field, that same slot is treated as an offset into the field-indices array, and the requested struct-relative ordinal is used to fetch the final field index from there. GetField @ (/K1/k1_win_gog_swkotor.exe @ 0x00410990, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x006238d0, /Other BioWare Engines/Aurora/nwmain.exe @ 0x14019fc20)
This MultiMap terminology and access pattern follow the historical Torlack/xoreos write-up (specs/torlack/itp.html).
Lists are stored as arrays of struct indices. The list field contains an offset into the list indices array, which contains the struct indices that make up the list.
Access Pattern: GetListCount proves the first runtime step for list payloads in all three binaries. It resolves the list label, requires field type 0x0f, then reads the first dword of the referenced list payload as the element count. K1 and TSL perform that through GetDataLayoutList @ (/K1/k1_win_gog_swkotor.exe @ 0x00410a60, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x006239e0); Aurora performs the same bounds checks inline against the list-data table at this+0xd8. After that count dword, the payload consists of that many struct indices. GetListCount @ (/K1/k1_win_gog_swkotor.exe @ 0x00411940, /TSL/k2_win_gog_aspyr_swkotor2.exe @ 0x00624970, /Other BioWare Engines/Aurora/nwmain.exe @ 0x1401a0370)
The same LIST indirection scheme is described in the Torlack/xoreos documentation for Aurora-family GFF containers (specs/torlack/itp.html).
GFF supports the following field types:
| Type ID | Name | Size (inline) | Description |
|---|---|---|---|
| 0 | Byte | 1 | 8-bit unsigned integer |
| 1 | Char | 1 | 8-bit signed integer |
| 2 | Word | 2 | 16-bit unsigned integer |
| 3 | Short | 2 | 16-bit signed integer |
| 4 | DWord | 4 | 32-bit unsigned integer |
| 5 | Int | 4 | 32-bit signed integer |
| 6 | DWord64 | 8 | 64-bit unsigned integer (stored in field data) |
| 7 | Int64 | 8 | 64-bit signed integer (stored in field data) |
| 8 | Float | 4 | 32-bit floating point |
| 9 | Double | 8 | 64-bit floating point (stored in field data) |
| 10 | CExoString | varies | Null-terminated string string (stored in field data) |
| 11 | ResRef | varies | Resource reference (stored in field data, max 16 chars) |
| 12 | CExoLocString | varies | Localized string (stored in field data) |
| 13 | Void | varies | Binary data blob (stored in field data) |
| 14 | Struct | 4 | Nested struct (struct index stored inline) |
| 15 | List | 4 | List of structs (offset to list indices stored inline) |
| 16 | orientation | 16 | Quaternion (4Γfloat, stored in field data as Vector4) |
| 17 | vector | 12 | 3D vector (3Γfloat, stored in field data) |
| 18 | StrRef | 4 | string reference (TLK StrRef, stored inline as int32) |
PyKotor's canonical type enum and storage definitions live in gff_data.py L73-L108.
Type Selection Guidelines:
-
Use Byte/Char for small integers (-128 to 255) and boolean flags
-
Use Word/Short for medium integers like IDs and counts
-
Use DWord/Int for large values and most numeric fields
-
Use Float for decimals that don't need high precision (positions, angles)
-
Use Double for high-precision calculations (rare in KotOR)
-
Use CExoString for text that doesn't need localization
-
Use CExoLocString for player-visible text that should be translated
-
Use ResRef for filenames without extensions. Typical payloads include:
-
Use Void for binary blobs like encrypted data or custom structures
-
Use Struct for nested objects with multiple fields
-
Use List for arrays of structs (inventory items, dialogue replies)
-
Use vector for 3D positions and directions
-
Use orientation for quaternion rotations
-
Use StrRef for references to dialog.tlk entries
Storage Optimization:
Primitive types (Byte, Char, Word, Short, DWord, Int, Float, Struct, List, StrRef) are stored inline in the field entry, meaning their value or reference (for Struct, List) is physically present in the field and does not incur extra indirection. More complex types (DWord64, Int64, Double, CExoString, ResRef, CExoLocString, Void, Orientation, Vector) are stored externally in the field data block, and the field entry only contains an offset pointing to the actual data. When designing custom GFF formats, use inline storage when possible for efficiency, as external/offset types introduce additional overhead in reading and writing.
A GFF Struct is a collection of named fields. Each GFF Struct has:
-
Struct ID: Type identifier (often
0xFFFFFFFFfor generic structs) - Fields: Dictionary mapping field names (labels) to field values
PyKotor's in-memory implementations of GFFStruct, GFFField, and GFFList are defined in gff_data.py.
GFF Fields can be accessed using type-specific getter/setter methods, for example:
-
get_uint8(label),set_uint8(label, value) -
get_int32(label),set_int32(label, value) -
get_float(label),set_float(label, value) -
get_string(label),set_string(label, value) -
get_resref(label),set_resref(label, value) -
get_locstring(label),set_locstring(label, value) -
get_vector3(label),set_vector3(label, value) -
get_struct(label),set_struct(label, struct) -
get_list(label),set_list(label, list)
A GFF List is an ordered collection of structs.
Common List Usage:
GFF Lists are used extensively for variable-length arrays:
- ItemList in UTC files: Character inventory items
- Equip_ItemList in UTC files: Equipped items
- EntryList in DLG files: Dialogue entry nodes
- ReplyList in DLG files: Dialogue reply options
- SkillList in UTC files: Character skills
- FeatList in UTC files: Character feats
- EffectList in various files: Applied effects
- Creature_List in GIT files: Spawned creatures in area
When modifying GFF Lists, always maintain struct IDs and parent references to avoid breaking internal links.
GFF (Generic file format) files are used as containers for various game resource types. Each generic type has its own structure and field definitions.
See ARE (Area) for detailed documentation.
See DLG (Dialogue) for detailed documentation.
See FAC (Faction) for detailed documentation.
See GIT (Game Instance Template) for detailed documentation.
Graphical User Interface. See GUI (Graphical User Interface) for detailed documentation.
See IFO (Module Info) for detailed documentation.
See JRL (Journal) for detailed documentation.
See PTH (Path) for detailed documentation.
See UTC (Creature) for detailed documentation.
See UTD (Door) for detailed documentation.
See UTE (Encounter) for detailed documentation.
See UTI (Item) for detailed documentation.
See UTM (Merchant) for detailed documentation.
See UTP (Placeable) for detailed documentation.
See UTS (Sound) for detailed documentation.
See UTT (Trigger) for detailed documentation.
See UTW (Waypoint) for detailed documentation.
Character data file (type ID 2015), GFF. Older Aurora format for PC/NPC character definitions. In KotOR the engine supports this type but no shipped content uses it β use UTC (Creature) instead.
BioWare-authored creature blueprint (type ID 2026), GFF. The engine-internal complement to the modder-facing UTC. Seldom encountered directly in mods.
BioWare-authored door blueprint (type ID 2041), GFF. Counterpart to the modder-facing UTD. In KotOR the engine supports this type but shipped modules use UTD for modder-placed doors.
BioWare-authored encounter blueprint (type ID 2039), GFF. Counterpart to the modder-facing UTE.
BioWare-authored random item generator blueprint (type ID 2054), GFF. Counterpart to the modder-facing UTG. Defines a randomized loot table authored by BioWare; not used directly in modding.
BioWare-authored item blueprint (type ID 2024), GFF. Counterpart to the modder-facing UTI.
BioWare-authored merchant/store blueprint (type ID 2050), GFF. Counterpart to the modder-facing UTM. KotOR supports the type but modders use UTM.
BioWare-authored placeable blueprint (type ID 2043), GFF. Counterpart to the modder-facing UTP.
BioWare-authored trigger blueprint (type ID 2031), GFF. Counterpart to the modder-facing UTT.
Odyssey crowd-attribute data (type ID 3025) [CWA], GFF. Stores NPC crowd behavior parameters for KotOR area populations. Not edited directly by modders.
Game instance comments (type ID 2046) [GIC], GFF. Toolset-only companion to GIT: instance labels and comments that the game engine never reads are stored here rather than in the runtime .git.
Tile/blueprint palette file (type ID 2030), GFF. The Aurora toolset uses .itp to organize tiles and object templates into a browsable palette shown in the toolset UI. See ITP (Palette) for detailed documentation.
Plot instance/manager file (type ID 2065), GFF. Stores plot variable state used by the Aurora toolset's plot wizard system. Not normally edited by KotOR modders.
Plot wizard template (type ID 2066), GFF. Provides the blueprint data driving the Aurora toolset plot wizard. Not normally edited by KotOR modders.
Odyssey quest file (type ID 3012), GFF. A second Odyssey-specific quest data type. Not present in retail KotOR I/TSL content; tracked in the registry for cross-engine completeness.
Odyssey store/merchant data (type ID 3013), GFF. Not present in retail KotOR content; tracked in the registry for cross-engine completeness.
Light template (type ID 20015), GFF. Defines a reusable light source object in the NWN2-era Aurora toolset. Not a KotOR I/TSL runtime type.
Random item generator template (type ID 2055), GFF. The user-authored counterpart to BTG. Defines a randomized loot table. Present in the engine's type registry but not commonly used in KotOR modding.
Tree template (type ID 20005), GFF. Defines a reusable vegetation/tree object in the NWN2-era toolset. Not a KotOR I/TSL runtime type.
World map data (type ID 20020), GFF. Stores the game-world map layout including area connections and availability flags. Used by the GUI subsystem for the galaxy/travel map. Not normally patched directly; galaxy map changes typically go through planetary.2da or module IFO edits instead.
Simple types (uint8, int8, uint16, int16, uint32, int32, float) store their values directly in the field entry's data/offset field (offset 0x0008 in the element structure). These values are stored in little-endian format.
Complex types require accessing data from the field data section:
- UInt64, Int64, double: The field's data/offset contains a byte offset into the field data section where the 8-byte value is stored.
-
String (
CExoString): The offset points to a uint32 length followed by the string bytes (not null-terminated). - ResRef: The offset points to a uint8 length (max 16) followed by the resource name bytes (not null-terminated).
-
LocalizedString (
CExoLocString): The offset points to a structure containing:-
uint32: Total size (not including this count) -
int32: StrRef ID (dialog.tlk reference, -1 if none) -
uint32: Number of language-specific strings - For each language string (if count > 0):
-
uint32: Language ID (Concepts) -
uint32: string length in bytes -
char[]: string data
-
-
-
Void (Binary): The offset points to a
uint32length followed by the binary data bytes. -
Vector3: The offset points to 12 bytes (3Γ
float) in the field data section. -
Vector4 / orientation: The offset points to 16 bytes (4Γ
float) in the field data section.
- Struct (CAPREF): The field's data/offset contains a struct index (not an offset). This references a struct in the struct array.
-
List: The field's data/offset contains a byte offset into the list indices array. At that offset, the first uint32 is the entry count, followed by that many uint32 struct indices. Access patterns and code examples are documented in Torlack's Aurora basics, archived at xoreos-docs β
specs/torlack/itp.html.
| Component | PyKotor Reference |
|---|---|
| Binary read | GFFBinaryReader.load (io_gff.py) |
| Binary write | GFFBinaryWriter.write (io_gff.py) |
| GFF data model | GFF (gff_data.py) |
| GFFStruct | GFFStruct (gff_data.py) |
- GFF-ARE
- GFF-DLG
- GFF-IFO
- GFF-UTI
- GFF-UTC
- GIT -- GFF-based game resources
- TSLPatcher GFFList Syntax -- Patching GFF via HoloPatcher/TSLPatcher
- Resource formats and resolution -- Hex resource type IDs (ResRef + type in archives)
- Container-Formats#key -- Resource resolution
- Bioware-Aurora-GFF -- Aurora GFF specification
- Community sources and archives -- DeadlyStream, forums for GFF structure and modding