At a glance

Movie overlay
PROT 0970 at 0x801CE818; play loop 1236 bytes; ring = 32 sectors at asset buffer +0x10000
Frame decoder
Iki: 10-byte header, LZSS qscale/DC table, AC VLC stream; port psx_mdec::MdecDecoder
Opening timeline
Partition-2 named records in each scene MAN, run by the field VM FUN_801DE840 as a spawned context
Camera
Op 0x45: 10 params into globals; mover actor FUN_801DD310; port engine-camera::camera_mover
Roller
Handler FUN_80037174; geometry at _DAT_801C6EA4 + 0x4C..0x52; port CutsceneNarration
Grade
Palette law (L, (L·247)>>8, L>>1) on the two graded CLUT rects; port Renderer::set_palette_grade
Confidence
Confirmed; the gold-grade CLUT rewrite is the prologue effect scripts' CLUT-cell cyclers
per-frame actor walk FUN_8002519C - every live context gets a slice each frame timeline record narration roller camera mover actor channels cut spawn glide spawn scene change crawl 1 scrolls crawl 2 snap dolly, apply 840 frames
The timeline never blocks on the roller or the mover: it spawns them and keeps executing, so camera cuts play under the scrolling text. It blocks only before a new crawl block, on a channel's completion flag, and before its terminal scene change.

Movie overlay: ring, demux and MDEC feed

StageMechanism
Decoder selectDAT_801E09FC: Iki by default; dispatch slots 9/10 (dev files) select the STRv2/v3 path
Ring32 sectors in the streaming asset buffer at _DAT_8007B85C + 0x10000, set up by StSetRing / StSetStream
ReadSetmode 0xE0 (Speed | RT | Size1: real-time XA on, sector filter off) then CdlReadS
DemuxData-ready callback gates each sector on magic 0x160 + stream number, skips to start_frame, accumulates sequential chunks into per-frame slots (status 2 = complete; a full ring drops the frame), latches end_frame on chunk 0
ConsumeStGetNext / StFreeRing - the retail counterpart of the port's StrFrameAssembler
DecodeIki: LZSS qscale/DC table, then a GTE leading-zero-count VLC scan into the MDEC-code double buffer
FeedDMA-0 into the MDEC; an out-slice callback blits each 32-px strip into the slot's VRAM rect; sync waiters spin on a 0x100000-iteration budget

The five MDEC register/DMA helpers (feed, reset, two sync waiters, timeout dump) are the port boundary: they describe the chip, so the port has no counterpart for them. Everything above them - loop, ring, pump, slice callback, output control word - is a decision about the bitstream and has a crates/mdec counterpart.

The STRv2/v3 path (dev slots only)

This decoder is a bit-prefix lookup, not a run/level VLC. A table unpacked at runtime into DAT_801E0A00 by FUN_801F1A00 - called once per movie even for Iki slots that never read it - stores pre-baked MDEC output codes, one to three per hit plus a bit length, in four regions: luma DC, chroma DC, AC primary, AC secondary. Only the DC coefficients (raw 10-bit in v2; size-prefixed predicted differences per channel in v3), the 0x7C1F escape codes and the end padding are computed. Ported as psx_mdec::strv2_table (unpacker, mdec strv2-table <overlay>) and strv2_decode::decode_frame; with no retail movie on this path there is no golden decode, so its tests pin the code paths against the disassembly.

Overlay residency window (captured during playback)
AddressStrideContents
0x801CAE0824 Blibcd CdlFILE cache ([CdlLOC][size][name[16]]) for the last directory searched - the MOV dir while a movie plays; not a movie structure
0x801CCA8056 B × 6ISO9660-shape directory records of the six movie files
0x801CE810variablePath strings: \DATA\MOV.STR;1, \DATA\MOV15.STR;1, \MOV\MV1A.STR;1, \MOV\MV6..MV1.STR;1
0x801CE8ACvariablePost-movie return-scene labels: town0b map01 chitei2 map02 jou uru2 town0e
0x801D0A6C32 B × 23Dispatch table - STR FMV table

MOV15.STR is a 15 fps test file and MV1A.STR an alternate cut of MV1; neither ships. The 8-bit ADPCM decode path (BitsPerSample::Eight) has no example in the movie corpus and is covered by synthetic tests only.

Decoding one Iki frame

