diff --git a/openfut-hook/src/probe.rs b/openfut-hook/src/probe.rs index fc208fa..92482c1 100644 --- a/openfut-hook/src/probe.rs +++ b/openfut-hook/src/probe.rs @@ -161,6 +161,47 @@ pub unsafe fn install_listener_probe() { crate::write_log(&format!("PROBE listener: dispatch site patched @ {:#x}\n", target as usize)); } +/// FORCING EXPERIMENT: directly invoke `nucleusConnectREST` (FIFA23.exe+0x2861910) +/// from a background thread once FIFA is at "connecting". That function is nearly +/// self-contained — it fetches X=[0x14acd02c0], M=[X+0x360], ctx=[M+0x778] from the +/// global singleton and, if ctx is non-null, sends a GetAuthCode LSX request. The +/// dormant subsystem never calls it; we call it ourselves to test whether triggering +/// the connect makes FIFA fire GetAuthCode (which the bridge answers) and advance. +/// Guarded: we only call once the ctx chain is valid, to avoid a null-deref crash. +pub fn install_force_connect() { + std::thread::spawn(|| unsafe { + let base = GetModuleHandleA(core::ptr::null()); + if base.is_null() { return; } + let base = base as usize; + let x_slot = base + 0xacd02c0; + let rest: extern "system" fn() -> usize = + core::mem::transmute(base + 0x2861910); + // Wait for the ctx chain to be valid (FIFA past bootstrap), up to ~60s. + let mut fired = 0; + for i in 0..120u32 { + std::thread::sleep(std::time::Duration::from_millis(500)); + let ctx = read_ptr(x_slot) + .filter(|&x| x != 0) + .and_then(|x| read_ptr(x + 0x360)) + .filter(|&m| m != 0) + .and_then(|m| read_ptr(m + 0x778)) + .filter(|&c| c != 0); + let Some(ctx) = ctx else { continue; }; + // Give the game a few seconds settled at "connecting" before poking. + if i < 60 { continue; } + crate::write_log(&format!( + "FORCE: calling nucleusConnectREST() (ctx={ctx:#x}) attempt {fired}\n" + )); + let r = rest(); + crate::write_log(&format!("FORCE: nucleusConnectREST returned {r:#x}\n")); + fired += 1; + if fired >= 3 { break; } + std::thread::sleep(std::time::Duration::from_millis(3000)); + } + crate::write_log("FORCE: done\n"); + }); +} + struct Target { /// DLL name (nul-terminated) or ignored when `main_exe` is true. module: &'static [u8], @@ -279,6 +320,9 @@ pub fn install_probes_deferred() { install_probes(); install_listener_probe(); install_state_sampler(); + // install_force_connect(); // manual-only: forcing experiment (2026-07-02); + // re-enable to auto-invoke nucleusConnectREST(). Off by default so normal + // probe builds don't poke the online flow. }); }