invk: story conditions
- Source
docs/formats/invk.md- SHA-256
001cf36f…97fef610
Header · Sections · Rebuilding · Leads
Conditions that start story scenes: sections by kind of moment (a battle turn starting or ending, damage, a unit dying, arriving at a world-map location, a story condition), each a list of children, and each child an ID with a condition in task instructions. When the game reaches such a moment it runs the section and acts on the IDs whose conditions hold: for turn and damage sections the ID is the scene to play, elsewhere it picks a pgrs row by invk_id. Found as a pakd entry with pakd id 2 in every event/chapter/chapter_*.dat, as three entries of event/event_data.dat, and as entries 11–19 of WarrenReport/WarrenReport.dat. Terms are as in the glossary.
| Based on | All 21 INVK entries: event/chapter/chapter_*.dat#1 (9), event/event_data.dat#7, #11, #16 and WarrenReport/WarrenReport.dat#11–#19; Gibbed's ScreenplayInvocationFile.cs and Screenplay/InvocationType.cs (checked 2026-10-11); the unpacked executable, SHA-256 7b0230a11498f40396c9048345e7cfe0ef442cb7ca8faffc6f197920d2669599. |
| Checked | 2026-10-12 |
| Method | A single-file C# program parsing every entry with Gibbed's layout; static analysis with Ghidra 12.1.4 of the setup (0x14035a290), the runner (0x140359e20) and its callers (R31 review, 2026-10-11). Checked against the game data, 2026-10-12: the round trip, tor round-trip local/game/Data/event/chapter local/game/Data/event/event_data.dat local/game/Data/WarrenReport/WarrenReport.dat --docs docs, rebuilt all 21 invk entries from their decoded rows and the 11 files byte for byte; tor read-back over the same files read the decoded data back to the same files. 2026-10-13 (R36): the round trip again, tor round-trip local/game/Data/event local/game/Data/WarrenReport --docs docs --quiet, 21/21 invk entries byte-identical; the decode refuses an entry whose section_count isn't the number of rows in Sections, whose padding isn't FF FF, whose sections don't each start where the one before ended, with an empty section not followed by FF FF, or with a child that has no instructions. DuckDB over the exported rows (data/game/event/**/*.invk/*.jsonl and data/game/WarrenReport/**/*.invk/*.jsonl, from tor export; a child starts at each row with step 0) for the facts that cite it. No in-game tests yet. |
| Name from | The game's magic INVK. |
Header
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x00 | 4 | raw[4] | magic |
INVK. The game doesn't check it; it finds the entry by pakd id. |
confirmed | The round trip (21/21). Data; 0x140350880. Agrees with Gibbed. |
| 0x04 | 2 | u16 | section_count |
Number of sections: 16 in every entry. A section index at or above it runs nothing. | confirmed | The round trip (21/21; R36: the decode refuses another count). 0x140359e20. Gibbed count. |
| 0x06 | 2 | raw[2] | padding |
FF FF. Never read. |
confirmed | The round trip (21/21; R36: the decode refuses other bytes). Agrees with Gibbed. |
| 0x08 | 4 × section_count |
u32[] | section_offsets |
Each section's offset from the start of the entry. | confirmed | The round trip (21/21). 0x14035a290. Gibbed offsets. |
Sections
Each section is a u16 child count, then the children, one after another, each ending where the next section starts. An empty section is a count of 0 followed by FF FF, which the game never reads. A child (an invk child) is a u16 ID, then task instructions up to FF FF.
- confirmed: every entry parses this way, every section ending exactly at the next section's offset. Source: the round trip (R36).
- confirmed: every empty section is
00 00 FF FF; no child has no instructions; no child ID appears twice in a section, and in some sections the IDs aren't in order. After the last section comealign16(end + 1)zeros, thepakdentry's padding (containers.md). Source: the round trip (R36: the decode refuses the first two otherwise); DuckDB over the rows (R36) for the IDs; the byte maps of the file pages (tor pages, 2026-10-13), where each entry ends in a zero tail up to the next 16-byte boundary (event_data, WarrenReport, the chapter files). - likely: a child's instructions are one expression (opcode 96–113) with its u8 count of operands, then that many single conditions, then
FF FF; every child in the game's files starts with an expression, and its count is the number of conditions that follow (DuckDB over the rows, R36: each child's step-0opcodeandcountagainst its other rows). A bare condition at the top level would leave the game reading theFF FFas the next child's ID. Source:0x140359d50,0x140359f90. - likely: the game runs a child only when its ID equals the one asked for, or is in the
invkfilter (theinvk_ids of the current story group), or the filter is empty; it collects the IDs whose conditions hold. Both lists hold 32 entries and overlap after that. Source:0x140359e20.
What each section is for (0x140359e20's callers). Sections with no meaning shown are named by index.
| Section | Name | Confidence | Run when |
|---|---|---|---|
| 0 | section_0 |
— | A pgrs condition of type 4 asks for a child (0x140356170). |
| 1 | attack_turn_start |
likely | A battle turn starts (AttackTurnEventJob, 0x1402cfbc0). The ID is the scene to play. |
| 2 | section_2 |
— | After damage, and when status effects end a turn (DamageResultEventJob, StatusTurnEndJob, 0x1402cfc40). The ID is the scene. |
| 3 | arrive_worldmap_location |
guess | Arriving at a world-map location (0x1403e31b0). |
| 4 | section_4 |
— | Arriving at a world-map location (0x1403e3360). |
| 5 | attack_turn_end |
likely | A battle turn ends (TurnEndEventJob, 0x1402cfab0). The ID is the scene. |
| 6 | section_6 |
— | From the world map (0x1403e5d50; 0x1403e59d0 case 0x12 calls 0x140359e20(…, 6, -1)). |
| 7 | section_7 |
— | From the world map (WorldmapManager::vfunction4). |
| 8 | section_8 |
— | No runner found. |
| 9 | section_9 |
— | No runner found. |
| 10 | section_10 |
— | No runner found. |
| 11 | section_11 |
— | No runner found. |
| 12 | section_12 |
— | No runner found. |
| 13 | section_13 |
— | From the minimap menu (0x1403e3130). |
| 14 | section_14 |
— | A unit dies, in event/event_data.dat#7 only (PERFORM_DEAD → 0x1402d0030). |
| 15 | warren_report |
confirmed | The Warren Report menus (0x14048b2a0); empty in the chapter files and event_data.dat; it holds every child of WarrenReport/WarrenReport.dat#11–#19, whose other sections are empty (DuckDB over the rows, R36). Named by the game's folder Data/WarrenReport and namespace Taco::Application::WarrenReport. |
Rebuilding
How the toolchain writes an invk entry from its sections, one per row of the table above, in that order. confirmed: these rules rebuild all 21 entries byte for byte from their decoded rows. Source: the round trip, 2026-10-12.
- The header:
INVK,section_count(the number of sections),FF FF, then each section's offset. - Each section: its child count, then each child's ID and its instructions as
taskwrites a list's, endingFF FF; an empty section is a count of 0 thenFF FF. - After the last section:
align16(end + 1)zeros when the original's bytes after its data were exactly that (containers.md); otherwise those bytes as they are.
Leads
- Sections 8–12: no runner found.
- Gibbed's
InvocationTypenames the sections (ATStart,ATEnd,IncomingStrongpoint, …); their comments look like PSP strings, none of which is in this executable's strings (grep AT_start strings.jsonlfinds none).