Movies, scenarios and the first routes #20

Merged
Gatchan merged 8 commits from routes into main 2026-10-29 12:13:43 +00:00
Owner

Movies, scenarios and the first two routes (brief §8.2-8.3). Fixes #19.

What

  • Movie format v1 (docs/movies.md): text, header with ROM CRC32, version, power-on seed (D2), start point (power_on or sram <crc32>), emulator revision (ff6_debug_emulator_revision, HW_EMULATOR_REVISION = lakesnes-048a0d7+4), frame count, end hashes (picture, WRAM, SRAM, FNV-1a 64); then run-length pad states. No ROM data.
  • ff6cli: --movie replays and checks the end hashes (exit 1 on mismatch, warns on another emulator revision; with --serve, replays first then serves), --record writes the movie of a fixed run or of a drive-server session. Server: per-frame pad history, load restores the history saved with the slot (abandoned branches are dropped from the movie), poke taints the history (movie refused until an earlier slot is loaded), new commands movie <path> and hash.
  • Scenarios: tools/scenario.py plays ff6rewrite-style JSON (wait, hold ... for, until <var> <op> <value> with optional tapping, expect by RAM-map name, validate_ports in shadow with not-run reporting) through the drive server and compiles it to a movie. Example tests/scenarios/title_new_game.json. Drive-server client tools/ff6drive.py.
  • RAM map (+9 vars, evidence from the disassembly, JP addresses from the .dbg files): nmi_handler (bank = running mode), dialog_keypress_state, menu_cursor_index, menu_next_state, battle_menu_input, battle_menu_char, battle_cmd_cursor, battle_commands, battle_hp. The btlgfx menu vars sit $30 lower in JP (STATE_BY_VERSION).
  • Routes (tests/routes/): behaviours.py (mode, confirm dialog, battle policy that moves the command cursor to the preferred command by reading battle_commands/battle_cmd_cursor, sub-list and target confirmation, battle end detection, open the field menu, menu cursor moves, checkpoints with reload and reseeded jitter), run_route.py (record / check / replay; movie comments carry route, scripts SHA-256, ff6cli SHA-256, ROM SHA-256 and the semantic checkpoint).
    • title_to_first_battle_win: power-on -> New Game -> opening -> first Narshe battle won -> settled on the field.
    • field_menu_tour: starts from the first route's movie, opens the main menu, enters Item, Skills, Equip, Relic, Status, Config (first character where asked), checks the menu state of each screen, tries Save (refused: no save point), closes the menu.
  • ctest: rom.movie.* (record/replay/session/poke, fast), rom.scenario.title_new_game (fast), route.<route>.<ver> (run + compare with the committed movie: frames, end hashes, checkpoint) and movie.<route>.<ver> (replay), label slow, skipped without ROM.
  • Side fix: Python tests now call the interpreter by path; as the imported target Python3::Interpreter it was run under wine in mingw builds (tools.gen_state* failed there).

Results

Route US v1.0 JP v1.0
title_to_first_battle_win battle starts frame 8684, won (party HP 63/68/70, monsters 0) frame 10187, field frame 10253 8005 / 9490 / 9556
field_menu_tour menu open 10293, closed 10897 9596 / 10202

Checkpoints (identical US and JP): map 0x13 (Narshe), party HP {Terra 63, Wedge 68, Vicks 70}, event bits ...4100020000000800...010001C0; menu states item 0x08, skills 0x0A, equip 0x36, relic 0x59, status 0x0C, config 0x0E, save 0x05. Final screenshots inspected (party in Narshe after the battle; main menu; equip screen).

Movie end hashes: title_to_first_battle_win US picture DA60C0464121BD2D wram 68B2D0337F251BFB, JP wram 1A7824E724F82571; field_menu_tour US picture 27FCC519A7906E3D wram 14FA1122B7FE966E, JP wram 4215481F7E8DA00D. Replays match on Linux (release) and on the mingw build under wine.

Validation

  • Builds clean: debug (ASan/UBSan), release, clang, mingw.
  • ctest release, all 24 tests (fast + slow), -j2: 100 % passed, 865 s. Routes ~108-122 s each, movie replays the same; rom.ports 326/329 s; rom.movie 18 s; rom.scenario 7 s.
  • ctest debug -LE slow: 13/13 passed (rom.movie 60 s, rom.scenario 73 s).
  • mingw (wine): rom.movie.US, rom.scenario, tools.*, route.field_menu_tour.JP_V10, movie.title_to_first_battle_win.US_V10 passed.
  • tools/ram_map_check.py: 196 vars, 0 errors, 0 warnings, 159 JP listing checks; gen_state.py --check clean.

Notes

  • The movies depend on interpreter timing (D1): a LakeSnes patch must bump HW_EMULATOR_REVISION, then the movies are regenerated with run_route.py --record.
  • Speed is about 90 frames/s in release, so each route test costs ~2 min.

🤖 Generated with Claude Code

