diff options
| author | gbc dev <gbc@localhost> | 2026-07-15 16:53:28 +0200 |
|---|---|---|
| committer | gbc dev <gbc@localhost> | 2026-07-15 16:53:28 +0200 |
| commit | 95fa3f0095d6f383840992cab6833cac92493a60 (patch) | |
| tree | b0ce39e89f15bdffbe38c3f1896750e702644952 /README.md | |
| parent | tools: stitch_overworld.py - DFS EXPLORE's connection graph into one overworl... (diff) | |
| download | sl0pboy-95fa3f0095d6f383840992cab6833cac92493a60.tar.gz sl0pboy-95fa3f0095d6f383840992cab6833cac92493a60.tar.xz sl0pboy-95fa3f0095d6f383840992cab6833cac92493a60.zip | |
control: video frame capture + deterministic input-movie replay
Two socket features for building reproducible showcase clips:
- record start <path> [everyN] / record stop: append the RGB888 framebuffer of
each produced frame to a flat 'GBCV' capture file (downsample with everyN).
tools/gbgif.py turns it into a GIF (or PNG frames). Capture is decoupled from
wall-clock/turbo/frameskip, so timing is always correct.
- input play <path> [reset] / input stop: drive the joypad from a synthesized
TAS-style movie (text: '<frames> [buttons...]' per line), frame-locked so
replay is deterministic. 'reset' does an exact power-on (preserve cart
ROM+SRAM, zero all other state) so a movie replays byte-identically -- verified
by hashing two runs. tools/gbmovie.py is a Python builder for movies.
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 48 |
1 files changed, 48 insertions, 0 deletions
@@ -153,9 +153,57 @@ or decimal; **memory bytes are hex** (so `read` output feeds straight back into | `delete <0xaddr\|#idx\|all>` | remove a breakpoint | | `watch <addr>` | value-change watchpoint on a byte (no arg lists) | | `unwatch <0xaddr\|#idx\|all>`| remove a watchpoint | +| `record start <path> [everyN]` | capture frames to `<path>` (1 of every N); build a GIF with `tools/gbgif.py` | +| `record stop` | finish the capture (replies with the frame count) | +| `record` (`status`) | report whether a capture is active | +| `input play <path> [reset]` | replay a synthesized input movie (frame-locked); `reset` = replay from a clean power-on | +| `input stop` | stop movie playback (releases all buttons) | +| `input` (`status`) | report movie playback state | | `quit` | shut the emulator down | | *(any button tokens)* | same vocabulary as the FIFO (tap/hold/release) | +### Video capture & input replay (demo GIFs) + +Capture the RGB framebuffer to a flat file and turn it into a GIF: + +``` +./gbctl record start /abs/out.gbv 2 # capture 1 of every 2 frames (~30fps) +... +./gbctl record stop +python3 tools/gbgif.py out.gbv out.gif --scale 3 +``` + +Drive the ROM from a **synthesized input movie** instead of live input, for +reproducible demos. A movie is a text file of `<frames> [buttons...]` lines +(`a b start select up down left right`; none/`-` = released); the emulator +applies one line's worth of joypad state per emulated frame, so replay is +deterministic. Build one by hand or with `tools/gbmovie.py`: + +```python +from gbmovie import Movie +m = Movie() +m.wait(340) # boot splash + fastboot -> overworld +m.hold("select", 8) # open the SL0P menu +m.tap("down", repeat=13) # cursor to the last item +m.hold("a", 6); m.wait(600) +m.save("demo.gbmv") +``` + +`input play <path> reset` gives a clean, repeatable power-on (keeps cart +ROM+SRAM, zeroes all other state), so a movie replays byte-identically every +time. For a frame-exact demo GIF, arm both while paused so they start on the +same frame: + +``` +./gbctl pause +./gbctl input play /abs/demo.gbmv reset +./gbctl record start /abs/demo.gbv 2 +./gbctl continue +... # let the movie play +./gbctl record stop +python3 tools/gbgif.py demo.gbv demo.gif --scale 3 --start 172 +``` + When free-running (`continue`) hits a breakpoint or watchpoint, every connected client receives an async line and the machine pauses: |