StepWhat happens
1 Header10 bytes: mdec_code_count, magic 0x3800, width, height, lzss_size
2 LZSS tablelzss_size bytes inflate to block_count × 2; control bits LSB-first, 0 = literal, 1 = back-reference (length byte +3, 1- or 2-byte offset +1, overlapping copies allowed). Block i: (table[i] << 8) | table[i + block_count] = 6-bit qscale, 10-bit signed DC
3 AC stream16-bit LE words, MSB-first; PSX VLC run/level codes per block, EOB 10, escape 000001 + 16-bit raw run << 10 | level. A full block still ends with an explicit EOB
4 Dequantise + IDCTcoef[0] = DC × Q[0]; coef[zigzag[i]] = (level × Q[i] × qscale + 4) >> 3, unclamped; two-pass separable IDCT with IDCT_C pre-scaled by 2048, full i64 rows, one >> 24 after the columns
5 MacroblocksSix blocks each: Cr, Cb, Y0..Y3; laid out column-major, 16 px down then the next column
6 Colour4:2:0 upsample; R = Y' + (91881·Cr >> 16), G = Y' − ((22554·Cb + 46802·Cr) >> 16), B = Y' + (116130·Cb >> 16) with Y' = Y + 128; row-major RGBA8 out

Implementation crates/mdec/src/lib.rs (MdecDecoder, AC_CODES, iki_lzss_decompress, IDCT_C, Q_MAT); a decoded-frame fingerprint is pinned against the disc. XA decode is bit-exact against a lossless reference of a real track; sector layout and filter coefficients are on XA audio.

The opening timeline

A timeline is a partition-2 named record in the scene MAN, executed by the ordinary field VM. Two things spawn one: op 0x44 in the scene's entry script (operand = a global record index re-based into partition 2), or a walk-on tile trigger (kind-1 record [tile_x][tile_z][p2_record][gate] in the scene .MAP's trigger block; the entry seat lands on the tile and fires the same tick). Both end in FUN_8003BDE0, which tests the record's story-flag gates against the bitmap at DAT_80085758 and spawns a VM context.

The scene's entry script runs first. The MAN loader FUN_8003AEB0 runs the script's install slice itself (FUN_8003AB2C, which calls the dispatcher directly), before any actor-list pass, so on the arrival frame the entry script's ops land before an arrival record's. In town01 that decides the visible tile window: the entry script sets the walking window (-10, -6, 8, 14) and the opening record then sets (-17, -13, 10, 31), which keeps the whole village in the establishing pan. The engine opens its first tick with that slice, ahead of a record installed at scene entry, and then runs the frame's own system pass, the script's second, as retail's first frame does.

FieldSizeMeaning
name_len1Name length in characters
namename_len × 2SJIS record name
C0 + bytes1 + C0Skipped
C1 + u16s1 + 2·C1Block if ANY flag set
C2 + u16s1 + 2·C2Require ALL flags set
script-Entry PC; opdeene record 18 opens with an effect-colour reset then GFLAG_SET 26

System flag 7 is raised by the MAN loader FUN_8003AEB0 on every scene load (ori 0x1 into 0x80085758 at 0x8003AEF4), and no shipped script clears it. A record whose C1 list names it never spawns: over a hundred developer leftovers carry one, among them town01's two サウンド外 / サウンド内 walk-on records south of the Genesis Tree, whose whole body is a Greetings. box.

Nearly every op in the record carries the cross-context target 0xF8, which resolves to the player / camera-anchor actor - so the timeline drives the camera and the lead actor rather than itself. On-disc records have no end opcode: they park in a Nop+JmpRel-to-self loop or loop back to their top as a resident driver, and retail leaves them spinning invisibly.

Camera Configure (op 0x45)

A big-endian 10-bit mask selects which of ten signed-16 parameters follow; each is written to a camera struct at 0x801C6EA8 and committed by FUN_801DE084, which either snaps (apply == 0, killing any glide in flight) or hands a mover actor ten (start, end) pairs plus one shared progress, duration and curve.

ParamGlobalRole
0 / 1 / 2_DAT_8007B790/92/94Pitch / yaw / roll (GTE angles, 4096 = 360°); eight scenes stage a non-zero roll
3 / 4 / 5_DAT_800840B8/BC/C0Eye-space offset trio (dx, dy, depth) - slot 5 is how far the eye sits behind the focus
6 / 7 / 8_DAT_80089118/1C/20Focus point; GTE translation is (−X, +Y, −Z)
9_DAT_8007B6F4H, the projection distance (zoom)

