1. Architectural Role & Engine Purpose
The QVM (Covert Strike Virtual Machine Bytecode) format powers the advanced tactical simulation, multi-tiered stealth awareness systems, security alarm matrices, surveillance camera panning networks, and mission objective progressions in I.G.I. 2: Covert Strike (2003).
For the sequel, Innerloop kept the same LOOP bytecode container (VM v8.5,
49 opcodes) while shipping a much richer script catalog: AI routines, physics hooks,
per-level objects.qvm, config and menu scripts. QVM content is more complex even though the
bytecode format is shared with IGI 1.
2. Binary File Layout & Memory Map Diagram
The IGI 2 QVM format keeps the shared 60-byte LOOP header (VM v8.5) with table indexing for larger scripts:
3. Engine Pipeline & Multi-Threaded Task Scheduler Diagram
IGI 2 executes scripts across cooperative priority queues, dynamically handling guard AI combat tactics:
4. Architectural Mechanics & Engine Implementation Details
The shared VM v8.5 runtime executes IGI 2's larger script catalog (AI, physics, per-level
objects.qvm, config, menus) without changing the bytecode format:
- Shared Opcode Space: The same 49 opcodes (
0x00–0x30) as IGI 1 — stack pushes, arithmetic, comparisons, branches (BRA/BF/BT),JSR/RET, andCALL(0x18) for script subroutines and native engine methods. - Richer Script Content: Larger identifier/string pools and per-level scripts drive squad behaviors, alarm matrices, camera networks, and mission objectives.
- Native Symbol Registration: Engine APIs are bound via native registrar push-pairs in
IGI2.EXE, providing fine-grained control over guard vision cones, weapon dispersion, and dialogue playback.
5. Binary Header Specification (60 Bytes)
| Offset | Size | Type | Field | Description |
|---|---|---|---|---|
0x00 | 4 | char[4] | signature | Magic FourCC: "LOOP" (0x4C4F4F50 in little-endian) |
0x04 | 4 | uint32_t | ver_major | Major version: strictly 8 |
0x08 | 4 | uint32_t | ver_minor | Minor version: strictly 5 (VM v8.5, shared with IGI 1) |
0x0C | 4 | uint32_t | of_itable | Byte offset to Identifier Table (ITable) |
0x10 | 4 | uint32_t | of_ivalue | Byte offset to Identifier String Pool |
0x14 | 4 | uint32_t | sz_itable | Size of Identifier Table in bytes |
0x18 | 4 | uint32_t | sz_ivalue | Size of Identifier String Pool in bytes |
0x1C | 4 | uint32_t | of_stable | Byte offset to String Table (STable) |
0x20 | 4 | uint32_t | of_svalue | Byte offset to String Value Pool |
0x24 | 4 | uint32_t | sz_stable | Size of String Table in bytes |
0x28 | 4 | uint32_t | sz_svalue | Size of String Value Pool in bytes |
0x2C | 4 | uint32_t | of_ctable | Byte offset to Bytecode Section (CTable) |
0x30 | 4 | uint32_t | sz_ctable | Size of Bytecode Section in bytes |
0x34 | 4 | uint32_t | flags | Scheduler configuration flags |
0x38 | 4 | uint32_t | reserved | Alignment padding |
6. C++ Struct Definitions
#pragma pack(push, 1)
struct QvmHeaderIgi2 {
char signature[4]; // "LOOP"
uint32_t ver_major; // 8 (shared VM v8.5)
uint32_t ver_minor; // 5
uint32_t of_itable; // Offset to Identifier Table
uint32_t of_ivalue; // Offset to Identifier Strings
uint32_t sz_itable; // Size of Identifier Table
uint32_t sz_ivalue; // Size of Identifier String Pool
uint32_t of_stable; // Offset to String Table
uint32_t of_svalue; // Offset to String Values
uint32_t sz_stable; // Size of String Table
uint32_t sz_svalue; // Size of String Value Pool
uint32_t of_ctable; // Offset to Bytecode Section
uint32_t sz_ctable; // Size of Bytecode Section
uint32_t flags; // Execution flags
uint32_t reserved; // Alignment
};
static_assert(sizeof(QvmHeaderIgi2) == 60, "QvmHeaderIgi2 must be 60 bytes");
#pragma pack(pop)