Graph (.dat) -- AI Navigation Graph

1. What the graph file contains

A graph<taskId>.dat file supplies an AI navigation graph to an AIGraph task. The task id appears in the filename and the graph's node positions are stored in the graph task's local frame. The engine adds the task position to a node position when it needs world coordinates.

The research signatures and method addresses below come from the IGI 1 graph analysis by Dimon Krevedko, Yoejin Light and Artiom Rotari, continued by IGI ResearchDevs@HM. The stream behavior and corpus observations are cross-checked against the OpenIGI graph reader and its retail corpus notes.

2. Two on-disk variants

VariantHow it is recognizedContentsRetail corpus
Named fields Begins with bytes CC DD EE FF (little-endian value 0xFFEEDDCC). Typed GraphStream values addressed by field-name hashes. Common order: capacity, route table, nodes, then links. 293 of 300 files
Positional No leading magic; the loader rewinds and reads fixed fields. Node count, slot count and link count, followed by node data, links and route table. Nodes also carry two counted pair lists. 7 of 300 files; all are from level 8

The field order is consistent in the named retail files, but these are streams rather than fixed-size C records: each field is written as values and node/link records are assembled as their fields arrive. The engine commits a node when its criteria field arrives and a link when its link-type field arrives.

3. Named-field signatures

The field name is not stored as text. The engine hashes its registered name, stores the hash as a typed 32-bit value, then reads the declared value type and payload. The abbreviated signature below is the four-byte prefix shown in the original static research: a type tag followed by the low three bytes of the little-endian field hash. The full hash is included so fields that share a prefix remain distinguishable.

FieldField hashSignature prefixValue type
nMaxNodes0x000D3AE604 E6 3A 0DInt32
nNodeID0x000735CE04 CE 35 07Int32
tNodePosition0x001D429504 95 42 1DReal64x3 (24 bytes)
vNodeGamma0x000F7E9C04 9C 7E 0FReal32
vNodeRadius0x0014302304 23 30 14Real32
nNodeMaterial0x001BB62904 29 B6 1BInt32
nNodeCriteria0x001BD3E504 E5 D3 1BString8
nLinkID10x0009104A04 4A 10 09Int32
nLinkID20x000918F604 F6 18 09Int32
eLinkType0x000DA92304 23 A9 0DInt32
RouteTable0x000FDE0D04 0D DE 0FBlob

A string value carries a length byte; the count includes the terminating NUL. A blob carries its element type and declared length before its bytes. The route-table reader uses the graph's declared slot count to determine the expected table size.

4. Nodes, links and positional-only data

4.1 Node fields

FieldOn-disk meaningObserved engine behavior
nNodeIDInteger node id; ids are also slot indices.Retail ids start at 1. Slot 0 is unused; the largest observed id is 380.
tNodePositionThree little-endian 64-bit floats.Stored relative to the graph task. The engine adds the task position for world coordinates.
vNodeGamma32-bit float.Preserved by the parser; its gameplay meaning is not established here.
vNodeRadius32-bit float.The nearest-node query scales it by 4096 engine units per metre.
nNodeMaterialInteger material id.-1 represents no material on the named-field path.
nNodeCriteriaLength-prefixed string.Names resolve to registered criteria values; the empty string is an ordinary node.

The static notes call the criteria field Stringx18; the stream reader handles the named form as a length-prefixed string. Treat 18 as the research's field label, not a proven fixed on-disk width.

4.2 Criteria names

Stored nameResolved valueObserved use
NODECRITERIA_DOOR1Doorway node
NODECRITERIA_VIEW2Observation post; excluded by the nearest-node query
NODECRITERIA_STAIR4Stair node

These are names resolved to bits in registration order, not a packed multi-bit integer in the file. Retail files contain the empty string or one of these three names.

4.3 Links

A link is assembled from nLinkID1, nLinkID2 and eLinkType. The engine registers it with both endpoint nodes, so links are undirected in the graph API. All 24,433 links observed in the retail corpus have link type 1.

4.4 Positional-only node data

The seven files without the magic use positional records. Each begins with node count, slot count and link count. A node then carries its id, position, gamma and radius, followed by an optional float, an optional material id, a criteria string and two counted lists of integer pairs. The engine's AIGraph hook reads the two lists to advance the stream; the retail parser retains them as positional extras. Links follow the nodes, and the route table follows the links when present.

5. Route table

The precomputed route table has nMaxNodes × nMaxNodes entries, each 8 bytes: a little-endian signed 32-bit next-node id and a 32-bit float cost. Its total size is therefore 8 × nMaxNodes² bytes.

struct RouteEntry {
    int32_t next_node; // -1 means no route
    float   cost;
};

For a route from from to to, the entry index is from + to × nMaxNodes. A no-route entry is { -1, -1.0 }. The engine asks the table for the next node at each step; the file already contains the route choices. This describes the observed lookup and does not assume which shortest-path algorithm produced the table.

6. Runtime extraction methods

The original research identifies these retail IGI.EXE routines as field extractors. Addresses are image-specific and should be read as labels for the analyzed executable:

FieldRoutine
nMaxNodesSUB_004FAEE0()
nNodeIDSUB_004FAF50()
tNodePositionSUB_004FAF80()
vNodeGammaSUB_004FAFA0()
vNodeRadiusSUB_004FAFC0()
nNodeMaterialSUB_004FAFE0()
nNodeCriteriaSUB_004FB030()
nLinkID1SUB_004FB080()
nLinkID2SUB_004FB0B0()
eLinkTypeSUB_004FB0D0()

7. Retail corpus observations

  • 300 graph files across the fourteen levels: 293 named-field and 7 positional.
  • Declared slot counts: 100 in 274 files, 200 in 19, 300 in 4 and 400 in 3.
  • 8,317 nodes; the largest node id is 380. Slot 0 is unused.
  • 24,433 links; 203 files contain links and 90 contain none.
  • All route tables are square over the declared slot count and are consumed exactly at end of file.
  • Criteria counts: empty 7,022; NODECRITERIA_DOOR 465; NODECRITERIA_VIEW 161; NODECRITERIA_STAIR 669.

Evidence note. The corpus counts and parser behavior above describe the OpenIGI retail-data audit. They are implementation cross-checks; the original graph research remains the source for the static signatures, criteria names and runtime addresses credited above.