The view builder composes screen = H · (R · (v − focus) + tr_eye) / Ze, with a constant 6× world scale folded into R. The mover advances t by the frame-skip factor each frame, so apply is a duration in display frames; every axis uses the same curve, angles included, with no shortest-arc handling.

CurveShapeFormula for offset k = end − start
1 (and any other)Linear(k·t)/d
2Quadratic ease-out(n + (n/d)(d − t))/d, n = k·t
3Quadratic ease-in((k·t)/d · t)/d
4Ease-in-outCurve 3 to the midpoint over d >> 1, then curve 2

Port: legaia_engine_camera::camera_mover, and both play hosts draw a scripted shot from the camera globals it writes (frame_step::scripted_view). Because retail scales the world 6× and the engine renders at 1×, tr_eye is divided by 6 - the perspective divide makes both project identically. Reference frame: focus (8640, 0, 10304), pitch 180, yaw −2967, H 792, tr_eye (260, 1293, 17145) projects the party to (172, 180), matching the capture.

The narration roller

Spawn op CC F8 80 N allocates a child actor from template DAT_801F28A0 with handler FUN_80037174, points its script pointer at the page-count byte, and the parent measures the pages to skip past the block. Config op CC F8 E8 w0 w1 w2 w3 (four signed-16 words) is mode-selected by w3:

w3Effect
0Seed geometry: +0x4C window top Y (default 64), +0x4E visible lines of 16 px (default 8, bottom clamped ≤ 232), +0x50 scroll divisor (px/frame = frame-skip / divisor, default 4)
1w0 == 0 pauses the live roller; otherwise +0x52 = stop after N lines
2Resume
3Unlink the child

Geometry and speed come from the scene: the roller reads its config block through *0x801C6EA4, the scene reset FUN_8003A024 stores 0x40 / 8 / 4 / 0, and a seed op overwrites the first three. A 4C E8 release count is block state too, so it dies with the scene. Every crawl block on the disc runs on a seed op placed immediately before it, or on the one an earlier block of the same scene left:

SceneSeeds (top / slots / divisor), in script order
opdeene0x40 / 8 / 4 (all three words zero - the defaults) for both blocks
opstati0x80 / 5 / 5, then 0x80 / 5 / 4
opurud0x80 / 4 / 4 twice, then 0x80 / 4 / 3 (the second block reuses the first seed)
map010x80 / 5 / 4

The line pitch is a fixed 16 (retail addiu s3,s3,0x10). The clock is the adaptive frame step DAT_1F800393: the accumulator gains it each frame and a pixel of climb happens when it reaches the divisor, the remainder dropped. With opdeene's frame-step floor of 3 (written by its own prescript), divisor 4 climbs a pixel every two frames.

Lines draw centred, all glyphs at once, scrolling upward in a clipped window. The single-line balloon (4C E1, centred at Y = 180, 120-frame timer) is a different op and never carries the crawl. Port engine-field::cutscene_narration (CutsceneNarration + RollerSeed::config_op_before).

Per-actor channels

Retail spawns one script context per partition-1 placement at scene entry (FUN_8003A1E4); its script id is partition-0 count + placement index, the id space cross-context ops resolve through. The timeline halt-acquires channels 0x05..0x0F (a sweep of 4C 85), pokes each beat (4C 45 param, 4B animate, 23/A3 move), resumes with B2 <id> 0A, and waits at B3 <id> <bit> until the channel's own script raises its completion flag. Even a halted channel keeps animating, because the per-actor anim tick runs independently of the parked script PC.

Port legaia_engine_field::field_channels: one FieldChannel per placement, stepped a slice per tick; pokes run against the resolved context; a failing cross-context flag test parks the timeline (bounded by CHANNEL_WAIT_PARK_TIMEOUT, falling back to a step-past). The player channel 0xF8 is modelled directly: ExecMove arms a countdown and the halt-acquire parks until it drains, so door records flow on to their scene change. Idle clips come from the scene's ANM bundle, whose descriptor-count seed is not uniform - the prologue scenes resolve only at count ≥ 5, so the render searches [3, 5, 6, 7].

