At a glance

GrainInstrumentRetail's sideScores
One routineRetail kernel differentialThe routine's own bytes, run off the disc in an R3000 interpreterEvery byte the routine wrote, against the Rust kernel
One instantRetail comparison corpusA library save state's RAM and VRAM, read offlineChannels in 0..1: scene, camera, party, flags, battle, the frame
A stretch of timeCast-sequence oracle, walk filmAn emulator recording from a state, one row a vsyncFrame by frame, paired on the game's own clock
SoundAudio oracles, real PCMlibsnd and SPU state from RAM; the emulator's own audio outputVoice registers, keyed notes, waveform correlation and level

Below them sit the checks that say what the bytes are and which routine a port claims to be; the verification ladder stacks all of them in one order. Around them sit the checks that are not about retail's numbers but about keeping the port honest as a program: the full-game ladder (New Game to credits on pad input alone), the soak harness (seeded random play looking for panics and softlocks), host drift and the browser frame hash (the window and the page running one engine the same way), and record / replay (the same input twice gives the same bytes).

Where each one takes retail from

source retail's side engine's side scored The user's discSCUS + overlays Routine in R3000interpreter, seededinputs, rand() hooked Rust kernelsame inputs Written regionsbyte for byte Library save stateretail disc onlyRAM · VRAM · SPU Read offlineRAM observables,displayed VRAM crop Seed + settlePSX mode, native320 x 240, --retail Corpus channelsone instant PCSX-Redux runcast injected or padschedule, every vsync Seeded play-windowsame input,every tick Film pairsaction state /module phase Mednafen runits own audio outputrecorded as WAV SPU stood wherethe state's stands;track, cues, cast Waveformscorrelation, level,residual
Only the kernel differential needs no save state, and only the corpus runs no emulator: a state's memory already holds every observable it scores. The film and PCM oracles resume a state in an emulator because they need what happens after its instant. Every recording is Sony-derived and stays under the gitignored captures/; only scores are committed.

One routine: the kernel differential

crates/retail-exec is a user-mode R3000 interpreter. A disc-gated test loads the executable and the overlay a routine lives in off the user's disc, seeds RAM with generated inputs in the shapes the game reaches (caps and empty cases included), runs the routine, and compares whole regions of what it wrote with what the Rust kernel produces. rand() is hooked so both sides draw one stream, and the run asserts it reached the arms it claims to test.

Nothing about a save state or a screenshot is involved, so it is the sharpest of the four: a write the port never knew about shows up as a byte. What it cannot see is whether the port calls the kernel at the right moment - a matching routine wired a frame late passes here and fails in a film. Its findings include a level-up applier that never drew retail's spread, field Magic heals that restored a catalog placeholder instead of the applier's level-scaled amount, and an SFX drainer that let a sustained run keep sounding.

One instant: the comparison corpus

For every retail save state in the library the engine can be put into, the corpus seeds the engine into the same situation through its own load path, lets it settle, and scores channels in 0..1 against what the state's RAM says: scene, mode, position, camera, facing, placements, BGM track word, visible-tile window, party, story flags, bag, and in a battle the enemies, HP and the action phase. The image channel renders the frame the way the GPU does - --retail --psx-render on the native 320 x 240 target - and scores the fraction of 8 x 8 blocks within tolerance of the displayed VRAM crop.

Scores are ratcheted: a committed baseline holds each state's channels, and a run fails when any channel falls below it. A rise is folded in by a reviewed bless; a deliberate fall needs a ledger entry (check-baseline-loosening.py).

A harness seat is not a game fix

The seed has to stand the engine where retail stood, and some of what retail stood on is history a save state does not record: how far an idle animation had run, which arm of a region selector last fired, how long a monster walked before its strike. Restoring that from the state is a harness seat - it changes what the comparison stands on, not the game - and the corpus page labels each one as such. A seat that lifts a score says the engine was measured unfairly; only a change to the engine's own code says the engine was wrong.

A still cannot see a cast. A summon runs for hundreds of vsyncs, and the one a state holds is often the white flash, under which the metric sees nothing. A high image score on a mid-cast state says that frame matches and nothing about the order, timing or length of the cast around it.

A stretch of time: the films

The cast-sequence oracle records one spell, summon or monster cast on both sides, from cast begin to past its Done band: a PCSX-Redux probe injects the cast into a retail state and dumps the displayed frame and per-vsync metadata, and the seeded play-window writes the same per tick. The walk film does the same for a field walk under a fixed pad schedule: player, follow camera, staging pose and placements, pass by pass.

Frames never pair on wall time. A cast's rows fold into runs of one key - the action state, or inside a cast module the module's own phase byte - and the two key lists align by longest common subsequence, so an arm one side skips shows as a one-sided row instead of shifting everything after it. A retail grab shows the frame built two game frames earlier, so it is keyed two counter bumps back, further on a frame that overran its step. A cast, end to end follows the same two clocks through the battle code.

Two retail recordings of one cast are not byte-identical, so a residual smaller than two retail runs differ from each other is noise, not a finding.

Sound

Sound is measured on a ladder, each rung stricter than the one before:

  • Registers. The audio trace compares what the SPU voices are doing; per-voice programming (pitch, envelope words, volume, reverb send) is exact per library state, and the key-on census counts every note a score keys.
  • State. The audio sweep reads libsnd's own records out of each state's RAM and asks whether the seeded engine is playing the same sequence, from the same place.
  • Whole scores and cues. The keying differential runs retail's sequencer off a state's RAM in the R3000 interpreter beside the port's, comparing every sounding voice at every vsync; the cue differential keys every sound-effect row both ways in one state per scene.
  • Waveforms. Real retail PCM: mednafen's own audio output, recorded while it plays on from a state, against the engine's mix of the same bars. Its channels are the SPU model alone (core), sequencer and SPU (track), the state's own ambient cue voices (emitter), and a mid-cast state's voice, cue ring and reaction cues rendered by the engine itself (cast). The state's Monaural option is seated on the engine side, and a cast's offset is fitted within the fight's frame step, because a game frame boundary can sit two or three vsyncs from the state's instant. No listening is involved: every statement is a number off the two waveforms.

Detail, tables and the per-state readings are on audio internals.

Which one to reach for

  1. Reproduce the gap first.Run the instrument that shows the symptom, with a filter to the one state or cast, and keep the number. Theorising before a measurement is how a plausible reading ends up on the falsified register.
  2. Narrow to a routine.If the gap points at one piece of arithmetic, put the routine in the kernel differential: it answers in seconds and leaves no doubt about who is wrong.
  3. Check the time axis.If one frame agrees and the play-through looks wrong, record a film: a late arm and a missing camera move live between the frames a state holds.
  4. Re-measure the same way.A fix is reported as before and after on the same command, with a game change and a harness seat kept apart.

The engine-neutral form of all this - states, seeding, channels, the ratchet and its ledger, evidence grades, disc denominators - is the retail-oracle method. What the corpus does not yet cover, counted against the disc, is capture coverage.

See also