At a glance

In the game
Every full-motion video: the attract intro and the eight story movies, plus which field scene the game returns to when each ends.
Magic / marker
None - static initialised data in the cutscene overlay's data section (an overlay is a chunk of code + data loaded into RAM on demand)
Lives in
PROT 0970 (the STR / MDEC overlay) at link base 0x801CE818: path strings at 0x801CE810, return-scene labels at 0x801CE8AC, the dispatch table at 0x801D0A6C (23 × 32 B)
Retail reader
Selector in the master dispatch + the play loop (how we know)
In the port
legaia_game_tables::fmv_dispatch
Confidence
Confirmed - stride and layout pinned from the PROT 0970 disc bytes, byte-identical in the resident RAM capture; every field cross-validated against the play loop's reads; the retail fmv_id 0..=8 range pinned by nine trigger save states.

The dispatch table

The field-VM FMV-trigger op (see the cutscene subsystem) writes an s16 fmv_id to a global; the master dispatch multiplies it by 32 and indexes the table. The play loop opens the slot's file, seeks to start_frame, and streams until the demuxed frame number reaches end_frame - which is how one MVn.STR carries several cutscenes by frame range:

MV3.STR fmv_id 2 1 map01 fmv_id 3 0xE2 chitei2 fmv_id 4 0x1A5 map02 fmv_id 5 0x27C caller scene 0x36A when a segment ends, the dispatch writes its return scene and hands back to the field
One movie file, four cutscenes: the frame ranges abut exactly, and each slot names where play resumes.

Record layout (32 bytes)

FieldMeaning
path_ptrPointer into the path string table at the overlay start
color_flagNon-0 = 24-bit colour (VRAM footprint width × 3/2, MDEC depth bit, DISPENV.isrgb24)
start_frame1-based; the loop seeks (start - 1) * 10 sectors in (the 15 fps cadence)
end_frameLast frame of the segment - the demux stops here
fb_x / fb_yVRAM decode target (0 / 8 on retail slots); the double-buffer sibling rect sits at fb_y + height
width / heightFrame size (320 × 240 retail)

Because the table is static initialised data in PROT 0970, it decodes straight from the disc. The +0x18/+0x1C pair is the one place the record can be overruled: it sizes the decode rects once, then from the first demuxed frame onward the play loop overwrites the rects' width and height from the STR sector header's own fields. A slot whose dimensions disagree with the movie loses.

Every word of the record is a play-loop input

The record has no field reserved for another subsystem and no undecoded margin: FUN_801CF098 reads all eight words itself, each traceable to the instruction that consumes it.

OffsetConsumed atFor
+0x00801cf0d0CdSearchFile on the path string
+0x04801cf100, 801cf27c, 801cf2f4, 801cf478the * 3/2 VRAM scaling, the MDEC depth bit, DISPENV.isrgb24
+0x08801cf1b8, 801cf9e0the (start - 1) * 10 seek, and StSetStream's armed seek
+0x0C801cf788 (in FUN_801CF740)the end-of-stream latch, tested per demuxed frame
+0x10801cf110, 801cf144the decode rect's VRAM x
+0x14801cf168, 801cf180the decode rect's VRAM y
+0x18801cf28cthe DISPENV width
+0x1C801cf168, 801cf2c8the sibling rect at fb_y + height, and the display rect's h = height * 2

The sector-header overwrite of width/height is at FUN_801CF740 (cached in DAT_801D0D50 / DAT_801D0D54); the one-time rect sizing at FUN_801CF8B0.

The nine retail movies (and the dev slots)

fmv_idMovieFramesPost-play hand-off
0\MOV\MV1.STR;11..0x53amenu / memory-card mode (the intro/attract movie)
1\MOV\MV2.STR;11..0xf4scene town0b, spawn word 0x0C
2\MOV\MV3.STR;11..0xe1scene map01, spawn word 0x55
3\MOV\MV3.STR;10xe2..0x1a4scene chitei2, spawn word 0x2C1
4\MOV\MV3.STR;10x1a5..0x27bscene map02, spawn word 0xF4
5\MOV\MV3.STR;10x27c..0x36aback to field mode with no scene-name write - play resumes in the caller scene
6\MOV\MV4.STR;11..0x152scene jou, spawn word 0x276
7\MOV\MV5.STR;11..0x288scene uru2, spawn word 0x1BC
8\MOV\MV6.STR;11..0x297scene town0e, spawn word 0x2E5
9 / 10MV1A.STR / MOV15.STR-dev slots - files not on the retail disc
11..=22\DATA\MOV.STR;11..0x64dev multi-window display-test slots (varying fb_x/fb_y/width)