Engine execution rules

  • Only cutscene-class records (the opening chain, gated walk-on beats) install as the modal timeline with camera seize and locomotion lock; an ordinary mid-play op-0x44 spawn becomes a concurrent helper context that leaves the camera alone but still refuses the pad until it ends - retail's script runner raises the player's engaged bit for every context it steps.
  • Narration blocks are data, so the stepper installs the pages and advances the PC past the block; it holds only for a scene's last crawl (so the scene change waits) and when a roller is still scrolling.
  • Camera beats merge into a persistent parameter set - one opdeene beat sets only H - cleared on scene entry.
  • Completion is the choreography wrap: a backward jump onto an already-executed PC. Real waits (0x4A, flag handshakes, the 0x49 name-entry suspend) halt at their own PC.
  • A byte & 0x7F < 0x20 at the PC is a dialog transition, not an opcode; an 0x80-bit op whose target matches no spawned channel is skipped by width; resolved-channel busy-waits fall through because engine pokes complete synchronously.
  • One engine tick is one vsync, and record contexts are stepped on every one; the frame delta they see is the game frame's length on a game-frame vsync and zero between (pacing).

Pacing: every wait is a count of display frames

Two clocks pace a record, and both count through the same factor. Op 0x4A (wait frames) adds the adaptive frame-skip byte DAT_1F800393 to a per-context sum on each visit and passes on the first visit whose sum reaches the operand; the camera mover adds the same byte to its progress. That byte is the number of display frames one logic tick spans, so every authored duration is a duration in 60 Hz frames, to within one game frame.

That last game frame is measurable. The op credits the frame that reaches it as well, so a wait of N at step s holds its record for s × (ceil(N / s) − 1) vsyncs:

StepOperand → vsyncs held
26 → 4, 30 → 28, 45 → 44, 90 → 88
35 → 3, 10 → 9, 30 → 27, 45 → 42
45 → 4, 15 → 12, 50 → 48, 430 → 428
  • Measured. Every row is read off a per-vsync capture of the zero-input opening; all 59 waits the chain holds at one step from end to end fit the formula.
  • The step is the adaptive one. opdeene runs at 3 because its prescript raises the floor; opstati and opurud sit on a floor of 2 and step by 2, 3 or 4 as their frames cost. No field scene steps by 1 - that floor belongs to name entry, the menus and battle.
  • Why crediting one per vsync is wrong. It holds a wait for N − 1 ticks, up to two vsyncs long per wait at step 3, which over the opening record's waits puts it some thirty vsyncs behind retail by its last vignette.
  • End-latch spins are on the grid too. The anim tick runs once a game frame and multiplies each clip's step by the frame step, so the engine advances every placed-prop, NPC and cross-context clip cursor on game frames only. The actor ticks after the record, so the record sees a clip's end latch on the next game frame and every spin ends on one.
  • The residual is one-sided. A retail leg, measured scene label to scene label, includes the load and mode-transition window before the record's first tick; the engine's scene loads are instant, so it lands short by that window. Running long is the regression signal.
Two ops that do not do what their dump suggests
  • 4C 49 selects a write variant on two bits of _DAT_1F800394 (bit 25 delta, bit 24 player-relative +0x4A = value + anchor[+0x16], else default), always advancing 6 bytes. The field-overlay dump's absolute-jump arm never applies on the opening path.
  • 4C 9F is a retire sweep, not a callback registration: it sets the kill bit on every live actor whose per-frame handler matches, then advances the script two bytes. It is inert during the opening and fires zero times under a live probe.
  • FUN_8003CF04 is a list finder over 0x8007C34C, not a kill function.

Fades, effect colour and the gold grade

OpBytesTargetEffect
4C 12[r][g][b][ramp u16]Multiply tint DAT_8007BCB8..BA, neutral 0x80; ramp jobs via FUN_8003C5F0Screen fade over the 3D scene; persists across scene changes; text unaffected
34 sub-0[op0][r][g][b][ramp u16]Spawns a colour tween on the effect actor at _DAT_8007B62C; op0 itself carries the blend (&1 ? 2 : 1) and the push kind (8 on &2, 0 on &4, else 2)Walks the effect colour out and back in as a pair of tweens; not a screen fade - the tableau stays lit through it. An all-zero operand clears the effect outright (the arm drops the actor pointer and never spawns), and a pure-white target under blend 2 loses an eighth of its duration

Every scene's entry script arms 4C 12 00 00 00 00 00 then 4C 12 80 80 80 44 00 - instant black, 68-frame ramp to neutral. New Game sets system flag 0x52F to arm that handshake, and the engine pre-runs the entry script's load-frame slice so black is on screen before the first render. Port: fade::SceneTintRamp in World::presentation.tint; the effect beat is a pool colour tween read back as World::screen_tint_pushes, which is the one model - a live capture of a retail beat reads a three-argument push per frame, the push's shape exactly, while the global multiply tint stays neutral throughout. The three arguments are an ordering-table bucket, a semi-transparency equation and a GP0 colour word (red in the low byte); both hosts draw the frame's pushes through one shared emitter.

