mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
cdrom: fail loudly when a requested disc does not mount
Root cause of the "black screen on a .cue that works as a .bin" reports. cdrom_init() calls iso_open() and, on failure, simply leaves the drive empty (stat_reg = CDSTAT_SHELL). That is correct for a BIOS-only target, which runs with no disc on purpose -- but when a game was GIVEN a disc path and the image does not mount, the game boots against an empty drive and renders nothing. No error, no log, just black. The earlier validate_disc_for_launch() pass cannot catch this, because the two sides read the disc differently: identify_disc() opens the data-track FILE directly, so it reported a green "Disc verified" badge for cues that the MOUNTING reader then rejected. Verified-but-unmountable was a silent state. Demonstrated against the pre-fix reader (a8aa640) on one cue: lowercase/unquoted cue -> FAILS (empty drive -> black screen) canonical cue -> MOUNTS bare .bin -> MOUNTS <- why picking the .bin "fixed" it So the symptom was never about cue-vs-bin semantics; it was cue PARSING strictness. The old mount-side parser required the literal uppercase "BINARY" and a quoted filename, while the verify side uppercased the line first. The shared cue parser landed in68667cdalready fixes that class (case-insensitive keywords, unquoted names, CRLF, case-insensitive payload lookup), which is why all four titles now boot from either file. This commit closes the remaining hole -- any OTHER reason a mount fails must not present as a black screen: - cdrom_has_disc() exposes the drive state (has_disc() was file-static, so no caller could tell a successful mount from an empty drive). - main() checks it right after cdrom_init() and, when a disc path was supplied but nothing mounted, reports what was selected, what it was mounted as, the resolver's note, and that the .bin can be selected directly -- then exits instead of booting into black. Also pins the regression: the lowercase/unquoted cue must MOUNT, not merely parse, so the verify side and the mount side can never silently disagree again. test_disc_path_resolve: 36 -> 38 checks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,15 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
void cdrom_init(const char* cue_path);
|
||||
|
||||
/* Did cdrom_init() actually mount an image? 0 = the drive is empty.
|
||||
*
|
||||
* cdrom_init() is deliberately non-fatal when iso_open() fails (a BIOS-only
|
||||
* target legitimately runs with no disc), so a game that was GIVEN a disc path
|
||||
* whose image could not be mounted would otherwise boot straight into an empty
|
||||
* drive and render nothing -- a black screen with no explanation. Callers that
|
||||
* passed a real path must check this and report. */
|
||||
int cdrom_has_disc(void);
|
||||
/* Disc license region for GetID ("SCEE"/"SCEA"/"SCEI"); derived from the
|
||||
* mounted disc's boot serial at launch. Defaults to "SCEA" (SCPH1001). */
|
||||
void cdrom_set_disc_scex(const char scex[4]);
|
||||
|
||||
@@ -2308,6 +2308,10 @@ void cdrom_init(const char* cue_path) {
|
||||
trace_cdrom('N', 0, has_disc() ? 1u : 0u, 0);
|
||||
}
|
||||
|
||||
int cdrom_has_disc(void) {
|
||||
return has_disc();
|
||||
}
|
||||
|
||||
uint32_t cdrom_read(uint32_t addr) {
|
||||
uint32_t ret = 0;
|
||||
switch (addr) {
|
||||
|
||||
@@ -5812,6 +5812,29 @@ session_reboot:
|
||||
std::fprintf(stdout, "psxrecomp: SPU float-shadow enabled (verified-enhancement)\n");
|
||||
spu_init();
|
||||
cdrom_init(disc_path_str.empty() ? NULL : disc_path_str.c_str());
|
||||
|
||||
/* A disc was requested but nothing mounted. cdrom_init() is non-fatal here
|
||||
* (BIOS-only targets run with an empty drive on purpose), so without this
|
||||
* check the game would boot into an empty drive and render NOTHING -- the
|
||||
* "black screen on a .cue that works as a .bin" symptom. The earlier
|
||||
* validate_disc_for_launch() pass cannot catch it: identify_disc() reads
|
||||
* the data track FILE directly, so a cue whose sheet the mounting reader
|
||||
* rejects still shows a green "Disc verified" badge. Report the actual
|
||||
* failure instead of leaving the player staring at black. */
|
||||
if (!disc_path_str.empty() && !cdrom_has_disc()) {
|
||||
const PSXRecompV4::DiscPathResolution r =
|
||||
PSXRecompV4::resolve_disc_path(disc_path_str);
|
||||
std::string detail =
|
||||
"The disc image was found and verified, but the CD-ROM drive could "
|
||||
"not mount it, so the game would boot with an empty drive.\n\n"
|
||||
"Selected:\n" + disc_path_str;
|
||||
if (r.mount != r.picked) detail += "\nMounted as:\n" + r.mount.string();
|
||||
if (!r.note.empty()) detail += "\n\n" + r.note;
|
||||
detail += "\n\nIf this is a .cue, check that every FILE line it names "
|
||||
"exists next to it; selecting the .bin directly also works.";
|
||||
launcher_warning("Disc Could Not Be Mounted", detail);
|
||||
return 1;
|
||||
}
|
||||
for (const auto& route : warm_cd_routes) {
|
||||
cdrom_register_warm_route(route.arm_lba, route.lbas.data(),
|
||||
(int)route.lbas.size(),
|
||||
|
||||
@@ -168,6 +168,22 @@ void test_cue_syntax_tolerance(const fs::path& root) {
|
||||
check(lower.usable(), "syntax: lowercase + unquoted + CRLF parses");
|
||||
check(same(lower.files.front().path, dir / "game.bin"), "syntax: resolves the payload");
|
||||
|
||||
// THE BLACK-SCREEN CASE. The old mount-side parser matched the literal
|
||||
// uppercase "BINARY" and required a quoted filename, so a cue like the one
|
||||
// above made ISOReader::Open() return false -- while the verify side
|
||||
// (identify_disc) uppercased the line, found the payload, and happily
|
||||
// reported "Disc verified". The launcher therefore showed a green badge and
|
||||
// the runtime then booted with an EMPTY DRIVE: a black screen that went
|
||||
// away if you picked the .bin instead. Both sides share one parser now, so
|
||||
// pin that they agree: this cue must MOUNT, not just parse.
|
||||
{
|
||||
PS1::ISOReader r;
|
||||
check(r.Open((dir / "lower.cue").string()),
|
||||
"syntax: a cue the verify side accepts must also MOUNT (black-screen regression)");
|
||||
check(r.TrackCount() >= 1, "syntax: mounted cue exposes its data track");
|
||||
r.Close();
|
||||
}
|
||||
|
||||
// A WAVE payload has no fixed sector geometry — refuse rather than guess.
|
||||
write_text(dir / "wave.cue",
|
||||
"FILE \"game.bin\" BINARY\n"
|
||||
|
||||
Reference in New Issue
Block a user