The shape of it

Capture moments of the original as emulator save states. For each one, read what the original was doing out of the state's own memory, put the reimplementation into the same situation, and score what it does against what the original did, channel by channel. Commit the scores as a baseline that may only rise. Around that loop sit the practices that keep it honest: an evidence grade on every claim, a register of disproved readings, a coverage denominator taken from the original's data, and runs that replay bit for bit.

Retail save statesnamed by their bytes Memoryobservables Video memorydisplayed frame Seed the engineits own load path Score channelseach in 0..1 Baseline that only risesplus a loosening ledger Report, worst state first

The parts that read emulator states and disc images are reusable code: reusing for other PS1 games says which crates carry, and runs them on a made-up game.

Capture retail states

A save state is the unit of evidence: the whole machine at one instant, main RAM, video memory and display registers, sound RAM, scratchpad. Every channel is read out of it offline, so no emulator runs during a measurement and one capture serves every later change.

  • Name each state by its bytes. Keep an immutable copy under the hash of its contents and a manifest mapping a label to the fingerprint, the scene, how it was made and anything done to it (capture tiers and the state library).
  • Record what was done to the machine. A state made after a debugger poke or on a patched disc is not the original's behaviour; name it and keep it out of the headline. A state replays the memory of the disc that booted it, and an emulator may apply a patch file beside the image without asking (patched-disc taint). Read the resident executable out of the state rather than trusting a file name.
  • Capture by script where you can. A closed-loop probe that loads a state, drives the pad, waits for a condition and saves can re-shoot a capture when a build changes. A derived state inherits everything its source carried.
  • Choose what to capture from a worklist built from the game's own data (see the denominator below), not from what was easy to reach.

Seed the engine from retail RAM

A capture is an oracle only if the reimplementation can be put into the same situation. Read the scene, position, party, story flags, camera and fight state from the original's memory; enter that situation through the engine's own code; tick until it settles.

Retail stateits own memory The situation scene, position, party, flags entered through the engine's load path History camera region, RNG stream, paused script installed, and named in the report Left to the engine ticks until it settles, then computes whatever the channels score
The more that sits in the amber box, the less the score says. Anything a channel scores has to be computed, or the channel only checks the seeding.
  • Enter through the engine's own load path. Copying memory into engine structures field by field tests the copy. This project loads the lifted save the way a memory-card load does, in the original's order, then places the player (the seeding model).
  • Seed history; compute the rest. Values that are the sum of everything before the capture (the camera region last crossed, a random stream hundreds of draws in, a paused script) are installed from the state. Everything else is left to the engine, because that is what the channel tests. Each installed value is named in the report.
  • Keep unseeded states in the report, with their reason, so the corpus never silently shrinks to the states that work.

Define scored channels

A channel is one observable read from both sides and scored in 0..1, with a stated tolerance and falloff so a near miss counts and noise does not move the number (channels).

  • State channels compare values read from memory: scene, mode, position, camera, music track, flags, combatants. Cheap, headless, precise; a failing one names the subsystem. Each needs its source address documented with evidence (retail observables).
  • The image channel compares the displayed frame, cropped from video memory by the original's own display registers, with the engine's. Render the engine's side the way the console did, at native resolution (PSX-mode rasterisation, the image channel), and score with a block metric that tolerates dithering rather than a mean pixel error.
  • Time-axis oracles judge what one frame cannot: record both sides from one state and pair frames on the game's own state-machine clock, not wall time (cast-sequence oracle).

Read the worst states first. For each, decide whether the divergence is the instrument's (a seeding gap) or the engine's, and catalogue the shapes that recur (divergence shapes).

Ratchet the baseline with a loosening ledger

Commit per-state, per-channel scores. A check fails when a score falls, when a measured channel goes unmeasured, or when a seedable state stops seeding; a rise is folded in by a reviewed bless (the ratchet). The baseline holds scores, never pixels or memory.

scoresuccessive changes rise: reviewed bless fall, no ledger entry: the gate fails fall with a ledger entry: accepted, and the reason is kept
Schematic. A score can fall because a fix is right, when the old score rested on an error, so a drop is allowed only with its reason on record.

Two ways a ratchet is defeated without tripping