The gold grade on opdeene / opstati / opurud is a palette-space law applied to the loaded assets: the CLUT rects (0, 473, 320, 2) and (0, 476, 320, 34) are rewritten entry for entry from the disc value to (L, (L·247) >> 8, L >> 1) with L = max(r, g, b), STP bit preserved. The writer is four mode-3 CLUT-cell cyclers, records 10..13 of each prologue scene's effect-script bundle: two capture the rects and re-upload them grey (op 0x2B saturation -0x400), two capture the grey and re-upload it at hue +0x38 / saturation +0x80, every frame through FUN_80019D50. Packet colours split by source - the ground kernel's runtime neutral 0x80 stays neutral and draws gold purely through its collapsed CLUT, while each resident TMD's authored colour words are rewritten by the scripts' own two 4C E6 HSV ops (saturation -0x100, then hue +0x38 / saturation +0x90 / value -0x1E) to (V, V·246 >> 8, V·112 >> 8) with V = min(max(r,g,b), 0xF8) − 30 - a retail opdeene state holds every resident word on that curve. No depth cue runs: IR0 is 0 on every render node throughout the opening.

The port applies the same laws in its mesh shaders (palette_law_word / prologue_sepia_word, CPU mirrors in lockstep tests) because a 4/8bpp texel is a palette entry; exact-neutral words stay neutral, and the screen tint rides the same uniform. The ground matches the capture at G/R 0.890, B/R 0.46..0.48. The far spires are the exception that is not a palette law: they are light-source rows, which the 4C E6 rewrite never touches, and retail shades them through opdeene's dim scene back colour 0x202020 (B/R ≈ 0.15 in the capture).

History: the depth-cue and tint models

The grade was first modelled as a gold far colour with a per-node depth-cue pull, then as a render-time pixel multiply; the CLUT peek falsified the first (IR0 = 0 everywhere) and the ground's green cast (G/R ≈ 1.07) the second. A signature scan across PROT 0970, 0897 and the executable found no CLUT-rewrite loop because there is none: the rewrite is two passes of the general HSV re-tint, data-driven by the effect scripts. The law was first read as (L, L−1, L/2), which holds below luminance 29. The op-0x34 ramp was read as a between-beat black fade and, before that, as a white flash with a 50% wash; the cold-boot capture holds the lit tableau across the span.

Battle intro and script helpers

PieceWhereWhat it does
Style selectFUN_801CE8CC (PROT 0979)Reads battle flags DAT_8007BD60 bit 0x80, first monster id DAT_8007BD0C, scene index DAT_80084540; default style 2, style 3 for three formations, 4 for one; two delay-slot stores land on both branch arms, which is why the flags-set path defaults to 1
Transition tickFUN_801CF5BCOwns the whole frame for DAT_801D2458 frames, then hands off to the battle scene
Emittersrender trackThe fade's second argument is a blend mode, not a depth; style 2's emitter is not a GTE emitter. Port render-kernels::battle_intro
FUN_801D27E0dialogue overlayParty-leader swap state machine (6 states on actor +0x54); writes DAT_80084597 and spawns the talk's own record on every swap
FUN_801D5C08dialogue overlayPosition tween: +0x9C += (+0x9E) × frame-skip, lerp +0x14 to +0x24, done bit 8 at t ≥ 0x1000
FUN_801D5D60dialogue overlayScripted-element teardown: restore the camera, clear enable flags once done
FUN_801D6058dialogue overlayAmbient particle emitter gated on _DAT_8007B854: single jittered spawns or 24 random bursts
FUN_801D5E20dialogue overlayRotates a mesh's own colour words

The full per-style emitter decode is in docs/subsystems/cutscene.md.

How we know

The movie path

