TO:R Data Map

Event scripts

Source
docs/executable/event-scripts.md
SHA-256
8eb122c3…a5e9ea73
Contents

The VM · Opcodes · Natives · Leads

How the executable runs the event script modules: the script VM, its opcodes, and the natives (game commands) that scripts call. The two tables it runs from are exported to data/exe/ as views exe.script_opcodes and exe.script_natives (The indexes). It's the same VM as the PSP release's, which Gibbed documented (reference_tools/Gibbed.LetUsClingTogether/docs/cutscene_scripting/), with more natives. Terms are as in the glossary.

Based on The unpacked executable, SHA-256 7b0230a11498f40396c9048345e7cfe0ef442cb7ca8faffc6f197920d2669599; every script module in event/ (event_script.md); Gibbed's opcodes.md and psp_natives.md (checked 2026-10-11).
Checked 2026-10-11
Method Static analysis with Ghidra 12.1.4: the decompiled code in local/exe/decomp/, objdump -d, and ScriptTablesScript (Our Ghidra scripts); single-file C# programs counting opcode and native uses over the scripts; the R31 review of names, which read every interpreter case (2026-10-11). Every fact here is at most likely.

The VM

Opcodes

The interpreter is one switch on the opcode byte (0x14034a1b0), with cases 0–99, whose numbers match Gibbed's opcode list. After each instruction it advances by the instruction's size, read from a table of 0x18-byte records at 0x140C9E300, one per opcode; when an instruction blocks (a native that isn't done, a wait), it steps back by the same size and the thread stops there.

exe.script_opcodes has one row per record, 100 rows (ScriptTablesScript): opcode, record_address, size (bytes, 0, 1 or 3), operand_kind, unk_0x04, name_address and name (the record's name pointer), unk_0x10 and unk_0x14.

operand_kind Opcodes The u16 operand is
0 the rest none (size 1; nop_0, 0, has size 0), or unused (98, size 3)
1 79 push_int_immediate a signed value
2 76 push_tag, 77 push_act, 78 push_int_table an index into the unk_0x2C_offset table or the int table
3 72–75 (push_variable, pop_variable, …) an index into the variable table
4 88 call, 89 call_pop_r08, 93 call_result_r10, 94 call_pop_r08_result_r10 a native's number (Natives)
5 80 push_float_table an index into the float table
6 95 request_all, 97 request_wait_all, 99 request_i_all an index into the unk_0x30_offset lists
7 2 tag (size 0) —
8 1 label (size 0), jumps 81–87, 90–92, 96 an index into the script's jump table

Opcode names

Each opcode's name, from its case in 0x14034a1b0 (R31 review). The guess names keep Gibbed's or FF12's label (in the last column) where the code fits it but doesn't show it; opcodes with no meaning shown are named by number. "Pops 2" takes the top as the right-hand operand.

Opcode Name Confidence What the case does Gibbed
0 nop_0 likely Nothing (size 0); the same case as 42, 43, 44, 51 Invalid
1 label guess Not a case: the default ends the run; record size 0, operand_kind 8 Label
2 tag guess Default case; size 0, operand_kind 7 Tag
3 halt likely Sets thread +0xD bit 0x40, which makes the interpreter return at entry, and stays on the instruction Halt
4 discard likely Pops one int and drops it System
5 logical_or likely Pops 2, pushes 1 unless both 0 LogicalOr
6 logical_and likely Pops 2, pushes 1 when both non-zero LogicalAnd
7 bit_or likely | Or
8 bit_xor likely ^ ExclusiveOr
9 bit_and likely & And
10 equal likely 0x140349d70 returns sign(second − top); pushes == 0 Equal
11 not_equal likely ≠ 0 NotEqual
12 greater_than likely > 0 GreaterThan
13 less_than likely < 0 LessThan
14 greater_or_equal likely ≥ 0 GreaterThanOrEqual
15 less_or_equal likely ≤ 0 LessThanOrEqual
16 shift_left likely << ShiftLeft
17 shift_right likely >>, arithmetic ShiftRight
18 add likely Int or float Add
19 subtract likely Subtract
20 multiply likely Multiply
21 divide likely ÷ 0 gives 0 Divide
22 modulo likely Ints only Modulo
23 logical_not likely Pushes value == 0 Not
24 bit_not likely Pushes ~value, not the negation Negate
25 op_25 — Default case Undefined25
26 op_26 — Default case Undefined26
27 push_r10 likely Pushes frame +0x10 PushA
28 pop_r10 likely Pops into frame +0x10 PopA
29 push_r08 likely Frame +0x08 PushX
30 push_r0c likely Frame +0x0C PushY
31 pop_r08 likely PopX
32 pop_r0c likely PopY
33 request guess Pops 3, starts a function on another script (0x14046c390, 0x14046b780), mode 0 to 0x14034e2e0 Unknown33; FF12 REQ
34 request_f guess As 33, mode 2 Unknown34; FF12 FREQ
35 request_t guess As 33, mode 4 Unknown35; FF12 TREQ
36 request_sw guess The request family's second path (LAB_14034b977) Unknown36; FF12 REQSW
37 request_f_sw guess As 36 Unknown37; FF12 FREQSW
38 request_t_sw guess As 36 Unknown38; FF12 TREQSW
39 request_ew guess 0x14034e2e0(…, 0, 3) Unknown39; FF12 REQEW
40 request_f_ew guess (…, 2, 3) Unknown40; FF12 FREQEW
41 request_t_ew guess (…, 4, 3) Unknown41; FF12 TREQEW
42 nop_42 likely Case 0's group NoOperation42
43 nop_43 likely Case 0's group NoOperation43
44 nop_44 likely Case 0's group NoOperation44
45 return likely Pops the call frame (0x14046b3e0, 0x14046b830), resumes the caller or ends the thread (0x140349be0) Return
46 return_n likely Pops n, 0x14046b560(thread, n + 1, 0) ReturnN
47 return_t guess As 45, then 0x14046c020(thread, 0x14046b690(…)), unread ReturnT; FF12 RETT
48 return_t_n guess Pops n, then as 47 ReturnTN; FF12 RETTN
49 op_49 — Default case Undefined49
50 request_wait guess Pops 2, 0x14046c100(script, fn, −1), waits (state 3) Unknown50; FF12 REQWAIT
51 nop_51 likely Case 0's group NoOperation51
52 request_change guess Pops 3; writes a u16 into slot (< 8) +0xA8 of another thread Unknown52; FF12 REQCHG
53 request_cancel guess Pops n, 0x14046b560(thread, n + 1, 1) Unknown53; FF12 REQCANCEL
54 pop_int_0 likely Pops an int into thread +0x54 PopInt0
55 pop_int_1 likely Into thread +0x58 PopInt1
56 pop_int_2 likely Into thread +0x5C PopInt2
57 pop_int_3 likely Into thread +0x60 PopInt3
58 pop_float_0 likely Pops a float into thread +0x64 PopFloat0
59 pop_float_1 likely Into thread +0x68 PopFloat1
60 pop_float_2 likely Into thread +0x6C PopFloat2
61 pop_float_3 likely Into thread +0x70 PopFloat3
62 push_int_0 likely Pushes thread +0x54 back PushInt0
63 push_int_1 likely +0x58 PushInt1
64 push_int_2 likely +0x5C PushInt2
65 push_int_3 likely +0x60 PushInt3
66 push_float_0 likely Pushes thread +0x64 back PushFloat0
67 push_float_1 likely +0x68 PushFloat1
68 push_float_2 likely +0x6C PushFloat2
69 push_float_3 likely +0x70 PushFloat3
70 op_70 — Pops one int, then a guard-check stub Undefined70
71 op_71 — Default case Undefined71
72 push_variable likely Through 0x14034d500, by type PushVariable
73 pop_variable likely Clamped to the type PopVariable
74 op_74 — Resolves into 0x140F2D240, pushes nothing PushVariableDebug
75 push_variable_ref likely Pushes offset | scope | 0x70000000 PushVariablePointer
76 push_tag guess Reads the table at context +0x70 (unk_0x2C_offset, empty everywhere) PushTag
77 push_act guess As 76 PushAct
78 push_int_table likely Pushes ints[operand] PushIntFromTable
79 push_int_immediate likely Pushes the signed operand PushIntImmediate
80 push_float_table likely Pushes floats[operand] PushFloatFromTable
81 jump likely Jump
82 jump_if_r0c_eq likely Pops v; jumps if v == r0c JumpIfEqual
83 jump_if_r0c_lt likely r0c < v JumpIfGreaterThan, named from v's side
84 jump_if_r0c_le likely r0c ≤ v JumpIfGreaterThanOrEqual
85 jump_if_r0c_gt likely v < r0c JumpIfLessThan
86 jump_if_r0c_ge likely v ≤ r0c JumpIfLessThanOrEqual
87 jump_if_r0c_between likely Pops 2 bounds JumpIfBetween
88 call likely r08 = thread ID, 0x14034d330 Call
89 call_pop_r08 likely Pops r08 first CallAct
90 pop_r08_jump likely PopXAndJump
91 pop_r08_jump_if_nonzero likely PopXAndJumpIfNotZero
92 pop_r08_jump_if_zero likely PopXAndJumpIfZero
93 call_result_r10 likely r08 = thread ID, call, pops the result into r10 CallAndPopA
94 call_pop_r08_result_r10 likely Pops r08 first, then as 93 CallActAndPopA
95 request_all guess Starts list operand of unk_0x30_offset (0x14034de80), then 0x14034e4e0(…, 0) Unknown95; FF12 REQALL
96 jump_internal guess The same case as 81 JumpInternal
97 request_wait_all guess 0x14034df10, waits (state 5) Unknown97; FF12 REQWAITALL
98 inc_init_tag guess Peeks 3, sets bit 1 << top on a thread (0x14034d9a0) Unknown98; FF12 INCINITTAG
99 request_i_all guess As 95 with last argument 1 Unknown99; FF12 REQIALL

Natives

A call opcode's u16 operand is a native's number: bits 12–15 pick one of 16 native tables, bits 0–11 the entry (0x14034d330). Only table 0 is registered: 328 entries of 0x20 bytes at 0x140C9FF80, with the count, 328, at 0x140C9FF70 (0x14046c4a0). Each entry holds up to three handlers, called with the script, the thread, a per-call work area and the stack:

Offset Handler When
0x00 start Once, on the first try: pops the arguments. 98 natives.
0x08 update Every try, start's included: returns bit 0 set while the command is still running, and the VM blocks and retries the instruction later. 99 natives.
0x10 slot_0x10 Never set, and never read.
0x18 finish When done: runs the command, or sets its result. 229 natives.

After finish, the VM pushes the result: the work area's value as an int or a float when the handler set its state byte to 2 or 3, else 0. call_result_r10 then pops it into r10; call_pop_r08 and call_pop_r08_result_r10 first pop an actor into r08. Source: 0x14034d330, 0x14046c430 (start), 0x14046c3f0 (update), 0x14046c460 (finish), 0x14034a1b0 (cases 0x58–0x5E).

exe.script_natives has one row per entry (ScriptTablesScript): native, record_address, start, update, slot_0x10 and finish (addresses, null when empty) each with its function's name, and pop_calls: the direct calls to the two pop functions in start and finish. pop_calls is the argument count only for natives that pop their arguments directly and always; it misses pops through a helper (natives 27–38) and counts optional ones (15, 16). Handlers that weren't functions in the Ghidra project (50) were made functions by the script. The same script writes the task opcode table, view exe.task_opcodes: opcode, record_address, operand_size, unk_0x04, handler, handler_name, one row per record of the 114 at 0x140694BD0.

Numbering against the PSP release

What the natives do

Each native's name, arguments (in the order a script passes them), result and what it does are in the names list, names.jsonl (../../data/exe/names.jsonl), which the toolchain reads for _native in the decoded event scripts: one row per handler function, named Native<number>_<name> (with _Start, _Update or _Finish when the native has more than one), its comment holding the rest; exe.script_natives shows the names in its *_name columns. A handler shared by several natives is named after the first, and its comment lists the others. Found by reading each native's handlers and their callees (static analysis, 2026-10-11). The words actor, current actor, bind and talk are Gibbed's, kept as guess names.

The natives come in groups (likely):

Natives Group
0–12 Waits, the end of the event (1), screen fades and the map's sepia tint (2–8), message speed
13–46 The two message windows at 0x140D518B0: talk from an actor or from the actor bound to a script thread, waiting or not
47–94 Actors: direction, animations, portraits, position, class; reading game variables and game flags (90, 91; 92 and 93, which would set them, do nothing)
95–122 Movement over the map's tiles (speed, waypoints, tiles to avoid, waits)
123–166 Binding actors to script threads, showing and hiding them, fades
167–182 The camera
183–193 Music and sound
194–238 Units in battle (AI value, wait turn, animations, falling, vanishing, status effects), the letterbox bars (203, 204), turning event skipping on and off (205, 206), map animations, markers and tile colours
239–282 Vfx in 9 slots at 0x140D51EC0, projectiles, a unit using an ability at a tile, weather (guess), a system message (268), the battle result screen; 272–274 do nothing on PC
283–327 Reborn's: voiced talk and voice playback (283–315, voice bank by the voice-language setting), music, settings queries, an achievement call (317, unused)

Leads