MV3.STR's four segments abut exactly. The scene + spawn word hand-offs write the next-scene name global (from the label table of seven CDNAME-shape names: town0b, map01, chitei2, map02, jou, uru2, town0e) plus a spawn/door word, then set field mode - so each mid-game FMV returns to a specific scene rather than the trigger scene. Every movie on the retail disc is dispatched. The path strings live in a packed null-terminated table at the overlay start; three of the nine (MOV.STR, MOV15.STR, MV1A.STR) are dev-only.

Which scene fires which movie

The fmv_id each scene fires is inline script data, not a runtime value: the trigger op carries its id as a literal i16 operand, so walking every scene MAN's scripts with the field-VM disassembler recovers the full assignment straight from the disc:

Scene (extraction entry)fmv_idMovie segmentReturns to
0004_town011MV2.STRtown0b
0095_garmel2MV3.STR 1..0xe1map01
0606_deroa, 0706_chitei23MV3.STR 0xe2..0x1a4chitei2
0218_dohaty4MV3.STR 0x1a5..0x27bmap02
taiku (live-confirmed)5MV3.STR 0x27c..0x36acaller scene
0348_town0d6MV4.STRjou
0435_uru7MV5.STRuru2
0689_jouine8MV6.STRtown0e

One trigger op per scene; no other scene MAN carries one. fmv_id 0 (the intro) fires from the title/new-game path rather than a scene script. Slot 5 appears in no scene MAN partition-1 script; a live playthrough capture pins taiku as the scene (it enters the movie mode with id 5 and resumes in taiku itself, exactly per the slot-5 hand-off), while the byte-level carrier of its trigger op sits in taiku's uncompressed scene structures outside partition 1 - the one open detail.

The look-alike table: libcd's directory cache

A second, unrelated table shares the overlay's data section and has been mistaken for the FMV table: libcd's CdSearchFile per-directory file cache (libcd is the CD-ROM library of PsyQ, the PlayStation SDK) at 0x801CAE08 - one 24-byte CdlFILE record per file of the last-searched directory: 4-byte BCD location, 4-byte size, 16-byte name. It starts with the . / .. entries; the contents change with whatever directory was searched last (the XA directory at the title screen, MOV during a movie). It describes the disc filesystem, not the cutscenes.

In the port

legaia_game_tables::fmv_dispatch::FmvTable::from_str_overlay decodes the table from the PROT 0970 entry bytes; entry(fmv_id) gives the movie path and frame range, and engine_path() declines the dev slots whose files are not on the disc. The player builds its decode rects in legaia_mdec::str_player.

How we know

Evidence by claim
SubjectEvidence
Selector: fmv_id * 32 into the tableFUN_801CEA3C, disasm 0x801CEC94..A4 (lh the global, sll v0,v0,0x5, addu onto 0x801D0A6C)
Dispatch-table stride + field layoutPROT 0970 disc bytes at base 0x801CE818 (asset overlay ...); byte-identical in the overlay_str_fmv.bin RAM capture
Play-loop field readsFUN_801CF098; funcs/str0970_801cf098.txt
Return-scene hand-offs (scene label + spawn word + mode)FUN_801CEA3C; funcs/overlay_cutscene_str_0970_801cea3c.txt
Per-scene trigger assignmentField-VM disassembler walk over every scene MAN
fmv_id 0..=8 retail rangeNine save states, one per id, each captured just before playback
Only two writers of the id globalRaw-byte scan of all 1233 PROT entries, every MIPS addressing form and alignment phase: the trigger-op handler and the title-attract tick; the debug menu writes it through a register-pointer editor, invisible to static scans
Table decode from discFmvTable::from_str_overlay against the disc entry
Directory-cache identificationTitle-screen + FMV-overlay RAM captures; PSX-SPX libcd CdlFILE definition

Full path-table offsets and the trigger-corpus save-state details are in the source of record: docs/formats/str-fmv-table.md.

See also