Files
psxrecomp/runtime/include/disc_path.h
T
Matthew Stanley 68667cd58b disc+bios: either file the user picks resolves, mounts, and verifies
Two path-resolution defects where the runtime silently accepted only one of
two equally-valid spellings of the same input.

1. Disc: picking the .cue threw away the disc's TOC.

normalize_disc_path_for_launch() swapped any .cue for its same-named .bin
unconditionally, and every disc entry point (game.toml, --disc, the cached
disc.cfg, the launcher's Change Disc, the first-run dialog) went through it.
That discarded the cue's table of contents -- every CD-DA track and pregap
with it -- even though iso_reader.cpp has full multi-FILE cue support that
the swap defeated before it was ever reached. In the other direction,
picking a redump audio-track bin mounted that track as the whole disc, which
cannot boot.

resolve_disc_path() (runtime/include/disc_path.h) now canonicalizes any pick:

  .cue that resolves            -> mount the cue (TOC intact)
  .cue with a missing bin       -> the payload it still names, else a
                                   stem-matched sibling image
  .bin/.iso/.img owned by a cue -> mount that cue
  bare image, no cue            -> mount it

identify_disc() resolves through the same code, so the header/serial/CRC32
verdict is read from the data track no matter which member was picked.

While here: the mount path and the verify path had two divergent private cue
parsers (quoted-only + case-sensitive vs quoted-or-bare + case-insensitive),
so they could disagree about the same file. Both now use one parser
(runtime/include/cue_sheet.h), which additionally handles CRLF, backslash
paths, and case-insensitive payload lookup for case-sensitive filesystems.

2. BIOS: the multi-BIOS rename made configs and dump folders incompatible.

SCPH*.toml name "US-PSX-SCPH1001.BIN" while every pre-existing dump folder,
bios.cfg and tools/regen_bios.sh default name "SCPH1001.BIN". Either
combination fataled with "cannot open BIOS file" despite the right ROM being
present. resolve_bios_rom() (recompiler/include/bios_rom_alias.h) matches on
the model token -- the last '-'-separated component of the stem -- so both
conventions resolve in both directions without a hardcoded alias table.
Applied at the config loader, the positional psxrecomp-bios CLI, and each
rung of the runtime's BIOS search.

The positional CLI also derived out_stem from the raw filename, so a
region-qualified ROM would have emitted US-PSX-SCPH1001_full.c -- a name
runtime.cmake cannot link. It now derives the model token.

Also fixes SCPH5552.toml, whose rom field omitted the bios/ directory that
its two siblings have.

Tests: test_disc_path_resolve (36 checks, five dump layouts) and
test_bios_rom_alias (15 checks, both directions + negative cases), both
registered in runtime/CMakeLists.txt. Registered the pre-existing
test_iso_reader_cdda, which was wired into no build.

Verified on real dumps: Ape Escape .cue and .bin both give
SCUS-94423 / NTSC-U / crc=C6F455BC; Tomba 2's multi-track dump gives
crc=1FB8C151 and a 2-track TOC from the .cue, the data bin, and the CD-DA
bin alike.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-24 22:17:52 -07:00

53 lines
2.3 KiB
C++

// disc_path.h — canonicalize whatever disc file the user actually picked.
//
// Players do not reliably know whether to hand us the .cue or the .bin, and
// both are legitimate things to find in a dump folder. Either pick must mount
// and must verify identically, so every entry point that accepts a disc path
// (game.toml [runtime] disc, --disc, the cached disc.cfg, the launcher's
// "Change Disc" picker, the first-run file dialog) routes through
// resolve_disc_path() first.
//
// The rule is "prefer the cue, but never require it":
//
// picked a .cue that resolves -> mount the cue (keeps the TOC)
// picked a .cue with a missing bin -> mount a sibling image, if one exists
// picked a .bin/.iso/.img owned by a cue -> mount that cue (keeps the TOC)
// picked a .bin/.iso/.img with no cue -> mount it directly
//
// Preferring the cue matters beyond tidiness: the cue is the only place the
// CD-DA track layout lives. Silently swapping a cue for its same-named .bin
// (which is what the runtime used to do) drops every audio track and pregap on
// a single-file multi-track dump.
//
// `data` is always the binary carrying the ISO9660 header and SYSTEM.CNF, so
// header / serial / CRC32 checks read the same bytes no matter which file the
// user picked — that is what keeps the "Disc verified" verdict stable across
// picks.
#pragma once
#include <filesystem>
#include <string>
namespace PSXRecompV4 {
struct DiscPathResolution {
std::filesystem::path picked; // what the caller asked for (absolute where possible)
std::filesystem::path mount; // hand this to ISOReader::Open / cdrom_init
std::filesystem::path data; // data-track binary; read identity/CRC from this
bool from_cue = false; // `mount` is a cue sheet
bool upgraded_to_cue = false; // caller picked a bin; we found the cue that owns it
bool cue_fallback = false; // caller picked an unusable cue; fell back to a bin
// Human-readable note describing any substitution, "" when `mount` is just
// `picked`. Suitable for a launcher warning or a startup banner.
std::string note;
};
// Never fails: an unrecognizable or missing path resolves to itself, so the
// caller's existing "could not open the disc" reporting still fires.
DiscPathResolution resolve_disc_path(const std::filesystem::path& picked);
} // namespace PSXRecompV4