From 1c7111ddbf73db694784d7c7a40192c6f7386bb4 Mon Sep 17 00:00:00 2001 From: funman300 Date: Tue, 18 Aug 2026 20:20:27 +0000 Subject: [PATCH] Harden FIFA17 SBC dispatch repair --- openfut-hook/src/sbc_dispatch.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/openfut-hook/src/sbc_dispatch.rs b/openfut-hook/src/sbc_dispatch.rs index 2226df3..42d5d32 100644 --- a/openfut-hook/src/sbc_dispatch.rs +++ b/openfut-hook/src/sbc_dispatch.rs @@ -92,6 +92,7 @@ enum Rejection { ReaderMissing, ParseFailed, ResponseClassMismatch, + ModelChanged, ModelEmpty, NotifierNotCurrent, ControllerMismatch, @@ -118,6 +119,7 @@ struct DecisionInput { deserializer_thread: usize, response_class_matches: bool, model: usize, + live_category_count: usize, category_count: usize, notifier_entries: u64, notifier_exits: u64, @@ -175,6 +177,9 @@ fn decide(input: DecisionInput) -> Result { if input.model == 0 || input.category_count == 0 || input.category_count == usize::MAX { return Err(Rejection::ModelEmpty); } + if input.live_category_count != input.category_count { + return Err(Rejection::ModelChanged); + } if input.notifier_entries != generation || input.notifier_entries == 0 || input.notifier_exits.checked_add(1) != Some(input.notifier_entries) @@ -265,6 +270,12 @@ unsafe extern "system" fn completion_wrapper( let response_vtable = crate::sbc_trace::guarded_usize(evidence.deserializer_this); let captured_controller = SBC_CONTROLLER.load(Ordering::Acquire); + let live_category_count = evidence + .model + .checked_add(0x50) + .and_then(|address| crate::sbc_trace::guarded_u16(address)) + .map(usize::from) + .unwrap_or(usize::MAX); let (controller_matches, controller_model_matches) = controller_identity(evidence.base, captured_controller, evidence.model); let input = DecisionInput { @@ -287,6 +298,7 @@ unsafe extern "system" fn completion_wrapper( response_class_matches: response_vtable == evidence.base.checked_add(CATEGORY_RESPONSE_VTABLE_RVA), model: evidence.model, + live_category_count, category_count: evidence.category_count, notifier_entries: evidence.notifier_entries, notifier_exits: evidence.notifier_exits, @@ -676,6 +688,7 @@ mod tests { deserializer_thread: 7, response_class_matches: true, model: 0x4000, + live_category_count: 2, category_count: 2, notifier_entries: generation, notifier_exits: generation - 1, @@ -731,6 +744,10 @@ mod tests { let mut input = valid_input(1); input.controller_model_matches = false; assert_eq!(decide(input), Err(Rejection::ControllerModelMismatch)); + + let mut input = valid_input(1); + input.live_category_count = 0; + assert_eq!(decide(input), Err(Rejection::ModelChanged)); } #[test]