Observe the running original
Skill
re-observe. Run the original game where it can be watched, driven and captured - GameFrame's emulators, DOSBox-X, 86Box, Wine, a Windows machine or a streamed PC, console emulators - and capture frames, state, inputs, calls, file reads and sound as measured evidence. Also list every input from outside, inventory every interface screen, and build a queryable object model of the game with entry points for tests and agents. Use after inventory, before porting a system whose behaviour you need to see, and whenever a question can be settled by running the original.
Copy this prompt into your coding agent. Replace the part in angle brackets.
In my GameFrame project, use the re-observe skill for <the task>.
Read the skill first and follow its steps in order.
Report the evidence it asks for, with its label: observed, measured, inferred or unknown.Static reading says what the code could do; a run of the original says what it does. This skill gets the original running somewhere you can watch, drive and capture it, and turns what you see into evidence labelled measured. Details and tables are in docs/observe/.
1. Know the kind of game
Read docs/games/README.md and the family page for your game. Write the answers in docs/FINDINGS.md: who drives (a stream, a simulation, an environment, turns), the time model, how exact the interface must be. They decide what to capture.
2. Choose where to run it
| The game | Run it in | For |
|---|---|---|
| One function of a Windows x64 binary | tools/emu/pe_emu.py | exact results in tests (re-emulate) |
| One routine of a DOS program | tools/emu/dos_emu | the same, 16-bit |
| A whole DOS program, headless | tools/emu/dos_env | frames, memory and demos in scripts; keys from env.keys |
| A DOS program with a window | DOSBox-X (debugger, file log), scripted with tools/dos/dbx_control.py | breakpoints, memory, fileio_trace.py |
| A DOS or Windows 3.x/9x game that depends on its PC hardware | 86Box, with a recorded machine profile | the configured CPU, BIOS, chipset, buses, sound cards and resource assignments (docs/observe/86box.md) |
| A Windows game | Wine (WINEDEBUG=+relay), a Windows machine or VM, or a PC streamed to you | API calls, screenshots, debuggers, RenderDoc |
| A console or arcade game | an emulator with a debugger (Mesen, bsnes, Dolphin, PCSX2, MAME) | memory, input movies, frame advance |
Prefer headless, scripted runs for evidence. Use windowed runs to see what happens and where to look. docs/observe/running-the-original.md has the full table.
Use DOSBox-X for fast DOS runs that need its debugger or file log. Use 86Box when the question depends on a particular PC, BIOS, expansion bus, guest Windows version or card resource setting. Record the 86Box release, machine profile, guest OS, card model, I/O ports, IRQ, DMA and driver version with each capture. Treat the result as an observation of that 86Box build and profile, not as physical-hardware evidence. Keep 86Box source out of a source-blind implementation task; pass the behavior description and its setup conditions to the implementation stage.
Before the first run, list what the game needs and the modules that provide it (docs/harness/README.md):
./gameframe run needs game/ # cpu, os, api, lib, drm, media: each with its evidence
./gameframe run harness plan game/ --script derived/harness.sh # a runtime, the layers, the gapsRead the script, set its variables, then run it. A need marked NO MODULE is a gap. When a change around the game makes it run (a client, a decoder, a setting), write that change as a module from docs/harness/modules/_template.md: in the project, or in GameFrame when any game with that need could use it. Check modules with ./gameframe run harness check.
To record a whole run as the reference for a port, read docs/observe/recording.md: tools/live/memrec.py records named values from the game's memory, memlog.py reads them back, and the replay checks every sample with a window (tools/verify/replay.py, re-verify). In a 3D game, tools/triage/axes.py reads such a recording over the level's vertices and names the up axis, its sign and the handedness, before any viewer or editor relies on them.
Before the first run, ask what is known about the game's era and the system you run it on: ./gameframe advise windows-9x directdraw wine or ./gameframe advise dos dosbox. Old games assumed hardware that modern systems refuse (port I/O, 16-bit desktops, short mode lists, a CD in the drive). The context entries list the checks to make first, such as ./gameframe run insns GAME.EXE '^(in|out|cli|sti)' for privileged instructions. When a run fails, pass the error text or the log's tail to ./gameframe advise -.
In an emulator, start every run from a save state, record the inputs as a movie, and have a script dump the game's memory after each frame. Those dumps are ground truth for tools/verify/ramdiff.py. Search Data Crystal, The Cutting Room Floor and TASVideos for RAM maps and mechanics before measuring (docs/observe/emulators.md).
3. Capture, with the inputs that made it
For every capture, write next to it the game version (hash), settings, seed, and the inputs or demo, so it can be made again. Keep captures in ignored folders (derived/): they are game material.
| Capture | How |
|---|---|
| Frames | video memory in the emulator; screenshots; screencapture -l of a window |
| State | memory dumps at chosen points; the game's own saves; tools/triage/memscan.py finds a value's address across dumps, tools/triage/pointerpaths.py a chain of pointers that finds it in every run, tools/formats/markers.py where a file's fields land (docs/observe/memory.md) |
| Frames | tools/formats/rawimage.py turns a dump of video memory into a PNG in the original's mode and palette |
| Inputs | the game's demo or replay files; breakpoints on input reads |
| Driving a game under Wine | tools/live/winput.py run --prefix PREFIX window TITLE key enter ... sends keys as scancodes, mouse moves and clicks from inside Wine. It needs no macOS permission |
| Where to click | tools/live/align.py find REF.png NOW.png --point X,Y --check out.png lines up a known picture of the screen (a frame from the port, an earlier capture) with a capture of the window now, frame and scale included, and gives the click point in the capture. Look at out.png before clicking. When the screen changed too much (score below 0.5), tools/llm/locate.py NOW.png --find "the Exit button" asks a selected vision model, local or paid |
| Calls and random draws | tools/emu/call_trace.py; debugger traces |
| File reads | tools/dos/fileio_trace.py on DOSBox-X logs; Process Monitor; Wine's WINEDEBUG=+file (docs/observe/system-calls.md) |
| Calls to the system | Wine's relay log, strace, a proxy library (docs/observe/system-calls.md) |
| What it draws | apitrace or RenderDoc captures of the draw calls; dgVoodoo 2 for older APIs (docs/observe/drawing.md) |
| Network messages | Wireshark captures, then tools/formats/pcap.py and tools/formats/fields.py (docs/observe/network.md) |
| The code for one action | coverage of two runs, with and without the action, compared by tools/triage/coverdiff.py (docs/observe/debuggers.md) |
| Values at a moment | a debugger or an emulator's GDB stub, with memory breakpoints (docs/observe/debuggers.md) |
| Sound | the chip's register writes; WAV recordings |
4. List every input
Fill a table in docs/FINDINGS.md of every input from outside, with the address of each read: controls, clocks and timers, random seeds, configuration and files, the command line and environment, hardware detection, window messages, network. Search for them with the signatures in docs/observe/inputs.md (interrupts, ports, API imports). Then design the port's input record: one per frame or per command, the only way the rules receive anything from outside.
5. Inventory the interface
List every screen: how to reach it, the code or layout that draws it, its states, inputs, what it changes, its text and font, and its captures. Find screens by playing, by following the code from the main menu, and by listing layout and string data. Capture each state with the inputs that reached it. docs/observe/ui.md has the fields and how to rebuild data-driven and coded interfaces.
6. Build the object model and its entry points
Give the original and the port the same model, named with the original's names: the interface tree, the world's entities and fields, and the session (loop, frame, pause, seed). Add entry points as the work needs them: query, input (as the player), commands, time (step, run n), state in and out, trace, capture. Entry points only read or act as a player would; tests, scripts and agents use nothing else. docs/observe/object-model.md has the rules and examples from three ports.
Evidence to produce
docs/FINDINGS.md: the kind of game, the inputs table, the screen inventory.- Capture scripts in the project (not one-off commands), so every capture can be repeated.
- A capture script prints its usage for
--helpor no arguments and runs nothing. It writes into a new folder or refuses to overwrite one, so checking its usage never truncates a capture that took a quarter of an hour. - Labels (
labels/) for every variable and function the captures name.
Pitfalls
- A capture without its inputs cannot be repeated or trusted. Record the inputs first.
- A compressed video stream is not pixel-exact: compare layout. Use blurred image differences with a threshold.
- The clock and the random seed are inputs too. A second run that differs usually read one.
- A live online service belongs to someone else. Its terms usually forbid testing against it. Breaking copy protection is restricted in many countries. If the user's plan touches either, say so once with the source (
docs/LEGAL.md,docs/preservation/law/), then follow the user's decision.
Agents and tools
Agents that preload this skill: re-observer, re-listener, re-cataloguer.
Tools this skill names:
- dbx_control: Drive DOSBox-X's built-in debugger from a script, through its debugger control channel.
- fileio_trace: Turn a DOSBox-X file log into a per-file map of reads: which file, at which offset, how many bytes, in what order.
- axes: Find which axis is "up" in a 3D game's coordinates, its sign, and whether they are mirrored.
- coverdiff: Find the code that runs only when you do something in the game: compare the code coverage of two runs, one without the action and one with it.
- memscan: Find where a running game keeps a value, from memory dumps taken while you watch the value change: the gold, a unit's health, the turn counter, the random seed.
- pointerpaths: Find a way to the address of a value that moves: a chain of pointers from a fixed address (a global variable of the program) through the game's objects to the value.
- call_trace: Record the calls an emulated function makes to chosen functions, in order, with call sites.
- dos_emu: Call functions inside a 16-bit DOS or Windows 3.x program (NE or MZ) under Unicorn.
- dos_env: A small DOS, BIOS and PC for tools/emu/dos_emu/, so a whole 16-bit program can run headless.
- pe_emu: Call functions inside a Windows x64 game's EXE and DLLs under Unicorn, without running the game.
- fields: Find the fields in many messages or records of one kind: which byte offsets are constant, which count up, which hold the length, which pick the kind of message, and which vary.
- markers: Mark the fields of a data file with unique values, then find where the game puts each one in memory.
- pcap: Read a network capture (.pcap or .pcapng, from Wireshark or tcpdump) and split it into the conversations of a game: who talks to whom, on which ports, and the bytes of each message.
- rawimage: Turn raw pixel data into a PNG: a dump of video memory, a screen saved by a DOS game, or a block of a data file that may hold a picture.
- ramdiff: Compare the game's memory frame by frame, from the original and from the port, and report the first frame and the first addresses that differ.
- replay: Check a port against a recording of the original that was read from outside, sample by sample.
- align: Line up a reference picture of a screen with a capture of the screen now: offset and scale.
- memlog: Read a recording made by memrec.py: a summary, a CSV table, or one sample by time.
- memrec: Read a running game's memory from outside: snapshots to diff, and recordings of named values.
- winput: Build and run winput, which sends keys, mouse moves and clicks to a game from inside Wine.
- locate: Find a screen element, or a reference screen, in a capture with a vision model.