FunctionAddressWhat it provesSource
Master dispatchFUN_801CEA3C32-byte slot stride (sll v0,v0,0x5), decoder select, return-scene hand-off, intro-only skipPROT 0970 disassembly, byte-identical to the playback RAM capture
Play loopFUN_801CF098Seek math, SPU CD mix, per-frame pump, teardownPROT 0970
St demuxFUN_8005ECD4 / FUN_8005F024Ring slots, magic gate, end-frame latchSCUS_942.54
Iki decoderFUN_801D0378 / FUN_801D0604LZSS DC table + AC scan; STRv2 path FUN_801D070C is dev-onlyPROT 0970
FMV trigger op0x801E30E4 in FUN_801DE840Writes _DAT_8007BA78 and _DAT_8007B83C = 0x1A; a label, not a callableField overlay 0897 dump; JT chain 0x801CEE60 → 0x801CF010
Title attract triggerFUN_801DD35C (0x801DDCF0)Idle countdown underflow writes mode 26 with id 0PCSX-Redux watchpoint - boot
Per-scene triggersman_field_scripts::scene_fmv_triggersOne trigger op per scene MAN, the table on the overviewDisc walk of every scene MAN
XA clip starterFUN_8003D53C / FUN_8003D764CdlSetfilter {1, chan} for clips; no filter for moviesSCUS_942.54
Clip table fillerFUN_801CFA7834 [CdlLOC][len] slots at 0x801C6ED8 from \XA\XA%d.XA;1PROT 0895 init.pak
Per-movie single XA track-(file 1, chan 0) on all six moviesRaw-sector subheader scan

The opening and its helpers

ClaimEvidence
Ring / demux state machineFUN_8005BBF8, FUN_8005EDC4, FUN_8005EB68, FUN_8005ECD4, FUN_8005F024, FUN_8005EF40, FUN_8005EE4C (SCUS_942.54 St library)
MDEC feed boundaryFUN_801CFD84, FUN_801CFFDC, FUN_801CF56C, FUN_801D0100, FUN_801D0198, FUN_801D0248, FUN_801CFEE0 - carry the MDEC_in_sync / MDEC_rest:bad option strings
Record header + gatesFUN_8003BDE0; decoder man_field_scripts::partition_record_span
Spawn routesOp 0x44 at FUN_801DE840 (ra 0x801DF098); tile trigger FUN_801D1EC4 → FUN_801D5630 → FUN_8003BDE0 (ra 0x801D218C); exec breakpoint = 5 hits across the opening
Cross-context 0xF8FUN_8003C83C(0xF8) → _DAT_8007C364
CameraFUN_801DE084, FUN_801DD310 (mover), FUN_801DC0BC (per-frame), FUN_800172C0 (view), FUN_80026988 (rotation, LUT 0x80070A2C; FUN_8001CF50 is the per-node camera-relative variant), base matrix DAT_8007BF10; live capture 2471/2480 axis values
RollerFUN_80037174, template DAT_801F28A0, allocator FUN_80020DE0, page measure FUN_8003CA38; per-scene geometry from pixel capture
Caption TIMPROT 0749 at LZS offset 0x01EC30; zero UI text/blit draws in the caption window; string absent from a full RAM dump
ChannelsFUN_8003A1E4 per placement from FUN_8003AEB0; anim tick FUN_8003BC08 → FUN_80021DF4 (13 channels on opdeene)
Fade opsFUN_8003C5F0; op-0x34 arm at 0x801E1FB0
Gold gradeVRAM CLUT peek vs disc TIMs, zero mismatches; render-node walk IR0 = 0; engine ground pixel-matched
Battle introFUN_801CE8CC, FUN_801CF5BC, DAT_801D2458 (PROT 0979)
The per-STR trigger capture corpus

Nine save states, one per fmv_id 0..=8, captured immediately before each movie starts, pin the trigger-side state: _DAT_8007BA78 = the expected id, _DAT_8007B83C = 0x1A, BGM id 2000, active scene map01. The states were produced through the dev menu's register editor (FUN_801DBD04 family), not by stepping a scene's trigger op, so they prove the id/mode tuple across the whole range but not the per-scene assignment - that comes from the MAN walk above. There is no per-movie event record carrying post-play flags; the dev menu's “jump to beat” is the MAP CHANGE warp appliers plus the event-flag editor.

History: superseded readings
  • The dispatch table was read with a 64-byte stride and paired the wrong slot halves; the shift instruction pins 32 bytes. With the right stride the last three scenes play MV4/MV5/MV6 rather than “vestigial triggers at cut movies”.
  • A “compact MV table” at 0x801CAE40 was the libcd CdlFILE directory cache read 8 bytes out of phase.
  • The confirm-press flag-26 packet was read as the required hand-off into town01; it is the skip, and the natural chain arrives through the tile trigger. Op 0x44 was labelled COUNTER before the probe showed it spawning a record.
  • Reading the GTE matrix from a save state does not recover the cutscene camera - it is the last-rendered object's composed transform (row norms ≈ 6). The camera comes from the op-0x45 globals.

More on do-not-re-walk.

See also