Movies, scenarios and the first two routes (brief §8.2-8.3). Fixes #19. ## What - **Movie format v1** (`docs/movies.md`): text, header with ROM CRC32, version, power-on seed (D2), start point (`power_on` or `sram <crc32>`), emulator revision (`ff6_debug_emulator_revision`, `HW_EMULATOR_REVISION` = `lakesnes-048a0d7+4`), frame count, end hashes (picture, WRAM, SRAM, FNV-1a 64); then run-length pad states. No ROM data. - **ff6cli**: `--movie` replays and checks the end hashes (exit 1 on mismatch, warns on another emulator revision; with `--serve`, replays first then serves), `--record` writes the movie of a fixed run or of a drive-server session. Server: per-frame pad history, `load` restores the history saved with the slot (abandoned branches are dropped from the movie), `poke` taints the history (movie refused until an earlier slot is loaded), new commands `movie <path>` and `hash`. - **Scenarios**: `tools/scenario.py` plays ff6rewrite-style JSON (`wait`, `hold ... for`, `until <var> <op> <value>` with optional tapping, `expect` by RAM-map name, `validate_ports` in shadow with `not-run` reporting) through the drive server and compiles it to a movie. Example `tests/scenarios/title_new_game.json`. Drive-server client `tools/ff6drive.py`. - **RAM map** (+9 vars, evidence from the disassembly, JP addresses from the .dbg files): `nmi_handler` (bank = running mode), `dialog_keypress_state`, `menu_cursor_index`, `menu_next_state`, `battle_menu_input`, `battle_menu_char`, `battle_cmd_cursor`, `battle_commands`, `battle_hp`. The btlgfx menu vars sit $30 lower in JP (`STATE_BY_VERSION`). - **Routes** (`tests/routes/`): `behaviours.py` (mode, confirm dialog, battle policy that moves the command cursor to the preferred command by reading `battle_commands`/`battle_cmd_cursor`, sub-list and target confirmation, battle end detection, open the field menu, menu cursor moves, checkpoints with reload and reseeded jitter), `run_route.py` (record / check / replay; movie comments carry route, scripts SHA-256, ff6cli SHA-256, ROM SHA-256 and the semantic checkpoint). - `title_to_first_battle_win`: power-on -> New Game -> opening -> first Narshe battle won -> settled on the field. - `field_menu_tour`: starts from the first route's movie, opens the main menu, enters Item, Skills, Equip, Relic, Status, Config (first character where asked), checks the menu state of each screen, tries Save (refused: no save point), closes the menu. - **ctest**: `rom.movie.*` (record/replay/session/poke, fast), `rom.scenario.title_new_game` (fast), `route.<route>.<ver>` (run + compare with the committed movie: frames, end hashes, checkpoint) and `movie.<route>.<ver>` (replay), label `slow`, skipped without ROM. - Side fix: Python tests now call the interpreter by path; as the imported target `Python3::Interpreter` it was run under wine in mingw builds (`tools.gen_state*` failed there). ## Results | Route | US v1.0 | JP v1.0 | |---|---|---| | title_to_first_battle_win | battle starts frame 8684, won (party HP 63/68/70, monsters 0) frame 10187, field frame 10253 | 8005 / 9490 / 9556 | | field_menu_tour | menu open 10293, closed 10897 | 9596 / 10202 | Checkpoints (identical US and JP): map 0x13 (Narshe), party HP {Terra 63, Wedge 68, Vicks 70}, event bits `...4100020000000800...010001C0`; menu states item 0x08, skills 0x0A, equip 0x36, relic 0x59, status 0x0C, config 0x0E, save 0x05. Final screenshots inspected (party in Narshe after the battle; main menu; equip screen). Movie end hashes: title_to_first_battle_win US picture DA60C0464121BD2D wram 68B2D0337F251BFB, JP wram 1A7824E724F82571; field_menu_tour US picture 27FCC519A7906E3D wram 14FA1122B7FE966E, JP wram 4215481F7E8DA00D. Replays match on Linux (release) and on the mingw build under wine. ## Validation - Builds clean: debug (ASan/UBSan), release, clang, mingw. - `ctest` release, all 24 tests (fast + slow), -j2: 100 % passed, 865 s. Routes ~108-122 s each, movie replays the same; rom.ports 326/329 s; rom.movie 18 s; rom.scenario 7 s. - `ctest` debug `-LE slow`: 13/13 passed (rom.movie 60 s, rom.scenario 73 s). - mingw (wine): rom.movie.US, rom.scenario, tools.*, route.field_menu_tour.JP_V10, movie.title_to_first_battle_win.US_V10 passed. - `tools/ram_map_check.py`: 196 vars, 0 errors, 0 warnings, 159 JP listing checks; `gen_state.py --check` clean. ## Notes - The movies depend on interpreter timing (D1): a LakeSnes patch must bump `HW_EMULATOR_REVISION`, then the movies are regenerated with `run_route.py --record`. - Speed is about 90 frames/s in release, so each route test costs ~2 min. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
title_to_first_battle_win: battle won, field map 0x13 at frame 10253 US / 9556 JP.
field_menu_tour: 7 main-menu entries visited, closed at frame 10897 US / 10202 JP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Gatchan/FF6Portage!20
No description provided.