FAITHFUL_TIMING_PLAN gets the dated session entry and ACCURACY_BURNDOWN's
backend-equivalence item names the tool and the R4 warm/cold result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves the warm/cold A/B tool out of R4 so every title can use it. `run`
launches the runtime from a command template ({build} {port} {headless}
{state_dir}) or from --runtime/--game/--disc, optionally installs a saved
overlay state (--seed), turbos to frame N and saves the frame_fingerprint
ring plus the dispatch-miss count. Runs default to PSX_DEBUG_FMV_QUIET=0 and
PSX_OVERLAY_AUTOCOMPILE_OFF=1, and the dump records what they used.
`compare` judges on the guest-fact columns (cyc mmio mc sp sc wc ws qc) and
reports wr/pc only as the frame where the runs part. It tolerates the
one-write VBlank straddle from docs/TCP_COMMANDS.md (ws/wc off for one frame,
equal again at the next) and, when FMV-quiet was on, the wc+qc shift with ws
judged against the carried offset. Every tolerated frame is counted and
listed; --strict turns them off. A straddle on the last frame, a judge column
an older runtime does not report, or different FMV-quiet settings make the
result INCOMPLETE. Exit codes are unchanged from the R4 tool: IDENTICAL 0,
MISMATCH 1, usage 2, INCOMPLETE 3, MISSES 4.
The self-test (ctest fp_identity) covers column judging, both tolerances,
truncated and mismatched frame sets, dumps without ws/qc, and drives `run`
against a fake debug server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frame_fingerprint's wr column is an ordered hash over main-RAM writes.
DMA and device writes (MDEC-out, CD, SPU, GPU->RAM) are recorded in host
device-service order. Native shards flush cycles at every store barrier,
while batched interpreted and static code services devices after the
block. So identical guest writes gave a different wr forever after.
- ws: an order-independent sum of a splitmix64 term per (addr, val) over
main RAM. Together with wc it is the main-RAM judge column. wr and pc
stay as ordered locators.
- qc: fingerprint-eligible writes that FMV-quiet kept out. If a straddled
write falls into a quiet frame in one run only, the cumulative columns
never agree again, and qc is where that shows.
- The accumulator moves to runtime/include/frame_fingerprint.h so the
semantics are unit-tested (frame_fingerprint_test).
- The JSON buffer is sized from the real maximum entry length (284 B),
which the old 224 B per entry could overflow.
docs/TCP_COMMANDS.md documents the column roles, the one-frame snapshot
straddle and the FMV-quiet caveat (set PSX_DEBUG_FMV_QUIET=0 for A/B
identity runs). Existing columns keep their meaning. Nothing outside
debug-tools builds changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New projects describe themselves to community lists (recomp.board et al.)
via .recomp.json at the repo root: game, system PS1, type recomp,
toolchain psxrecomp, status exploring, and original region + serial.
write_recomp_json.py takes the serial from disc_probe.json and derives
region from the Sony serial prefix, falling back to --region (which
defaults to USA and would mislabel PAL discs). It only fills missing
fields, so re-running it never clobbers a maintainer's edits.
Both gitignore templates ignored /*.json at the root, which would have
silently dropped the file from the initial commit; un-ignore it.
Closes#406
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runtime mounts a .chd natively and the launcher lists it in the disc
picker, so a player could select one and see a valid disc identity, then have
first-run setup fail: verify-disc, prepare_disc.py and probe_disc.py hash and
parse raw 2352-byte track data, which a CHD stores compressed. Nothing on the
Python side could read it, and telling players to install MAME for chdman is
not an answer.
libchdr is already vendored and compiled for the runtime and the native media
CLI. Build it once more as a shared library in recompiler/ (CMake target chdr,
output next to the emitters, static CRT on Windows so a plain python.exe can
load it) and read the CHD from Python through ctypes. tools/psx_chd.py mirrors
the layout the runtime's iso_reader.cpp uses -- 2448-byte frames, 4-frame
track padding, stored versus virtual pregaps, big-endian audio -- and
reproduces the .bin files a Redump dump of the same disc contains, in either
of Redump's two layouts (one bin per track, or one bin for the disc).
verify-disc hashes those reconstructed track bytes against [prepare_disc]
instead of the container. prepare_disc.py writes the tracks into out_dir in
whichever layout the recorded digests match and then continues down its
normal cue path, so the data track is written once, SBI companions are looked
up next to the CHD, and the receipt names the CHD the player chose.
probe_disc.py extracts to a scratch directory and probes the cue. The CLI
builds target chdr on demand for a kit whose emitters predate it, and falls
back to the extract-it-first message when it cannot.
Verified against two discs from a Redump library converted with chdman: a
single-track MODE2 disc and a four-track disc with stored audio pregaps both
reproduce their catalogued track-1 size, MD5 and SHA-1 exactly; verify-disc
and prepare_disc.py accept the four-track one end to end. Tests build a
synthetic uncompressed v5 CHD around a cooked ISO image, so no retail data is
needed; the decode tests skip with a reason when no libchdr is built.