GFF List Index Corruption
Summary
A binary GFF writer can silently corrupt list mapping if it writes list index entries in a way that allows recursive nested-list writes to interleave with the parent list’s index block.
This is a compatibility-critical issue for KOTOR data because many resources depend on stable list ordering and correct struct index mapping.
How GFF Lists Work
In binary GFF, a List field stores:
- A relative offset into the
list_indicestable. - At that offset:
count(u32)countstruct indices (u32 each), each pointing into the struct table.
If these indices are wrong, the parser will load the wrong list structs.
Failure Mode
The bug class occurs when a writer:
- Starts writing a parent list.
- Recursively builds child structs.
- Appends list indices directly while recursion is still producing nested list index data.
Because nested lists also write into the same list_indices buffer, parent and child index blocks can interleave and the parent list can point at unintended structs.
Observable Symptoms
- Struct IDs in list entries change after roundtrip.
- Expected fields are missing from entries after roundtrip.
- Mod compatibility breaks for list-heavy resources due to reordered/remapped entries.
Correct Writer Strategy
For each list field:
- Write list count.
- Reserve contiguous slots for all struct indices up front.
- Build each child struct recursively.
- Backfill each reserved slot with the final struct index.
This guarantees parent list index layout is stable even when nested lists write their own index blocks.
Implementation Status
In this repository:
rakata-formats/src/gff/writer.rsreserves list index slots and backfills them.- Regression tests cover:
- synthetic list order + struct-id stability
- UTC fixture roundtrip stability on lists like
FeatList,ItemList,ClassList.
rakata-generics/src/utc.rsincludes a no-op rebuild test to ensure typed conversion does not drift list order/IDs.