From c91dba45aff7f996bc9ae3f687bc41b615219eaa Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Thu, 15 Oct 2026 01:16:42 -0700 Subject: [PATCH] Retain verified disc identity in negotiated content plans --- runtime/src/mod_runtime.cpp | 19 +++++++++++++++++-- runtime/tests/test_mod_runtime.cpp | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/runtime/src/mod_runtime.cpp b/runtime/src/mod_runtime.cpp index d7c0cdb7..b54efa09 100644 --- a/runtime/src/mod_runtime.cpp +++ b/runtime/src/mod_runtime.cpp @@ -1987,6 +1987,17 @@ bool mod_runtime_prepare_resources(const std::filesystem::path& disc_path, std:: HostLaunchTimingScope timing(HOST_LAUNCH_PREPARE_RESOURCES); RuntimeMods& s = state(); if (!s.initialized) { timing.success(); return true; } + // Cold lobby offers prepare without committing a plan. Establish their + // selected disc identity too; final commit independently revalidates it. + if (!disc_path.empty() && (disc_path != s.disc_path || s.disc_sha256.empty())) { + std::string digest; + if (!measured_disc_digest(disc_path, digest, &s.error)) { + if (error) *error = s.error; + return false; + } + s.disc_path = disc_path; + s.disc_sha256 = std::move(digest); + } const auto media_cache = media_cache_root(); if (!s.manager.prepare_resources(s.game_id, disc_path, media_cache, &s.error, s.cached_prepare, active_prepare_progress ? *active_prepare_progress : ModPrepareProgress{})) { @@ -2154,7 +2165,9 @@ bool mod_runtime_commit(const std::filesystem::path& disc_path, std::string* err host_file_identity(resolve_disc_path(disc_path).data, inputs.data_identity)); #endif #if !defined(PSX_LAUNCHER_MOD_COMMIT_WORKER_SAFE) - if (disc_path != s.disc_path) + // Offline legacy commits retain path-based digest reuse. A netplay commit + // must remeasure even after a same-path lobby offer was prepared. + if (!save_selection || disc_path != s.disc_path) #endif { std::string hash_error, digest; @@ -2238,7 +2251,9 @@ const std::string& mod_runtime_session_plan_fp() { std::string mod_runtime_plan_fingerprint_portable() { RuntimeMods& s = state(); if (!s.initialized) return {}; - ModResolution plan = s.manager.resolve(s.game_id, s.exe_sha256, std::string()); + // Portable means independent of machine-local paths, not disc identity. + // Exact-disc overlays must retain the source qualification used at commit. + ModResolution plan = s.manager.resolve(s.game_id, s.exe_sha256, s.disc_sha256); if (!plan.ok || !netplay_resources_verified(plan)) return {}; return plan.fingerprint; } diff --git a/runtime/tests/test_mod_runtime.cpp b/runtime/tests/test_mod_runtime.cpp index 3d0d179a..260340e7 100644 --- a/runtime/tests/test_mod_runtime.cpp +++ b/runtime/tests/test_mod_runtime.cpp @@ -1263,6 +1263,26 @@ int main(int argc, char** argv) { check(!PSXRecompV4::mod_runtime_prepare_for_launcher(raw_path,&error, [&](float fraction,const std::string&){++reports;return fraction<1.0f;}),"cancel before installing prepared plan"); check(reports>1 && error=="Preparation canceled.","runtime propagates cooperative cancellation"); + check(PSXRecompV4::mod_runtime_initialize(folder,"READER",0,{},&error),"cold offer overlay initialize"); + check(PSXRecompV4::mod_runtime_prepare_resources(raw_path,&error),"cold offer prepares exact-disc resources without commit"); + const auto offer_fp=PSXRecompV4::mod_runtime_plan_fingerprint_portable(); + check(offer_fp.size()==64 && PSXRecompV4::mod_runtime_fingerprint().empty(),"cold offer qualifies disc without installing a plan"); + check(PSXRecompV4::mod_runtime_initialize(folder,"READER",0,{},&error),"cold LAN overlay initialize"); + test_match_caps={}; + check(PSXRecompV4::mod_runtime_commit_for_direct_netplay(raw_path,&error),"cold LAN accepts exact-disc resource overlay"); + check(PSXRecompV4::mod_runtime_session_plan_fp()==offer_fp && + PSXRecompV4::mod_runtime_plan_fingerprint_portable()==offer_fp && + PSXRecompV4::mod_runtime_fingerprint()==offer_fp,"offer and committed exact-disc identities agree"); + PSXRecompV4::mod_runtime_end_netplay(); + const auto changed_disc=folder/"offered.bin"; + write_bytes(changed_disc,raw_iso); + check(PSXRecompV4::mod_runtime_initialize(folder,"READER",0,{},&error),"changed offer overlay initialize"); + check(PSXRecompV4::mod_runtime_prepare_resources(changed_disc,&error),"prepare source before same-path mutation"); + auto changed_bytes=raw_iso;changed_bytes[10]^=1; // Outside every stock overlay guard range. + write_bytes(changed_disc,changed_bytes); + check(!PSXRecompV4::mod_runtime_commit_for_direct_netplay(changed_disc,&error),"final LAN commit rejects same-path changed source identity"); + check(PSXRecompV4::mod_runtime_session_plan_fp().empty(),"changed offer cannot install session identity"); + check(PSXRecompV4::mod_runtime_initialize(folder,"READER",0,{},&error),"restore original overlay selection"); check(PSXRecompV4::mod_runtime_prepare_for_launcher(raw_path,&error),"prepare verified resource-backed overlay"); mod_runtime_enable_disc_patches(); fs::remove(pack_path,ec); // Committed bytes must remain immutable and resident.