.QSC -- Script Source & MagicObjects

1. Architectural Role & Engine Purpose

The QSC ("Q Script Code") format is the high-level human-readable script source language used by Innerloop designers to script all gameplay behaviors, AI patrols, weapon mechanics, trigger volumes, and camera cuts. In retail shipments of Project I.G.I., only binary .qvm files were included.

Naming — read this carefully. QSC stands for "Q Script Code": the Q is the bare C-style letter used throughout the Innerloop C++ codebase (cf. QSC, QVM, .qsc). It is not "Quake Script" and not "Quest Script" — neither name appears in any engine string, header, or tool in the retail files. The Innerloop QVM bytecode VM was derived from / inspired by the Quake virtual machine, but the two are not directly related: QVM has its own LOOP container, its own 49-opcode instruction set, and its own registrar-based native bridging.

Through reverse-engineering and the development of project-igi-converter, QSC source code is reconstructed via complete control-flow graph (CFG) analysis, basic-block restructuring, and Abstract Syntax Tree (AST) synthesis.

2. Script Compilation & Decompilation AST Pipeline Diagram

The QSC language pipeline maps bidirectionally between structured high-level scripts and raw VM v8.5 bytecode:

QSC SCRIPT LANGUAGE — ABSTRACT SYNTAX TREE (AST) LAYOUT QSC SCRIPT SOURCE — LEXICAL & STRUCTURAL GRAMMAR SOURCE AST High-level structured mission script source reconstructed from retail QVM bytecode Top-level function calls, nested arguments, symbol tables GLOBAL SCOPE & STATE VARIABLES GLOBALS Static persistent variables across all mission tasks Task(1, "patrol", Loop(3, Goto(...), Wait(5.0))); COOPERATIVE TASK BLOCKS TASKS Concurrent coroutines scheduled by engine scheduler Nested calls evaluated recursively as arguments SENSORY EVENT HANDLERS EVENTS Interrupt callbacks triggered by guard perception Task_New(-1, "MagicObjConfig", name, model, type); NATIVE ENGINE CALL WRAPPERS NATIVES Direct bindings into IGI.EXE engine functions AI_WalkToWaypoint(102); AI_PlaySpeech("ALARM.WAV"); DECOMPILER CONTROL FLOW RECOVERY CFG RECOVERY Bytecode basic blocks restructured into structured loops and branches JMP/JZ/JNZ → Structured if/else, while loops, and switch jump tables

3. Engine Script Architecture & Task Scheduler Diagram

The Innerloop script engine executes QSC scripts concurrently using an internal cooperative task scheduler:

QSC SCRIPT TOOLCHAIN — BIDIRECTIONAL COMPILATION & DECOMPILATION 1. COMPILATION (qc.exe) • Parse .QSC Text Source • Build Symbol Tables • Emit VM v8.5 Bytecode 2. RETAIL BINARY (QVM) • Shipped in AI/*.qvm • Packed Magic "LOOP" • Stripped Local Names 3. CFG DISASSEMBLY • qvm disasm (.qas) • Extract Basic Blocks • Dominator Tree Analysis 4. DECOMPILATION • qvm decompile (.qsc) • High-Level Code Gen • Event Hook Recovery

4. Language Grammar & Structural Constructs

QSC is a semicolon-terminated, function-call-based language. A QSC file consists of a sequence of top-level function calls separated by semicolons:

FunctionName(arg1, arg2, NestedFunc(innerArg), "string arg");

Argument types:

  • String: Enclosed in double quotes ("hello"). Supports \" escape sequences.
  • Number: Integer or floating-point (e.g. 42, -3.14, 1e5).
  • Boolean: TRUE or FALSE (case-sensitive).
  • Function: Nested function calls act as arguments (evaluated recursively).

Parser limits: MAX_QSC_FUNCS 4096 maximum function nodes; MAX_QSC_ARGS 65536 maximum argument nodes.

Mission behavior entries such as magicobjconfig.qsc use this same grammar with Task_New(-1, "MagicObjConfig", name, model_id, TASKTYPE_XXX) calls that bind mesh sub-parts (ATTAs) to runtime behaviors (see QVM & MagicObjects). Native engine routines in IGI.EXE are invoked through these script function calls.

5. QSC Script Example (Function-Call Syntax)

Task(1, "patrol",
    StatusText(1, "Guard patrolling"),
    AIGraph(1, "patrol_route_01"),
    Loop(3,
        Goto(100.0, 200.0, 50.0),
        Wait(5.0)
    )
);

MagicObject config example (magicobjconfig.qsc):

Task_New(-1, "MagicObjConfig",
    "name",
    "model_id",
    TASKTYPE_XXX
);