mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
feat(bios): pick the BIOS at runtime — OpenBIOS unless the player chose one
Implements docs/BIOS_SELECTION.md on top of the backend ABI. One build
now carries both images and decides at startup, so the per-BIOS build
flavour (and its three uncross-checked restatements) is gone.
- runtime.cmake takes PSXRECOMP_BIOS_STEMS (default "OpenBIOS;SCPH1001")
and generates psx_bios_registry.c from it, so the stem list is the
single source of truth for what a build carries.
- [runtime] openbios (default true) records whether a title may use the
bundled image. Not exposed to settings.toml: it is a developer's
compatibility finding, not a player preference.
- Selection: an explicit choice that matches a linked image wins;
otherwise the bundled image; otherwise prompt. A mismatched explicit
choice is explained and falls back to OpenBIOS when the title allows
it, instead of dead-ending.
- validate_bios_for_launch now ASKS WHICH backend an image is, rather
than checking against one hardcoded identity, and activates it.
Crucially, "no BIOS" means "the player chose none" — not "no BIOS file
was discoverable". The compile-time DEFAULT_BIOS_PATH is no longer
treated as a choice: it made a stray file near the executable produce a
BIOS Mismatch dialog before the bundled image was considered.
Also fixed while wiring this: the settings.toml BIOS-adoption check read
psx_bios_image.image_bundled, which now runs before any backend is
active (all-zero), so it asks the registry instead.
Verified on Tomba! with ONE binary, both paths:
no --bios -> OPENBIOS, no dialog, boots to in-game save
--bios SCPH1001.BIN-> SCPH-1001 (crc 37157331, bundled=0)
and the per-image state follows the selection, not just the label:
kernel-bless window 0x500-0x4BA0 vs 0x500-0x8500, deliver_event_ret
0x0 vs 0x80001720.
Still outstanding: savestates must record the active BIOS and refuse to
load under another (kernel RAM layout differs); a player-facing way to
clear a remembered choice; retiring PSXRECOMP_BIOS_STEM/_PROFILE, which
now only feed the staleness stamp.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -222,6 +222,9 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path&
|
||||
if (runtime.contains("fast_boot")) {
|
||||
rt.fast_boot = toml::find<bool>(runtime, "fast_boot");
|
||||
}
|
||||
if (runtime.contains("openbios")) {
|
||||
rt.openbios = toml::find<bool>(runtime, "openbios");
|
||||
}
|
||||
if (runtime.contains("bios_hle")) {
|
||||
rt.bios_hle = toml::find<bool>(runtime, "bios_hle");
|
||||
}
|
||||
|
||||
@@ -132,6 +132,16 @@ struct RuntimeConfig {
|
||||
// to LLE. Implies the HLE boot shell-skip unless bios_hle_keep_intro.
|
||||
// PSX_BIOS_HLE / PSX_BIOS_HLE_KEEP_INTRO env override at launch.
|
||||
// Runtime: runtime/src/bios_hle.c.
|
||||
// openbios: may this title run on the bundled, redistributable OpenBIOS?
|
||||
// Default true — a player who chooses no BIOS gets OpenBIOS and never has
|
||||
// to find one (docs/BIOS_SELECTION.md). Set false ONLY for a title with a
|
||||
// verified OpenBIOS incompatibility; a retail image is then required and
|
||||
// the player is prompted for it.
|
||||
//
|
||||
// Deliberately NOT overridable by a player's settings.toml: this records a
|
||||
// developer's compatibility finding, not a preference.
|
||||
bool openbios = true;
|
||||
|
||||
bool bios_hle = true;
|
||||
bool bios_hle_keep_intro = false;
|
||||
|
||||
|
||||
+58
-14
@@ -168,6 +168,7 @@ set(PSXRECOMP_RUNTIME_SOURCES
|
||||
${PSXRECOMP_ROOT}/runtime/src/event_ring.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/game_options.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/psx_keybinds.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/psx_bios_backend.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/psx_netplay.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/psx_lobby_client.c
|
||||
${PSXRECOMP_ROOT}/recompiler/src/config_loader.cpp
|
||||
@@ -246,22 +247,62 @@ if(PSXRECOMP_LOBBY_INCLUDE_DIR)
|
||||
list(APPEND PSXRECOMP_RUNTIME_INCLUDE_DIRS ${PSXRECOMP_LOBBY_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
# Which recompiled BIOS the runtime links, selected by profile. One BIOS per
|
||||
# binary: generated symbols are address-derived (func_1FC00000, psx_dispatch,
|
||||
# psx_bios_kernel_bodies), so two images collide on essentially every symbol —
|
||||
# a game repo picks its BIOS at configure time. The stem must match the
|
||||
# profile's [recompiler] out_stem; it is a separate cache variable (rather
|
||||
# than parsed out of the profile) because CMake cannot read TOML at configure
|
||||
# time — a mismatch surfaces immediately as missing generated/<stem>_*.c.
|
||||
set(PSXRECOMP_BIOS_STEM "SCPH1001" CACHE STRING
|
||||
"Output stem of the recompiled BIOS to link (matches the profile's out_stem)")
|
||||
set(PSXRECOMP_BIOS_PROFILE "${PSXRECOMP_ROOT}/bios/SCPH1001.toml" CACHE FILEPATH
|
||||
# Which recompiled BIOSes the runtime links. A build carries every image it
|
||||
# ships — bundled OpenBIOS plus a retail one — and chooses between them at
|
||||
# startup (docs/BIOS_SELECTION.md). Each image exports a single
|
||||
# <STEM>_psx_bios_backend descriptor and namespaces everything else, so they
|
||||
# co-link; the runtime routes psx_dispatch()/psx_bios_image through whichever
|
||||
# backend it selects.
|
||||
#
|
||||
# One build configuration on purpose: the previous per-BIOS flavour needed the
|
||||
# same choice restated in game.toml and two CMake variables with nothing
|
||||
# cross-checking them, and it linked cleanly when they disagreed.
|
||||
set(PSXRECOMP_BUNDLED_BIOS_PATH "bios/openbios.bin" CACHE STRING
|
||||
"Bundled redistributable BIOS image, relative to the executable")
|
||||
set(PSXRECOMP_BIOS_STEMS "OpenBIOS;SCPH1001" CACHE STRING
|
||||
"Recompiled BIOS stems to link (first bundled/redistributable one is the default at runtime)")
|
||||
# The profile is still needed as the staleness-stamp input for the primary stem.
|
||||
list(GET PSXRECOMP_BIOS_STEMS 0 PSXRECOMP_BIOS_STEM_PRIMARY)
|
||||
set(PSXRECOMP_BIOS_STEM "${PSXRECOMP_BIOS_STEM_PRIMARY}" CACHE STRING
|
||||
"Primary recompiled BIOS stem (staleness stamp; see PSXRECOMP_BIOS_STEMS)")
|
||||
set(PSXRECOMP_BIOS_PROFILE "${PSXRECOMP_ROOT}/bios/${PSXRECOMP_BIOS_STEM}.toml" CACHE FILEPATH
|
||||
"BIOS profile TOML this build regenerates from (staleness stamp input)")
|
||||
|
||||
set(PSXRECOMP_BIOS_GENERATED
|
||||
${PSXRECOMP_ROOT}/generated/${PSXRECOMP_BIOS_STEM}_full.c
|
||||
${PSXRECOMP_ROOT}/generated/${PSXRECOMP_BIOS_STEM}_dispatch.c
|
||||
)
|
||||
set(PSXRECOMP_BIOS_GENERATED "")
|
||||
set(_psxrt_registry_externs "")
|
||||
set(_psxrt_registry_entries "")
|
||||
foreach(_stem IN LISTS PSXRECOMP_BIOS_STEMS)
|
||||
list(APPEND PSXRECOMP_BIOS_GENERATED
|
||||
${PSXRECOMP_ROOT}/generated/${_stem}_full.c
|
||||
${PSXRECOMP_ROOT}/generated/${_stem}_dispatch.c)
|
||||
string(APPEND _psxrt_registry_externs
|
||||
"extern const PsxBiosBackend ${_stem}_psx_bios_backend;
|
||||
")
|
||||
string(APPEND _psxrt_registry_entries
|
||||
" &${_stem}_psx_bios_backend,
|
||||
")
|
||||
endforeach()
|
||||
list(LENGTH PSXRECOMP_BIOS_STEMS _psxrt_bios_count)
|
||||
|
||||
# Registry of the compiled-in backends, in preference order. Generated so the
|
||||
# stem list stays the single source of truth.
|
||||
set(_psxrt_registry_c "${CMAKE_BINARY_DIR}/psx_bios_registry.c")
|
||||
file(WRITE "${_psxrt_registry_c}"
|
||||
"/* Generated by runtime.cmake from PSXRECOMP_BIOS_STEMS — do not edit. */
|
||||
"
|
||||
"#include \"psx_bios_backend.h\"
|
||||
|
||||
"
|
||||
"${_psxrt_registry_externs}"
|
||||
"
|
||||
const PsxBiosBackend *const psx_bios_registry[] = {
|
||||
"
|
||||
"${_psxrt_registry_entries}"
|
||||
"};
|
||||
"
|
||||
"const uint32_t psx_bios_registry_count = ${_psxrt_bios_count}u;
|
||||
")
|
||||
list(APPEND PSXRECOMP_BIOS_GENERATED "${_psxrt_registry_c}")
|
||||
|
||||
# --- BIOS generated/ staleness check (hygiene) -----------------------------------
|
||||
# generated/<stem>_*.c is gitignored build output produced by a SEPARATE build
|
||||
@@ -510,6 +551,9 @@ function(psxrecomp_add_runtime_target target)
|
||||
target_compile_definitions(${target} PRIVATE
|
||||
DEFAULT_DEBUG_PORT=${PSXRT_DEBUG_PORT}
|
||||
PSX_DEFAULT_BIOS_PATH="${PSXRT_DEFAULT_BIOS_PATH}"
|
||||
# Where the shipped redistributable image lives, relative to the exe.
|
||||
# This is what a player gets when they choose no BIOS.
|
||||
PSX_BUNDLED_BIOS_PATH="${PSXRECOMP_BUNDLED_BIOS_PATH}"
|
||||
PSX_DEFAULT_GAME_CONFIG_PATH="${PSXRT_DEFAULT_GAME_CONFIG_PATH}"
|
||||
PSX_WINDOW_TITLE="${PSXRT_WINDOW_TITLE}"
|
||||
PSX_BUILD_REV="${PSX_GIT_REV}"
|
||||
|
||||
+138
-70
@@ -6,7 +6,8 @@
|
||||
*/
|
||||
|
||||
#include "cpu_state.h"
|
||||
#include "psx_bios_image.h" /* the linked recompiled BIOS's self-description */
|
||||
#include "psx_bios_image.h" /* the active BIOS's self-description */
|
||||
#include "psx_bios_backend.h" /* routing to whichever BIOS was selected */
|
||||
#include "psx_scheduler.h" /* psx_scheduler_run — deterministic TCB scheduler */
|
||||
#include "parity_trace.h" /* general two-process control-flow parity ring */
|
||||
#include "device_trace.h" /* general two-process device-event cycle ring */
|
||||
@@ -860,6 +861,16 @@ static void launcher_info(const char* title, const std::string& msg) {
|
||||
* config loads, before any interactive file resolution. */
|
||||
static std::string s_picker_game_name = "PSXRecomp";
|
||||
|
||||
/* BIOS selection state (docs/BIOS_SELECTION.md). s_openbios_allowed is the
|
||||
* game's [runtime] openbios; s_bundled_bios_rel is where the shipped
|
||||
* redistributable image lives relative to the executable. */
|
||||
static bool s_openbios_allowed = true;
|
||||
#ifdef PSX_BUNDLED_BIOS_PATH
|
||||
static std::string s_bundled_bios_rel = PSX_BUNDLED_BIOS_PATH;
|
||||
#else
|
||||
static std::string s_bundled_bios_rel = "bios/openbios.bin";
|
||||
#endif
|
||||
|
||||
static bool pick_runtime_file(const char* title, const char* filter,
|
||||
std::filesystem::path& out) {
|
||||
// Headless: never open an interactive file dialog (it blocks). Fail the
|
||||
@@ -990,94 +1001,135 @@ static std::filesystem::path normalize_disc_path_for_launch(const std::filesyste
|
||||
return p;
|
||||
}
|
||||
|
||||
static bool validate_bios_for_launch(const std::filesystem::path& path) {
|
||||
/* HARD identity gate against the linked recompiled BIOS
|
||||
* (psx_bios_image, emitted from the BIOS profile). The selected file is
|
||||
* only ever DATA: the code that executes is the statically recompiled C
|
||||
* of one specific image. Running that code against a different image's
|
||||
* bytes is a guaranteed wild-jump crash (mismatched jump tables /
|
||||
* kernel-copy source), so a mismatch is a refusal — the old warn-only
|
||||
* CRC check let exactly that crash happen. */
|
||||
/* Which compiled-in backend, if any, was recompiled from THIS file?
|
||||
*
|
||||
* The chosen file is only ever DATA: the code that executes is the statically
|
||||
* recompiled C of one specific image, so running it against another image's
|
||||
* bytes is a guaranteed wild jump. Identity therefore decides WHICH backend
|
||||
* may run, not merely whether to warn. Null = no linked image matches.
|
||||
*/
|
||||
static const PsxBiosBackend* bios_backend_for_file(const std::filesystem::path& path,
|
||||
uint32_t* out_crc,
|
||||
uint64_t* out_size) {
|
||||
std::ifstream f(path, std::ios::binary | std::ios::ate);
|
||||
if (!f.is_open()) return false;
|
||||
if (!f.is_open()) return nullptr;
|
||||
const std::streamoff size = f.tellg();
|
||||
if ((uint64_t)size != (uint64_t)psx_bios_image.image_size) {
|
||||
char buf[320];
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
"The selected BIOS is %llu bytes; this build's recompiled BIOS "
|
||||
"(%s) is %u bytes.\n\nSelect the exact image this build was "
|
||||
"compiled from.",
|
||||
(unsigned long long)size,
|
||||
psx_bios_image.image_id, psx_bios_image.image_size);
|
||||
launcher_warning("BIOS Mismatch", buf);
|
||||
return false;
|
||||
}
|
||||
if (out_size) *out_size = (uint64_t)size;
|
||||
if (size <= 0) return nullptr;
|
||||
std::vector<uint8_t> data((size_t)size);
|
||||
if (!read_at(f, 0, data.data(), data.size())) return false;
|
||||
if (!read_at(f, 0, data.data(), data.size())) return nullptr;
|
||||
const uint32_t crc = crc32_compute(data.data(), data.size());
|
||||
if (crc != psx_bios_image.image_crc32) {
|
||||
char buf[448];
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
"The selected BIOS (CRC32 %08X) is not the image this build's "
|
||||
"recompiled BIOS was compiled from:\n\n %s\n CRC32 %08X\n"
|
||||
" SHA256 %.16s...\n\nThe compiled-in code would execute against "
|
||||
"mismatched data and crash. Select the exact matching image.",
|
||||
crc, psx_bios_image.image_id, psx_bios_image.image_crc32,
|
||||
psx_bios_image.image_sha256);
|
||||
launcher_warning("BIOS Mismatch", buf);
|
||||
return false;
|
||||
if (out_crc) *out_crc = crc;
|
||||
for (uint32_t i = 0; i < psx_bios_registry_count; i++) {
|
||||
const PsxBiosBackend* b = psx_bios_registry[i];
|
||||
if (!b || !b->image) continue;
|
||||
if ((uint64_t)size == (uint64_t)b->image->image_size &&
|
||||
crc == b->image->image_crc32)
|
||||
return b;
|
||||
}
|
||||
return true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* What a player may supply, for mismatch and picker copy. The bundled image is
|
||||
* excluded: it is never something to go and find. */
|
||||
static std::string bios_accepted_images() {
|
||||
std::string s;
|
||||
for (uint32_t i = 0; i < psx_bios_registry_count; i++) {
|
||||
const PsxBiosBackend* b = psx_bios_registry[i];
|
||||
if (!b || !b->image || b->image->image_bundled) continue;
|
||||
if (!s.empty()) s += ", ";
|
||||
s += b->image->image_id;
|
||||
s += " (" + std::to_string(b->image->image_size / 1024u) + " KB)";
|
||||
}
|
||||
return s.empty() ? std::string("(this build ships its own BIOS)") : s;
|
||||
}
|
||||
|
||||
/* Identity-gate a player-chosen BIOS and activate its backend on success. */
|
||||
static bool validate_bios_for_launch(const std::filesystem::path& path) {
|
||||
uint32_t crc = 0; uint64_t size = 0;
|
||||
const PsxBiosBackend* b = bios_backend_for_file(path, &crc, &size);
|
||||
if (b) return psx_bios_activate(b) != 0;
|
||||
|
||||
char buf[512];
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
"That BIOS (%llu bytes, CRC32 %08X) is not an image this build was "
|
||||
"compiled from.\n\nThis build accepts: %s\n\nThe compiled-in code "
|
||||
"would execute against mismatched data and crash.",
|
||||
(unsigned long long)size, crc, bios_accepted_images().c_str());
|
||||
launcher_warning("BIOS Mismatch", buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::filesystem::path resolve_bios_path(const char* requested, const char* argv0);
|
||||
|
||||
/* Resolve which BIOS image file to load, and activate its backend.
|
||||
*
|
||||
* Policy (docs/BIOS_SELECTION.md):
|
||||
* no explicit player choice -> bundled OpenBIOS, silently
|
||||
* explicit choice, matching -> that image
|
||||
* explicit choice, mismatched-> explained; falls back to OpenBIOS if allowed
|
||||
* openbios disabled for this title -> a retail image is required
|
||||
*
|
||||
* "Explicit" means --bios or a remembered launcher/settings pick. Finding a
|
||||
* file on disk deliberately does NOT count: discovery used to adopt whatever
|
||||
* happened to sit near the executable, so two players with the same build
|
||||
* could end up on different BIOSes.
|
||||
*/
|
||||
static std::filesystem::path resolve_bios_for_runtime(const char* requested,
|
||||
const char* argv0) {
|
||||
std::filesystem::path resolved = resolve_bios_path(requested, argv0);
|
||||
if (!resolved.empty() && std::filesystem::exists(resolved) &&
|
||||
validate_bios_for_launch(resolved)) {
|
||||
return resolved;
|
||||
const char* argv0,
|
||||
bool requested_is_explicit) {
|
||||
const bool openbios_allowed = s_openbios_allowed;
|
||||
const PsxBiosBackend* bundled = psx_bios_bundled();
|
||||
|
||||
/* 1. An explicit choice: --bios, else a remembered pick. */
|
||||
std::filesystem::path chosen;
|
||||
if (requested_is_explicit && requested && requested[0]) {
|
||||
/* --bios, or a BIOS named in settings.toml. NOT the compile-time
|
||||
* default: that is a build constant, not something the player asked
|
||||
* for, and treating it as a choice made a stray file near the exe
|
||||
* produce a "BIOS Mismatch" dialog before the bundled image was even
|
||||
* considered. */
|
||||
chosen = resolve_bios_path(requested, argv0);
|
||||
} else {
|
||||
std::filesystem::path cached = read_cached_path(argv0, "bios.cfg");
|
||||
if (!cached.empty() && std::filesystem::exists(cached)) chosen = cached;
|
||||
}
|
||||
if (!chosen.empty() && std::filesystem::exists(chosen)) {
|
||||
if (validate_bios_for_launch(chosen)) return chosen; /* activates it */
|
||||
/* Mismatched. With OpenBIOS available this is recoverable, so say so
|
||||
* and carry on rather than dead-ending on a file they chose once. */
|
||||
if (!(openbios_allowed && bundled)) return {};
|
||||
}
|
||||
|
||||
/* Bundled BIOS (psx_bios_image.image_bundled — a redistributable image
|
||||
* like OpenBIOS that ships WITH the game): the user supplies only their
|
||||
* disc. There is nothing to pick and no cached path to consult — if the
|
||||
* bundled image is missing or tampered (the identity gate above already
|
||||
* refused it), that is an installation defect, not a selection problem. */
|
||||
if (psx_bios_image.image_bundled) {
|
||||
/* 2. No usable explicit choice -> bundled OpenBIOS, if this title allows it. */
|
||||
if (openbios_allowed && bundled) {
|
||||
std::filesystem::path img = resolve_bios_path(
|
||||
s_bundled_bios_rel.empty() ? nullptr : s_bundled_bios_rel.c_str(), argv0);
|
||||
if (!img.empty() && std::filesystem::exists(img) &&
|
||||
bios_backend_for_file(img, nullptr, nullptr) == bundled &&
|
||||
psx_bios_activate(bundled)) {
|
||||
return img;
|
||||
}
|
||||
launcher_warning("Bundled BIOS Missing",
|
||||
std::string("This build ships its own BIOS (") +
|
||||
psx_bios_image.image_id + "), but the bundled image was not "
|
||||
"found or does not match.\n\nExpected next to the executable:\n" +
|
||||
(requested && requested[0] ? requested : "(default path)") +
|
||||
"\n\nReinstall or rebuild — there is nothing to select.");
|
||||
bundled->image->image_id + "), but the bundled image is missing "
|
||||
"or does not match.\n\nExpected next to the executable:\n" +
|
||||
(s_bundled_bios_rel.empty() ? "(default path)" : s_bundled_bios_rel) +
|
||||
"\n\nReinstall or rebuild.");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::filesystem::path cached = read_cached_path(argv0, "bios.cfg");
|
||||
if (!cached.empty() && std::filesystem::exists(cached) &&
|
||||
validate_bios_for_launch(cached)) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
/* Interactive pick. Be explicit about WHICH of the two user-supplied
|
||||
* files is being requested — first-time users see two pickers in a
|
||||
* row and the difference between "BIOS" and "disc image" is not
|
||||
* obvious to non-technical players. */
|
||||
const std::string bios_id = psx_bios_image.image_id[0]
|
||||
? std::string(psx_bios_image.image_id) : std::string("PlayStation BIOS");
|
||||
/* 3. This title requires a retail BIOS: ask for one. */
|
||||
const std::string accepted = bios_accepted_images();
|
||||
launcher_info((s_picker_game_name + " — PlayStation BIOS needed").c_str(),
|
||||
s_picker_game_name + " does not include any Sony or game files.\n\n"
|
||||
s_picker_game_name + " requires a PlayStation BIOS.\n\n"
|
||||
"Step 1 of 2 — PlayStation BIOS\n\n"
|
||||
"In the next window, select your PlayStation BIOS dump. This build "
|
||||
"requires the exact image it was compiled from: " + bios_id +
|
||||
" (" + std::to_string(psx_bios_image.image_size / 1024u) + " KB). "
|
||||
"You must dump it from your own console or otherwise "
|
||||
"legally obtain it.\n\n"
|
||||
"(This is NOT the game disc — that is asked for next.)");
|
||||
"requires the exact image it was compiled from: " + accepted + ". "
|
||||
"You must dump it from your own console or otherwise legally obtain "
|
||||
"it.\n\n(This is NOT the game disc — that is asked for next.)");
|
||||
std::string bios_title =
|
||||
s_picker_game_name + " — Step 1 of 2: select PlayStation BIOS (" + bios_id + ")";
|
||||
s_picker_game_name + " — Step 1 of 2: select PlayStation BIOS (" + accepted + ")";
|
||||
for (;;) {
|
||||
std::filesystem::path picked;
|
||||
if (!pick_runtime_file(
|
||||
@@ -4459,6 +4511,10 @@ int main(int argc, char** argv) {
|
||||
const char* game_config_path = nullptr;
|
||||
const char* disc_override_path = nullptr;
|
||||
bool bios_from_cli = false; /* CLI --bios/positional wins over settings.toml */
|
||||
/* Did the PLAYER choose this BIOS (CLI or settings), as opposed to it
|
||||
* being the compile-time default? Only a real choice overrides the
|
||||
* bundled OpenBIOS — see docs/BIOS_SELECTION.md. */
|
||||
bool bios_explicit = false;
|
||||
/* Launcher overrides (mirrors snesrecomp): --launcher forces the GUI back on
|
||||
* even when [launcher] skip_launcher = true is set; --no-launcher (and the
|
||||
* PSX_NO_LAUNCHER env) forces it off. --launcher wins if both are given. */
|
||||
@@ -4499,6 +4555,7 @@ int main(int argc, char** argv) {
|
||||
if (std::strcmp(argv[i], "--bios") == 0 && i + 1 < argc) {
|
||||
bios_path = argv[++i];
|
||||
bios_from_cli = true;
|
||||
bios_explicit = true;
|
||||
} else if (std::strcmp(argv[i], "--game") == 0 && i + 1 < argc) {
|
||||
game_config_path = argv[++i];
|
||||
} else if (std::strcmp(argv[i], "--disc") == 0 && i + 1 < argc) {
|
||||
@@ -4549,6 +4606,8 @@ int main(int argc, char** argv) {
|
||||
if (!bios_from_cli) {
|
||||
bios_path = argv[i];
|
||||
bios_from_cli = true;
|
||||
bios_explicit = true;
|
||||
bios_explicit = true;
|
||||
} else {
|
||||
std::fprintf(stderr,
|
||||
"psxrecomp: ignoring unexpected positional argument after BIOS selection: %s\n",
|
||||
@@ -4827,6 +4886,9 @@ int main(int argc, char** argv) {
|
||||
fast_boot = gc.runtime.fast_boot;
|
||||
bios_hle = gc.runtime.bios_hle;
|
||||
bios_hle_keep_intro = gc.runtime.bios_hle_keep_intro;
|
||||
/* Developer compatibility finding, applied before BIOS selection.
|
||||
* Not exposed to settings.toml on purpose — see BIOS_SELECTION.md. */
|
||||
s_openbios_allowed = gc.runtime.openbios;
|
||||
/* Let the dispatch layer distinguish "dirty because text was
|
||||
* loaded" from "diverged because runtime wrote different code over
|
||||
* the original EXE image". Packed/self-modifying games can rewrite
|
||||
@@ -5111,9 +5173,14 @@ int main(int argc, char** argv) {
|
||||
* as-hide treatment as lock_mode / ws_offered below) so a stale
|
||||
* settings.toml or launcher-less build can never point a bundled
|
||||
* build at a foreign image. The identity gate is the backstop. */
|
||||
if (us.has_bios_path && !bios_from_cli && !psx_bios_image.image_bundled) {
|
||||
/* A BIOS named in settings.toml is a player choice. Note this runs
|
||||
* BEFORE any backend is activated, so it must not consult
|
||||
* psx_bios_image (all-zero until selection) — ask the registry. */
|
||||
if (us.has_bios_path && !bios_from_cli &&
|
||||
!(s_openbios_allowed && psx_bios_bundled() != nullptr)) {
|
||||
settings_bios_storage = us.bios_path.string();
|
||||
bios_path = settings_bios_storage.c_str();
|
||||
bios_explicit = true;
|
||||
}
|
||||
if (us.has_disc_path && !disc_override_path)
|
||||
resolved_disc = normalize_disc_path_for_launch(us.disc_path);
|
||||
@@ -5673,7 +5740,8 @@ int main(int argc, char** argv) {
|
||||
memcard2_path.clear();
|
||||
}
|
||||
|
||||
std::filesystem::path resolved_bios = resolve_bios_for_runtime(bios_path, argv[0]);
|
||||
std::filesystem::path resolved_bios =
|
||||
resolve_bios_for_runtime(bios_path, argv[0], bios_explicit);
|
||||
if (resolved_bios.empty()) {
|
||||
std::fprintf(stderr, "psxrecomp: no BIOS selected; exiting.\n");
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user