The baseline is edited in the same commit: a second gate compares every baseline with the base revision and fails on any value that moved the wrong way unless a ledger records that exact step with a reason. The measurement never runs: a test that skips for want of data reports as a pass, so count skips by reason on the machine that has the data (ratchet integrity).

Run the original's routines beside their ports

A save state answers for the inputs it happened to hold. A rule the original computes - a damage roll, a status tick, a sort, a camera matrix - also has inputs no capture reached, and a reimplementation can be wrong only there. Close that gap by running the original's routine itself: load the executable and its overlay off the user's disc into a user-mode interpreter of the console's CPU and coprocessors, seed memory with generated inputs in the shapes the game reaches, call the routine, and compare every byte of the regions it wrote with what the port's kernel produces from the same inputs. Hook the random-number call so both sides draw one stream, and assert the run reached the arms it claims to test (retail kernel differential).

Three rules make it a gate rather than a test suite:

  • Each test declares the routines it runs, and the run proves it. The interpreter logs every entry it executes with the bytes there; a test fails unless each routine it names was entered and its bytes are the named image's.
  • Coverage is counted from the declarations, so it needs no disc, and it is ratcheted like any other score.
  • Write the coprocessors from the hardware documentation, not from the reimplementation: a model copied from the code under test shares its defects, and a shared defect cancels in the comparison.

A differential is also the strongest provenance evidence there is: a tag claiming a routine is checked by running that routine. What it cannot see is when and with what the engine calls the kernel, which the state corpus and the films measure. Where it sits among the other checks is the verification ladder.

Grade every claim by its evidence

Every answered question carries the grade of its weakest load-bearing evidence: disassembly (instructions at stated addresses), capture (a state, probe log or measured oracle), decompiled-C (a decompiler's rendering, an inferred signature, a typed-in label) or inference (reasoning from surrounding facts or absence). The third marks a claim nobody has checked against instructions, which makes it a re-audit worklist (the evidence column).

Decompilers fail in repeatable ways: arguments left in registers vanish from calls, stores are reordered or dropped, labels inside a large function print as calls, a module imported at the wrong base prints every address shifted (decompiler artifacts). Port from the instructions, and hold a "no writers found" claim to the bar of a positive one (address reference scan).

Keep a falsified-hypotheses register

An undocumented format invites the same wrong readings from every newcomer. Keep each disproved one in three parts: the tempting reading and why it tempts, the decisive instruction, capture or count, and what is true instead. Entries are terminal; new evidence moves a row back to the open questions rather than rewriting its verdict (do not re-walk).

Take the denominator from the disc

Progress counted over work already done always flatters. Count against what the original contains:

Done, over what was started
The same work, over what the disc holds
Schematic. The second bar is the only one that shows what nobody has looked at yet.
  • Code: per executable image, the bytes covered by analysed functions, uncovered runs largest first (disc coverage).
  • Data: per archive entry, the bytes some parser consumes and the shape of the rest (byte accounting).
  • Captures: per scene, enemy, spell and formation the data defines, whether a corpus state shows it (capture coverage).

Negative results take the same discipline: a search over raw bytes, in every encoding an address can take, is what turns "no caller found" into "no reference exists".

Make runs replayable

Make the engine deterministic under a recorded input stream: a text file of per-frame pad transitions plus the seed replays to a bit-identical state trace. A test that runs one stream twice, with companions showing that another stream and another seed each change the trace, keeps determinism from regressing without any data from the original (determinism gate). Then every bug report is a replay file and every fuzzing finding reduces to the inputs that matter (soak harness).

Where to start

  1. A state reader for your emulator and a manifest naming states by their bytes.
  2. One state channel (scene and position) over a handful of states, reported worst first.
  3. The ratchet, its loosening ledger and the skip tally, before regressions have room to hide.
  4. The image channel at native resolution.
  5. The routine differential, once the port has rules worth holding to the original's bytes, with its declarations and coverage ratchet from the first test.
  6. Evidence grades and the falsified register: free to start, and they only grow in value.
  7. The disc-derived denominators, once a count over done work starts to flatter.
  8. Determinism replay, as soon as the engine takes input.

See also