From 8473471a7992440e11329d7e2bb9b96842a02303 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Fri, 17 Apr 2026 20:36:51 -0700 Subject: [PATCH] Expose company stat-band save analysis roots --- crates/rrt-runtime/src/smp.rs | 138 ++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index 9b89cce..9996c1d 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -2346,6 +2346,12 @@ pub struct SmpSaveCompanyRecordAnalysisEntry { pub scalar_dword_candidates: Vec, #[serde(default)] pub post_capacity_dword_candidates: Vec, + #[serde(default)] + pub stat_band_root_0cfb_candidates: Vec, + #[serde(default)] + pub stat_band_root_0d7f_candidates: Vec, + #[serde(default)] + pub stat_band_root_1c47_candidates: Vec, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -3064,6 +3070,27 @@ pub fn inspect_save_company_and_chairman_analysis_bytes( build_save_dword_candidate(&bytes, record_offset, label, *relative_offset) }) .collect::>>()?; + let stat_band_root_0cfb_candidates = + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_CANDIDATE_FIELDS + .iter() + .map(|(label, relative_offset)| { + build_save_dword_candidate(&bytes, record_offset, label, *relative_offset) + }) + .collect::>>()?; + let stat_band_root_0d7f_candidates = + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_CANDIDATE_FIELDS + .iter() + .map(|(label, relative_offset)| { + build_save_dword_candidate(&bytes, record_offset, label, *relative_offset) + }) + .collect::>>()?; + let stat_band_root_1c47_candidates = + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_CANDIDATE_FIELDS + .iter() + .map(|(label, relative_offset)| { + build_save_dword_candidate(&bytes, record_offset, label, *relative_offset) + }) + .collect::>>()?; entries.push(SmpSaveCompanyRecordAnalysisEntry { company_id, name, @@ -3089,6 +3116,9 @@ pub fn inspect_save_company_and_chairman_analysis_bytes( takeover_cooldown_year, scalar_dword_candidates, post_capacity_dword_candidates, + stat_band_root_0cfb_candidates, + stat_band_root_0d7f_candidates, + stat_band_root_1c47_candidates, }); } entries @@ -3211,6 +3241,9 @@ pub fn inspect_save_company_and_chairman_analysis_bytes( notes.push( "Company scalar_dword_candidates expose the current checked-in raw save windows around support/share-price/calendar lanes, and post_capacity_dword_candidates expose the immediate dwords after [company+0x7680] for deeper track-count and record-tail analysis.".to_string(), ); + notes.push( + "Company stat-band root candidates now also expose the first dword windows rooted at [company+0x0cfb], [company+0x0d7f], and [company+0x1c47], the same broader stat bands the grounded cheat reset branch clears before later finance/detail readers rebuild them.".to_string(), + ); notes.push( "Current atlas evidence ties company current_cash and book_value_per_share to stat-family 0x2329 slots 0x0d and 0x1d, so the remaining save-native company finance/governance closure likely needs a structured company-stat family reconstruction instead of more isolated raw offsets." .to_string(), @@ -3462,6 +3495,9 @@ const SAVE_COMPANY_RECORD_LINKED_TRANSIT_LATCH_OFFSET: usize = 0x0d56; const SAVE_COMPANY_RECORD_RECENT_PER_SHARE_SUBSCORE_OFFSET: usize = 0x0d19; const SAVE_COMPANY_RECORD_CACHED_SHARE_PRICE_OFFSET: usize = 0x0d7b; const SAVE_COMPANY_RECORD_TRACK_LAYING_CAPACITY_OFFSET: usize = 0x7680; +const SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET: usize = 0x0cfb; +const SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET: usize = 0x0d7f; +const SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET: usize = 0x1c47; const SAVE_COMPANY_RECORD_SCALAR_CANDIDATE_FIELDS: [(&str, usize); 7] = [ ("mutable_support_scalar", 0x4f), ("young_company_support_scalar", 0x57), @@ -3491,6 +3527,108 @@ const SAVE_COMPANY_RECORD_POST_CAPACITY_CANDIDATE_FIELDS: [(&str, usize); 6] = [ ("post_capacity_word_5", 0x7694), ("post_capacity_word_6", 0x7698), ]; +const SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_CANDIDATE_FIELDS: [(&str, usize); 8] = [ + ( + "stat_band_0cfb_word_1", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET, + ), + ( + "stat_band_0cfb_word_2", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET + 4, + ), + ( + "stat_band_0cfb_word_3", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET + 8, + ), + ( + "stat_band_0cfb_word_4", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET + 12, + ), + ( + "stat_band_0cfb_word_5", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET + 16, + ), + ( + "stat_band_0cfb_word_6", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET + 20, + ), + ( + "stat_band_0cfb_word_7", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET + 24, + ), + ( + "stat_band_0cfb_word_8", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET + 28, + ), +]; +const SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_CANDIDATE_FIELDS: [(&str, usize); 8] = [ + ( + "stat_band_0d7f_word_1", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET, + ), + ( + "stat_band_0d7f_word_2", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET + 4, + ), + ( + "stat_band_0d7f_word_3", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET + 8, + ), + ( + "stat_band_0d7f_word_4", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET + 12, + ), + ( + "stat_band_0d7f_word_5", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET + 16, + ), + ( + "stat_band_0d7f_word_6", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET + 20, + ), + ( + "stat_band_0d7f_word_7", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET + 24, + ), + ( + "stat_band_0d7f_word_8", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET + 28, + ), +]; +const SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_CANDIDATE_FIELDS: [(&str, usize); 8] = [ + ( + "stat_band_1c47_word_1", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET, + ), + ( + "stat_band_1c47_word_2", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET + 4, + ), + ( + "stat_band_1c47_word_3", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET + 8, + ), + ( + "stat_band_1c47_word_4", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET + 12, + ), + ( + "stat_band_1c47_word_5", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET + 16, + ), + ( + "stat_band_1c47_word_6", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET + 20, + ), + ( + "stat_band_1c47_word_7", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET + 24, + ), + ( + "stat_band_1c47_word_8", + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET + 28, + ), +]; const SAVE_COMPANY_RECORD_START_SCAN_LIMIT: usize = 0x120; const SAVE_CHAIRMAN_RECORD_NAME_OFFSET: usize = 0x08;