mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
Merge branch 'fix/spu-shadow-cd-activity-gate-20261024' into feat/launcher-quick-controls-artwork-20261024
This commit is contained in:
+112
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "disc_path.h"
|
||||
#include "host_file_identity.h"
|
||||
#include "func_override.h"
|
||||
#include "host_sha256.h"
|
||||
#include "iso_reader.h"
|
||||
#include "mod_packages.h"
|
||||
@@ -369,6 +370,27 @@ void build_function_entry_hooks(const RuntimeMods& s) {
|
||||
g_psx_mod_instruction_hooks = (uint32_t)instructions.size();
|
||||
}
|
||||
|
||||
/* Function overrides queue here at constructor time and are ARMED into the
|
||||
* func_override tier only for plugins the resolved package plan selects —
|
||||
* the same gating as vblank/activation callbacks. `id` is the full
|
||||
* registered name (may carry a ":label" suffix, kept for diagnostics);
|
||||
* `plugin` is the manifest-facing part gating matches on. */
|
||||
struct FunctionOverridePlugin {
|
||||
std::string id;
|
||||
std::string plugin;
|
||||
uint32_t address = 0;
|
||||
PSXModFunctionOverrideFn fn = nullptr;
|
||||
uint32_t guard[FO_MAX_GUARD_WORDS] = {0, 0, 0, 0};
|
||||
int n_guard = 0;
|
||||
int32_t credit = 0;
|
||||
bool armed = false;
|
||||
};
|
||||
|
||||
std::vector<FunctionOverridePlugin>& function_override_plugins() {
|
||||
static std::vector<FunctionOverridePlugin> value;
|
||||
return value;
|
||||
}
|
||||
|
||||
const ModPackage* selected_package(const std::string& id) {
|
||||
return state().manager.selected_package(id);
|
||||
}
|
||||
@@ -1734,6 +1756,20 @@ bool mod_runtime_clear_for_netplay(std::string* error) {
|
||||
s.disc_guard_failed = false;
|
||||
s.netplay_view_plan = false;
|
||||
s.error.clear();
|
||||
/* Clearing s.plan is enough for activation/vblank callbacks — they only
|
||||
* run while something iterates the plan. Function overrides are armed
|
||||
* into func_override.c's own table with the dispatcher hook installed,
|
||||
* so they survive a cleared plan and keep firing unless explicitly
|
||||
* disarmed. That matters most on the rematch path (main.cpp jumps to
|
||||
* session_reboot, which is PAST mod_runtime_activate_plugins and
|
||||
* func_override_install), where a modded session entering netplay would
|
||||
* otherwise print the vanilla banner and still run its overrides —
|
||||
* diverging from a peer without the mod. Also re-arm-able: dropping the
|
||||
* armed flag lets a later plan register the same override again. */
|
||||
const int disarmed = func_override_reset_package_armed();
|
||||
(void)disarmed;
|
||||
for (FunctionOverridePlugin& pending : function_override_plugins())
|
||||
pending.armed = false;
|
||||
if (error) error->clear();
|
||||
std::fprintf(stdout, "psxrecomp: mods cleared for netplay (vanilla session)\n");
|
||||
return true;
|
||||
@@ -3043,6 +3079,17 @@ bool mod_runtime_read_disc_file_sectors(const std::string& path, uint32_t max_by
|
||||
}
|
||||
} // namespace PSXRecompV4
|
||||
|
||||
static std::string s_override_activation_error;
|
||||
extern "C" const char* mod_runtime_activation_error(void) {
|
||||
return s_override_activation_error.empty() ? nullptr : s_override_activation_error.c_str();
|
||||
}
|
||||
|
||||
static int invoke_package_override(CPUState* cpu, FuncOverrideFn fn, const void* context) {
|
||||
using namespace PSXRecompV4;
|
||||
PluginCallbackScope scope(state(),static_cast<const ModResolution::Plugin*>(context),cpu);
|
||||
return fn(cpu);
|
||||
}
|
||||
|
||||
extern "C" void mod_runtime_activate_plugins(void) {
|
||||
using namespace PSXRecompV4;
|
||||
HostLaunchTimingScope timing(HOST_LAUNCH_PLUGIN_ACTIVATION);
|
||||
@@ -3050,6 +3097,9 @@ extern "C" void mod_runtime_activate_plugins(void) {
|
||||
psx_host_set_hud_draw_cb(nullptr, nullptr);
|
||||
mod_reset_plugin_sessions();
|
||||
RuntimeMods& s = state();
|
||||
s_override_activation_error.clear();
|
||||
func_override_reset_package_armed();
|
||||
for (auto& pending : function_override_plugins()) pending.armed = false;
|
||||
s.ticket_ready = false;
|
||||
psx_mod_set_disc_request_provider(nullptr);
|
||||
psx_projection_reset_session();
|
||||
@@ -3060,6 +3110,28 @@ extern "C" void mod_runtime_activate_plugins(void) {
|
||||
s.disc_extents.clear();
|
||||
s.audio_tracks.clear();
|
||||
s.extent_start = 0;
|
||||
/* Arm the entire tier before activation callbacks change host state. */
|
||||
for (auto& pending : function_override_plugins()) {
|
||||
const auto owner = std::find_if(s.plan.plugins.begin(),s.plan.plugins.end(),
|
||||
[&](const ModResolution::Plugin& p) { return p.id == pending.plugin; });
|
||||
if (owner == s.plan.plugins.end()) continue;
|
||||
const int rc = s.netplay_view_plan ? FO_ERR_ARGS : func_override_add_package_scoped(
|
||||
pending.id.c_str(),pending.address,pending.fn,
|
||||
pending.n_guard ? pending.guard : nullptr,pending.n_guard,pending.credit,
|
||||
invoke_package_override,&*owner);
|
||||
if (rc != FO_OK) {
|
||||
s_override_activation_error = "Cannot arm function override " + pending.id +
|
||||
" (registration error " + std::to_string(rc) + ")";
|
||||
s.error = s_override_activation_error;
|
||||
s.plan.ok = false;
|
||||
func_override_reset_package_armed();
|
||||
for (auto& item : function_override_plugins()) item.armed = false;
|
||||
clear_function_entry_hooks();
|
||||
return;
|
||||
}
|
||||
pending.armed = true;
|
||||
}
|
||||
func_override_install();
|
||||
s.activating = true;
|
||||
psx_mod_immediate_begin_activation();
|
||||
for (const ModResolution::Plugin& plugin : s.plan.plugins) {
|
||||
@@ -3555,6 +3627,45 @@ static int mod_plan_hooks_parked(void) {
|
||||
return PSXRecompV4::state().netplay_view_plan && !psx_mod_local_view_scope();
|
||||
}
|
||||
|
||||
extern "C" int psx_mod_register_function_override(
|
||||
const char* id, uint32_t address, PSXModFunctionOverrideFn fn,
|
||||
const uint32_t* expected_words, int n_words, int32_t credit) {
|
||||
using namespace PSXRecompV4;
|
||||
if (!id || !*id || !(address & 0x1fffffffu) || (address & 3u) || !fn) return 0;
|
||||
if (n_words < 0 || n_words > FO_MAX_GUARD_WORDS) return 0;
|
||||
if (n_words > 0 && !expected_words) return 0;
|
||||
if (credit < FO_CREDIT_SELF) return 0;
|
||||
/* An optional ":label" suffix names this override in diagnostics (the
|
||||
* `func_override` TCP command) without multiplying manifest plugin ids —
|
||||
* gating and resolver availability use only the part before the ':'.
|
||||
* "pkg.feature:aim" and "pkg.feature:fire" are two overrides under the
|
||||
* one manifest plugin "pkg.feature". */
|
||||
const char* colon = strchr(id, ':');
|
||||
const std::string plugin_id = colon ? std::string(id, colon - id)
|
||||
: std::string(id);
|
||||
if (plugin_id.empty() || (colon && !colon[1])) return 0;
|
||||
auto& plugins = function_override_plugins();
|
||||
const auto duplicate = std::find_if(
|
||||
plugins.begin(), plugins.end(),
|
||||
[&](const FunctionOverridePlugin& item) {
|
||||
return item.id == id && item.address == address;
|
||||
});
|
||||
if (duplicate != plugins.end()) return 0;
|
||||
FunctionOverridePlugin plugin;
|
||||
plugin.id = id;
|
||||
plugin.plugin = plugin_id;
|
||||
plugin.address = address;
|
||||
plugin.fn = fn;
|
||||
for (int i = 0; i < n_words; ++i) plugin.guard[i] = expected_words[i];
|
||||
plugin.n_guard = n_words;
|
||||
plugin.credit = credit;
|
||||
plugins.push_back(plugin);
|
||||
/* Mark the plugin id available to the package resolver so a manifest can
|
||||
* gate an override-only plugin (multiple overrides may share one). */
|
||||
mod_register_function_override_marker(plugin_id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" int psx_mod_dispatch_guest_function(CPUState* cpu, uint32_t address) {
|
||||
using namespace PSXRecompV4;
|
||||
if (!g_psx_mod_guest_functions || !cpu || address >= 0xC0000000u) return 0;
|
||||
@@ -3757,7 +3868,7 @@ static int game_netplay_function_entry(CPUState* cpu, uint32_t address) {
|
||||
}
|
||||
|
||||
extern "C" int psx_mod_function_entry_active(void) {
|
||||
return PSXRecompV4::function_entry_depth != 0;
|
||||
return PSXRecompV4::function_entry_depth != 0 || func_override_active();
|
||||
}
|
||||
|
||||
extern "C" void mod_runtime_function_entry_context_save(ModFunctionEntryContext *out) {
|
||||
|
||||
Reference in New Issue
Block a user