Rehost saved world absolute counter state

This commit is contained in:
Jan Petykiewicz 2026-04-17 22:00:12 -07:00
commit 2441fe6374
8 changed files with 381 additions and 54 deletions

View file

@ -44,7 +44,8 @@ company/chairman scalar candidates, including fixed-world chairman slot / role-g
explicit company dword candidate windows, richer chairman qword cache views, and derived explicit company dword candidate windows, richer chairman qword cache views, and derived
holdings-at-share-price / cached purchasing-power comparisons. The same fixed `0x32c8` world holdings-at-share-price / cached purchasing-power comparisons. The same fixed `0x32c8` world
block is now probed for the grounded issue-`0x37` pair at `[world+0x29/+0x2d]`, one broader block is now probed for the grounded issue-`0x37` pair at `[world+0x29/+0x2d]`, one broader
fixed-dword finance neighborhood around the absolute-calendar and issue lanes, and the separate fixed-dword finance neighborhood rooted at `[world+0x0d]` that now carries the saved calendar
tuple and absolute-counter owner lanes directly, and the separate
six-float economic tuning band, but current atlas evidence still keeps that editor-facing six-float economic tuning band, but current atlas evidence still keeps that editor-facing
tuning family distinct from the governance issue lanes behind investor confidence and prime-rate tuning family distinct from the governance issue lanes behind investor confidence and prime-rate
math. The next shared company-side slice is now rehosted too: save-native company direct records math. The next shared company-side slice is now rehosted too: save-native company direct records
@ -66,9 +67,11 @@ dividend / stock-capital logic can extend one owned market reader instead of ano
counter. The next bundled annual-finance reader seam is now rehosted on top of that same market counter. The next bundled annual-finance reader seam is now rehosted on top of that same market
state too, deriving assigned shares, public float, and rounded cached share price from one shared state too, deriving assigned shares, public float, and rounded cached share price from one shared
company market reader instead of scattering more finance helpers across the runtime. A checked-in company market reader instead of scattering more finance helpers across the runtime. A checked-in
The fixed-world finance neighborhood itself is now widened to 16 dwords rooted at `[world+0x11]`, The fixed-world finance neighborhood itself is now widened to 17 dwords rooted at `[world+0x0d]`,
so future issue-`0x38/0x39` closure can build on a broader owned restore-state window rather than so future issue-`0x38/0x39` closure can build on a broader owned restore-state window rather than
another narrow one-off probe. The next company-side seam is now bundled too: a shared company another narrow one-off probe; that same owner surface now also carries the saved absolute counter
as first-class runtime restore state instead of leaving it on “requires shell context” metadata.
The next company-side seam is now bundled too: a shared company
market reader now exposes outstanding shares, assigned shares, public float, rounded cached share market reader now exposes outstanding shares, assigned shares, public float, rounded cached share
price, salary lanes, bonus amount, and the full two-word current/prior issue-calendar tuples from price, salary lanes, bonus amount, and the full two-word current/prior issue-calendar tuples from
the owned annual-finance state instead of leaving that logic spread across summary helpers. The the owned annual-finance state instead of leaving that logic spread across summary helpers. The

View file

@ -35,6 +35,14 @@ pub struct ExpectedRuntimeSummary {
#[serde(default)] #[serde(default)]
pub world_restore_absolute_counter_reconstructible_from_save: Option<bool>, pub world_restore_absolute_counter_reconstructible_from_save: Option<bool>,
#[serde(default)] #[serde(default)]
pub world_restore_current_calendar_tuple_word_raw_u32: Option<u32>,
#[serde(default)]
pub world_restore_current_calendar_tuple_word_2_raw_u32: Option<u32>,
#[serde(default)]
pub world_restore_absolute_counter_raw_u32: Option<u32>,
#[serde(default)]
pub world_restore_absolute_counter_mirror_raw_u32: Option<u32>,
#[serde(default)]
pub world_restore_disable_cargo_economy_special_condition_slot: Option<u8>, pub world_restore_disable_cargo_economy_special_condition_slot: Option<u8>,
#[serde(default)] #[serde(default)]
pub world_restore_disable_cargo_economy_special_condition_reconstructible_from_save: pub world_restore_disable_cargo_economy_special_condition_reconstructible_from_save:
@ -369,6 +377,38 @@ impl ExpectedRuntimeSummary {
)); ));
} }
} }
if let Some(value) = self.world_restore_current_calendar_tuple_word_raw_u32 {
if actual.world_restore_current_calendar_tuple_word_raw_u32 != Some(value) {
mismatches.push(format!(
"world_restore_current_calendar_tuple_word_raw_u32 mismatch: expected {value}, got {:?}",
actual.world_restore_current_calendar_tuple_word_raw_u32
));
}
}
if let Some(value) = self.world_restore_current_calendar_tuple_word_2_raw_u32 {
if actual.world_restore_current_calendar_tuple_word_2_raw_u32 != Some(value) {
mismatches.push(format!(
"world_restore_current_calendar_tuple_word_2_raw_u32 mismatch: expected {value}, got {:?}",
actual.world_restore_current_calendar_tuple_word_2_raw_u32
));
}
}
if let Some(value) = self.world_restore_absolute_counter_raw_u32 {
if actual.world_restore_absolute_counter_raw_u32 != Some(value) {
mismatches.push(format!(
"world_restore_absolute_counter_raw_u32 mismatch: expected {value}, got {:?}",
actual.world_restore_absolute_counter_raw_u32
));
}
}
if let Some(value) = self.world_restore_absolute_counter_mirror_raw_u32 {
if actual.world_restore_absolute_counter_mirror_raw_u32 != Some(value) {
mismatches.push(format!(
"world_restore_absolute_counter_mirror_raw_u32 mismatch: expected {value}, got {:?}",
actual.world_restore_absolute_counter_mirror_raw_u32
));
}
}
if let Some(slot) = self.world_restore_disable_cargo_economy_special_condition_slot { if let Some(slot) = self.world_restore_disable_cargo_economy_special_condition_slot {
if actual.world_restore_disable_cargo_economy_special_condition_slot != Some(slot) { if actual.world_restore_disable_cargo_economy_special_condition_slot != Some(slot) {
mismatches.push(format!( mismatches.push(format!(

View file

@ -527,11 +527,18 @@ fn project_save_slice_components(
); );
metadata.insert( metadata.insert(
"save_slice.selected_year_absolute_counter_source".to_string(), "save_slice.selected_year_absolute_counter_source".to_string(),
"mode-adjusted-lane-via-0x51d390-0x409e80".to_string(), if save_slice.world_finance_neighborhood_state.is_some() {
"save-direct-world-block-absolute-counter".to_string()
} else {
"mode-adjusted-lane-via-0x51d390-0x409e80".to_string()
},
); );
metadata.insert( metadata.insert(
"save_slice.selected_year_absolute_counter_reconstructible_from_save".to_string(), "save_slice.selected_year_absolute_counter_reconstructible_from_save".to_string(),
"false".to_string(), save_slice
.world_finance_neighborhood_state
.is_some()
.to_string(),
); );
metadata.insert( metadata.insert(
"save_slice.disable_cargo_economy_special_condition_slot".to_string(), "save_slice.disable_cargo_economy_special_condition_slot".to_string(),
@ -547,8 +554,12 @@ fn project_save_slice_components(
); );
metadata.insert( metadata.insert(
"save_slice.selected_year_absolute_counter_adjustment_context".to_string(), "save_slice.selected_year_absolute_counter_adjustment_context".to_string(),
"editor-map-mode,shell-selected-year-adjust-policy-0x9d26-0x9d28,save-special-condition-disable-cargo-economy-slot-30" if save_slice.world_finance_neighborhood_state.is_some() {
.to_string(), "save-direct-world-block-0x32c8".to_string()
} else {
"editor-map-mode,shell-selected-year-adjust-policy-0x9d26-0x9d28,save-special-condition-disable-cargo-economy-slot-30"
.to_string()
},
); );
metadata.insert( metadata.insert(
"save_slice.mechanism_family".to_string(), "save_slice.mechanism_family".to_string(),
@ -726,8 +737,28 @@ fn project_save_slice_components(
campaign_scenario_enabled: Some(profile.profile_byte_0xc5 != 0), campaign_scenario_enabled: Some(profile.profile_byte_0xc5 != 0),
sandbox_enabled: Some(profile.profile_byte_0x82 != 0), sandbox_enabled: Some(profile.profile_byte_0x82 != 0),
seed_tuple_written_from_raw_lane: Some(true), seed_tuple_written_from_raw_lane: Some(true),
absolute_counter_requires_shell_context: Some(true), absolute_counter_requires_shell_context: Some(
absolute_counter_reconstructible_from_save: Some(false), save_slice.world_finance_neighborhood_state.is_none(),
),
absolute_counter_reconstructible_from_save: Some(
save_slice.world_finance_neighborhood_state.is_some(),
),
current_calendar_tuple_word_raw_u32: save_slice
.world_finance_neighborhood_state
.as_ref()
.map(|state| state.current_calendar_tuple_word_raw_u32),
current_calendar_tuple_word_2_raw_u32: save_slice
.world_finance_neighborhood_state
.as_ref()
.map(|state| state.current_calendar_tuple_word_2_raw_u32),
absolute_counter_raw_u32: save_slice
.world_finance_neighborhood_state
.as_ref()
.map(|state| state.absolute_counter_raw_u32),
absolute_counter_mirror_raw_u32: save_slice
.world_finance_neighborhood_state
.as_ref()
.map(|state| state.absolute_counter_mirror_raw_u32),
disable_cargo_economy_special_condition_slot: Some(30), disable_cargo_economy_special_condition_slot: Some(30),
disable_cargo_economy_special_condition_reconstructible_from_save: Some(true), disable_cargo_economy_special_condition_reconstructible_from_save: Some(true),
disable_cargo_economy_special_condition_write_side_grounded: Some(true), disable_cargo_economy_special_condition_write_side_grounded: Some(true),
@ -791,11 +822,19 @@ fn project_save_slice_components(
.map(|state| state.lane_value_f32_text.clone()) .map(|state| state.lane_value_f32_text.clone())
.unwrap_or_default(), .unwrap_or_default(),
absolute_counter_restore_kind: Some( absolute_counter_restore_kind: Some(
"mode-adjusted-selected-year-lane".to_string(), if save_slice.world_finance_neighborhood_state.is_some() {
"save-direct-world-absolute-counter".to_string()
} else {
"mode-adjusted-selected-year-lane".to_string()
},
), ),
absolute_counter_adjustment_context: Some( absolute_counter_adjustment_context: Some(
"editor-map-mode,shell-selected-year-adjust-policy-0x9d26-0x9d28,save-special-condition-disable-cargo-economy-slot-30" if save_slice.world_finance_neighborhood_state.is_some() {
.to_string(), "save-direct-world-block-0x32c8".to_string()
} else {
"editor-map-mode,shell-selected-year-adjust-policy-0x9d26-0x9d28,save-special-condition-disable-cargo-economy-slot-30"
.to_string()
},
), ),
} }
} else { } else {
@ -5800,7 +5839,45 @@ mod tests {
lane_raw_hex: vec!["0x3f400000".to_string(), "0x3be56042".to_string()], lane_raw_hex: vec!["0x3f400000".to_string(), "0x3be56042".to_string()],
lane_value_f32_text: vec!["0.750000".to_string(), "0.007000".to_string()], lane_value_f32_text: vec!["0.750000".to_string(), "0.007000".to_string()],
}), }),
world_finance_neighborhood_state: None, world_finance_neighborhood_state: Some(crate::SmpLoadedWorldFinanceNeighborhoodState {
source_kind: "save-fixed-world-block".to_string(),
semantic_family: "world-finance-neighborhood".to_string(),
current_calendar_tuple_word_raw_u32: 1,
current_calendar_tuple_word_raw_hex: "0x00000001".to_string(),
current_calendar_tuple_word_2_raw_u32: 2,
current_calendar_tuple_word_2_raw_hex: "0x00000002".to_string(),
absolute_counter_raw_u32: 3,
absolute_counter_raw_hex: "0x00000003".to_string(),
absolute_counter_mirror_raw_u32: 4,
absolute_counter_mirror_raw_hex: "0x00000004".to_string(),
labels: vec![
"current_calendar_tuple_word".to_string(),
"current_calendar_tuple_word_2".to_string(),
"absolute_calendar_counter".to_string(),
"absolute_calendar_counter_mirror".to_string(),
],
relative_offsets: vec![0x0d, 0x11, 0x15, 0x19],
relative_offset_hex: vec![
"0xd".to_string(),
"0x11".to_string(),
"0x15".to_string(),
"0x19".to_string(),
],
raw_u32: vec![1, 2, 3, 4],
raw_hex: vec![
"0x00000001".to_string(),
"0x00000002".to_string(),
"0x00000003".to_string(),
"0x00000004".to_string(),
],
value_i32: vec![1, 2, 3, 4],
value_f32_text: vec![
"0.000000".to_string(),
"0.000000".to_string(),
"0.000000".to_string(),
"0.000000".to_string(),
],
}),
company_roster: None, company_roster: None,
chairman_profile_table: None, chairman_profile_table: None,
special_conditions_table: Some(crate::SmpLoadedSpecialConditionsTable { special_conditions_table: Some(crate::SmpLoadedSpecialConditionsTable {
@ -5959,14 +6036,39 @@ mod tests {
.state .state
.world_restore .world_restore
.absolute_counter_requires_shell_context, .absolute_counter_requires_shell_context,
Some(true) Some(false)
); );
assert_eq!( assert_eq!(
import import
.state .state
.world_restore .world_restore
.absolute_counter_reconstructible_from_save, .absolute_counter_reconstructible_from_save,
Some(false) Some(true)
);
assert_eq!(
import
.state
.world_restore
.current_calendar_tuple_word_raw_u32,
Some(1)
);
assert_eq!(
import
.state
.world_restore
.current_calendar_tuple_word_2_raw_u32,
Some(2)
);
assert_eq!(
import.state.world_restore.absolute_counter_raw_u32,
Some(3)
);
assert_eq!(
import
.state
.world_restore
.absolute_counter_mirror_raw_u32,
Some(4)
); );
assert_eq!( assert_eq!(
import import
@ -6064,7 +6166,7 @@ mod tests {
.world_restore .world_restore
.absolute_counter_restore_kind .absolute_counter_restore_kind
.as_deref(), .as_deref(),
Some("mode-adjusted-selected-year-lane") Some("save-direct-world-absolute-counter")
); );
assert_eq!( assert_eq!(
import import
@ -6072,9 +6174,7 @@ mod tests {
.world_restore .world_restore
.absolute_counter_adjustment_context .absolute_counter_adjustment_context
.as_deref(), .as_deref(),
Some( Some("save-direct-world-block-0x32c8")
"editor-map-mode,shell-selected-year-adjust-policy-0x9d26-0x9d28,save-special-condition-disable-cargo-economy-slot-30"
)
); );
assert_eq!( assert_eq!(
import.state.save_profile.map_path.as_deref(), import.state.save_profile.map_path.as_deref(),

View file

@ -987,6 +987,14 @@ pub struct RuntimeWorldRestoreState {
#[serde(default)] #[serde(default)]
pub absolute_counter_reconstructible_from_save: Option<bool>, pub absolute_counter_reconstructible_from_save: Option<bool>,
#[serde(default)] #[serde(default)]
pub current_calendar_tuple_word_raw_u32: Option<u32>,
#[serde(default)]
pub current_calendar_tuple_word_2_raw_u32: Option<u32>,
#[serde(default)]
pub absolute_counter_raw_u32: Option<u32>,
#[serde(default)]
pub absolute_counter_mirror_raw_u32: Option<u32>,
#[serde(default)]
pub disable_cargo_economy_special_condition_slot: Option<u8>, pub disable_cargo_economy_special_condition_slot: Option<u8>,
#[serde(default)] #[serde(default)]
pub disable_cargo_economy_special_condition_reconstructible_from_save: Option<bool>, pub disable_cargo_economy_special_condition_reconstructible_from_save: Option<bool>,
@ -1909,6 +1917,10 @@ pub fn runtime_world_issue_state(
} }
} }
pub fn runtime_world_absolute_counter(state: &RuntimeState) -> Option<u32> {
state.world_restore.absolute_counter_raw_u32
}
pub fn runtime_company_unassigned_share_pool(state: &RuntimeState, company_id: u32) -> Option<u32> { pub fn runtime_company_unassigned_share_pool(state: &RuntimeState, company_id: u32) -> Option<u32> {
let outstanding_shares = state let outstanding_shares = state
.service_state .service_state
@ -2570,6 +2582,10 @@ mod tests {
seed_tuple_written_from_raw_lane: Some(true), seed_tuple_written_from_raw_lane: Some(true),
absolute_counter_requires_shell_context: Some(true), absolute_counter_requires_shell_context: Some(true),
absolute_counter_reconstructible_from_save: Some(false), absolute_counter_reconstructible_from_save: Some(false),
current_calendar_tuple_word_raw_u32: None,
current_calendar_tuple_word_2_raw_u32: None,
absolute_counter_raw_u32: None,
absolute_counter_mirror_raw_u32: None,
disable_cargo_economy_special_condition_slot: Some(30), disable_cargo_economy_special_condition_slot: Some(30),
disable_cargo_economy_special_condition_reconstructible_from_save: Some(true), disable_cargo_economy_special_condition_reconstructible_from_save: Some(true),
disable_cargo_economy_special_condition_write_side_grounded: Some(true), disable_cargo_economy_special_condition_write_side_grounded: Some(true),
@ -4078,6 +4094,62 @@ mod tests {
assert_eq!(issue.multiplier_raw_u32, 0x3d75c28f); assert_eq!(issue.multiplier_raw_u32, 0x3d75c28f);
assert_eq!(issue.multiplier_value_f32_text, "0.060000"); assert_eq!(issue.multiplier_value_f32_text, "0.060000");
assert_eq!(runtime_world_issue_state(&state, 0x39), None); assert_eq!(runtime_world_issue_state(&state, 0x39), None);
assert_eq!(runtime_world_absolute_counter(&state), None);
}
#[test]
fn reads_grounded_world_absolute_counter_from_runtime_restore_state() {
let state = RuntimeState {
calendar: CalendarPoint {
year: 1830,
month_slot: 0,
phase_slot: 0,
tick_slot: 0,
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState {
absolute_counter_raw_u32: Some(5),
absolute_counter_mirror_raw_u32: Some(5),
current_calendar_tuple_word_raw_u32: Some(0x0108_0210),
current_calendar_tuple_word_2_raw_u32: Some(0x35e6_3160),
..RuntimeWorldRestoreState::default()
},
metadata: BTreeMap::new(),
companies: Vec::new(),
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
chairman_profiles: Vec::new(),
selected_chairman_profile_id: None,
trains: Vec::new(),
locomotive_catalog: Vec::new(),
cargo_catalog: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
packed_event_collection: None,
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
named_locomotive_availability: BTreeMap::new(),
named_locomotive_cost: BTreeMap::new(),
all_cargo_price_override: None,
named_cargo_price_overrides: BTreeMap::new(),
all_cargo_production_override: None,
factory_cargo_production_override: None,
farm_mine_cargo_production_override: None,
named_cargo_production_overrides: BTreeMap::new(),
cargo_production_overrides: BTreeMap::new(),
world_runtime_variables: BTreeMap::new(),
company_runtime_variables: BTreeMap::new(),
player_runtime_variables: BTreeMap::new(),
territory_runtime_variables: BTreeMap::new(),
world_scalar_overrides: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
assert_eq!(runtime_world_absolute_counter(&state), Some(5));
} }
#[test] #[test]

View file

@ -97,10 +97,27 @@ const RT3_SAVE_WORLD_BLOCK_SELECTED_COMPANY_ID_RELATIVE_OFFSET: usize = 0x1d;
const RT3_SAVE_WORLD_BLOCK_SELECTED_CHAIRMAN_PROFILE_ID_RELATIVE_OFFSET: usize = 0x21; const RT3_SAVE_WORLD_BLOCK_SELECTED_CHAIRMAN_PROFILE_ID_RELATIVE_OFFSET: usize = 0x21;
const RT3_SAVE_WORLD_BLOCK_ISSUE_0X37_MULTIPLIER_RELATIVE_OFFSET: usize = 0x25; const RT3_SAVE_WORLD_BLOCK_ISSUE_0X37_MULTIPLIER_RELATIVE_OFFSET: usize = 0x25;
const RT3_SAVE_WORLD_BLOCK_ISSUE_0X37_VALUE_RELATIVE_OFFSET: usize = 0x29; const RT3_SAVE_WORLD_BLOCK_ISSUE_0X37_VALUE_RELATIVE_OFFSET: usize = 0x29;
const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS: [(&str, usize); 10] = [ const RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET: usize = 0x0d;
("absolute_calendar_counter_candidate_0", 0x11), const RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_2_RELATIVE_OFFSET: usize = 0x11;
("absolute_calendar_counter_candidate_1", 0x15), const RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_RELATIVE_OFFSET: usize = 0x15;
("absolute_calendar_counter_candidate_2", 0x19), const RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_MIRROR_RELATIVE_OFFSET: usize = 0x19;
const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS: [(&str, usize); 11] = [
(
"current_calendar_tuple_word",
RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET,
),
(
"current_calendar_tuple_word_2",
RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_2_RELATIVE_OFFSET,
),
(
"absolute_calendar_counter",
RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_RELATIVE_OFFSET,
),
(
"absolute_calendar_counter_mirror",
RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_MIRROR_RELATIVE_OFFSET,
),
("selection_context_candidate_0", 0x1d), ("selection_context_candidate_0", 0x1d),
("selection_context_candidate_1", 0x21), ("selection_context_candidate_1", 0x21),
("issue_0x37_multiplier", 0x25), ("issue_0x37_multiplier", 0x25),
@ -109,8 +126,8 @@ const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS: [(&str, usize)
("issue_neighbor_candidate_1", 0x31), ("issue_neighbor_candidate_1", 0x31),
("issue_neighbor_candidate_2", 0x35), ("issue_neighbor_candidate_2", 0x35),
]; ];
const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_ROOT_RELATIVE_OFFSET: usize = 0x11; const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_ROOT_RELATIVE_OFFSET: usize = 0x0d;
const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS: usize = 16; const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS: usize = 17;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET: usize = 0x83; const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET: usize = 0x83;
const RT3_SAVE_WORLD_BLOCK_CAMPAIGN_OVERRIDE_FLAG_RELATIVE_OFFSET: usize = 0xc1; const RT3_SAVE_WORLD_BLOCK_CAMPAIGN_OVERRIDE_FLAG_RELATIVE_OFFSET: usize = 0xc1;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_RELATIVE_OFFSET: usize = 0x0bbf; const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_RELATIVE_OFFSET: usize = 0x0bbf;
@ -1535,6 +1552,10 @@ pub struct SmpSaveWorldFinanceNeighborhoodProbe {
pub payload_offset: usize, pub payload_offset: usize,
pub payload_len: usize, pub payload_len: usize,
pub payload_len_hex: String, pub payload_len_hex: String,
pub current_calendar_tuple_word_lane: SmpSaveDwordCandidate,
pub current_calendar_tuple_word_2_lane: SmpSaveDwordCandidate,
pub absolute_counter_lane: SmpSaveDwordCandidate,
pub absolute_counter_mirror_lane: SmpSaveDwordCandidate,
pub dword_candidates: Vec<SmpSaveDwordCandidate>, pub dword_candidates: Vec<SmpSaveDwordCandidate>,
pub evidence: Vec<String>, pub evidence: Vec<String>,
} }
@ -2199,6 +2220,14 @@ pub struct SmpLoadedWorldEconomicTuningState {
pub struct SmpLoadedWorldFinanceNeighborhoodState { pub struct SmpLoadedWorldFinanceNeighborhoodState {
pub source_kind: String, pub source_kind: String,
pub semantic_family: String, pub semantic_family: String,
pub current_calendar_tuple_word_raw_u32: u32,
pub current_calendar_tuple_word_raw_hex: String,
pub current_calendar_tuple_word_2_raw_u32: u32,
pub current_calendar_tuple_word_2_raw_hex: String,
pub absolute_counter_raw_u32: u32,
pub absolute_counter_raw_hex: String,
pub absolute_counter_mirror_raw_u32: u32,
pub absolute_counter_mirror_raw_hex: String,
pub labels: Vec<String>, pub labels: Vec<String>,
pub relative_offsets: Vec<usize>, pub relative_offsets: Vec<usize>,
pub relative_offset_hex: Vec<String>, pub relative_offset_hex: Vec<String>,
@ -3412,6 +3441,20 @@ fn derive_loaded_world_finance_neighborhood_state_from_probe(
SmpLoadedWorldFinanceNeighborhoodState { SmpLoadedWorldFinanceNeighborhoodState {
source_kind: probe.source_kind.clone(), source_kind: probe.source_kind.clone(),
semantic_family: probe.semantic_family.clone(), semantic_family: probe.semantic_family.clone(),
current_calendar_tuple_word_raw_u32: probe.current_calendar_tuple_word_lane.raw_u32,
current_calendar_tuple_word_raw_hex: probe
.current_calendar_tuple_word_lane
.raw_u32_hex
.clone(),
current_calendar_tuple_word_2_raw_u32: probe.current_calendar_tuple_word_2_lane.raw_u32,
current_calendar_tuple_word_2_raw_hex: probe
.current_calendar_tuple_word_2_lane
.raw_u32_hex
.clone(),
absolute_counter_raw_u32: probe.absolute_counter_lane.raw_u32,
absolute_counter_raw_hex: probe.absolute_counter_lane.raw_u32_hex.clone(),
absolute_counter_mirror_raw_u32: probe.absolute_counter_mirror_lane.raw_u32,
absolute_counter_mirror_raw_hex: probe.absolute_counter_mirror_lane.raw_u32_hex.clone(),
labels: probe labels: probe
.dword_candidates .dword_candidates
.iter() .iter()
@ -8808,6 +8851,30 @@ fn parse_save_world_finance_neighborhood_probe(
continue; continue;
} }
let current_calendar_tuple_word_lane = build_save_dword_candidate(
bytes,
payload_offset,
"current_calendar_tuple_word",
RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET,
)?;
let current_calendar_tuple_word_2_lane = build_save_dword_candidate(
bytes,
payload_offset,
"current_calendar_tuple_word_2",
RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_2_RELATIVE_OFFSET,
)?;
let absolute_counter_lane = build_save_dword_candidate(
bytes,
payload_offset,
"absolute_calendar_counter",
RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_RELATIVE_OFFSET,
)?;
let absolute_counter_mirror_lane = build_save_dword_candidate(
bytes,
payload_offset,
"absolute_calendar_counter_mirror",
RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_MIRROR_RELATIVE_OFFSET,
)?;
let dword_candidates = let dword_candidates =
build_save_world_finance_neighborhood_candidates(bytes, payload_offset)?; build_save_world_finance_neighborhood_candidates(bytes, payload_offset)?;
@ -8819,6 +8886,10 @@ fn parse_save_world_finance_neighborhood_probe(
payload_offset, payload_offset,
payload_len: RT3_SAVE_WORLD_BLOCK_LEN, payload_len: RT3_SAVE_WORLD_BLOCK_LEN,
payload_len_hex: format!("0x{:x}", RT3_SAVE_WORLD_BLOCK_LEN), payload_len_hex: format!("0x{:x}", RT3_SAVE_WORLD_BLOCK_LEN),
current_calendar_tuple_word_lane,
current_calendar_tuple_word_2_lane,
absolute_counter_lane,
absolute_counter_mirror_lane,
dword_candidates, dword_candidates,
evidence: vec![ evidence: vec![
format!( format!(
@ -8827,7 +8898,13 @@ fn parse_save_world_finance_neighborhood_probe(
format!( format!(
"next chunk tag 0x32c9 appears at 0x{next_chunk_tag_offset:x}, matching the documented 0x4f2c payload span" "next chunk tag 0x32c9 appears at 0x{next_chunk_tag_offset:x}, matching the documented 0x4f2c payload span"
), ),
"finance-neighborhood candidates cover the fixed dword strip around the grounded world absolute-calendar, selection-context, and issue-0x37 lanes so broader issue-state closure can build on one rehosted owner surface.".to_string(), format!(
"payload +0x{:x}/+0x{:x}/+0x{:x} carry the saved world calendar tuple and absolute counter lanes that later company stock-issue cooldown readers compare against",
RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET,
RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_2_RELATIVE_OFFSET,
RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_RELATIVE_OFFSET
),
"finance-neighborhood candidates cover the fixed dword strip around the grounded world calendar tuple, absolute-counter, selection-context, and issue-0x37 lanes so broader finance reader closure can build on one rehosted owner surface.".to_string(),
], ],
}); });
} }
@ -15575,32 +15652,31 @@ mod tests {
probe.dword_candidates.len(), probe.dword_candidates.len(),
RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS
); );
assert_eq!( assert_eq!(probe.current_calendar_tuple_word_lane.relative_offset_hex, "0xd");
probe.dword_candidates[0].label, assert_eq!(probe.current_calendar_tuple_word_lane.value_i32, 1);
"absolute_calendar_counter_candidate_0" assert_eq!(probe.current_calendar_tuple_word_2_lane.relative_offset_hex, "0x11");
); assert_eq!(probe.current_calendar_tuple_word_2_lane.value_i32, 2);
assert_eq!(probe.dword_candidates[0].relative_offset_hex, "0x11"); assert_eq!(probe.absolute_counter_lane.relative_offset_hex, "0x15");
assert_eq!(probe.absolute_counter_lane.value_i32, 3);
assert_eq!(probe.absolute_counter_mirror_lane.relative_offset_hex, "0x19");
assert_eq!(probe.absolute_counter_mirror_lane.value_i32, 4);
assert_eq!(probe.dword_candidates[0].label, "current_calendar_tuple_word");
assert_eq!(probe.dword_candidates[0].relative_offset_hex, "0xd");
assert_eq!(probe.dword_candidates[0].value_i32, 1); assert_eq!(probe.dword_candidates[0].value_i32, 1);
assert_eq!(probe.dword_candidates[5].label, "issue_0x37_multiplier"); assert_eq!(probe.dword_candidates[6].label, "issue_0x37_multiplier");
assert_eq!(probe.dword_candidates[5].relative_offset_hex, "0x25"); assert_eq!(probe.dword_candidates[6].relative_offset_hex, "0x25");
assert_eq!( assert_eq!(probe.dword_candidates[10].label, "issue_neighbor_candidate_2");
probe.dword_candidates[9].label, assert_eq!(probe.dword_candidates[10].relative_offset_hex, "0x35");
"issue_neighbor_candidate_2"
);
assert_eq!(probe.dword_candidates[9].relative_offset_hex, "0x35");
assert_eq!(probe.dword_candidates[9].value_i32, 10);
assert_eq!(
probe.dword_candidates[10].label,
"finance_neighborhood_word_11"
);
assert_eq!(probe.dword_candidates[10].relative_offset_hex, "0x39");
assert_eq!(probe.dword_candidates[10].value_i32, 11); assert_eq!(probe.dword_candidates[10].value_i32, 11);
assert_eq!(probe.dword_candidates[11].label, "finance_neighborhood_word_12");
assert_eq!(probe.dword_candidates[11].relative_offset_hex, "0x39");
assert_eq!(probe.dword_candidates[11].value_i32, 12);
assert_eq!( assert_eq!(
probe.dword_candidates[15].label, probe.dword_candidates[16].label,
"finance_neighborhood_word_16" "finance_neighborhood_word_17"
); );
assert_eq!(probe.dword_candidates[15].relative_offset_hex, "0x4d"); assert_eq!(probe.dword_candidates[16].relative_offset_hex, "0x4d");
assert_eq!(probe.dword_candidates[15].value_i32, 16); assert_eq!(probe.dword_candidates[16].value_i32, 17);
} }
#[test] #[test]

View file

@ -21,6 +21,10 @@ pub struct RuntimeSummary {
pub world_restore_seed_tuple_written_from_raw_lane: Option<bool>, pub world_restore_seed_tuple_written_from_raw_lane: Option<bool>,
pub world_restore_absolute_counter_requires_shell_context: Option<bool>, pub world_restore_absolute_counter_requires_shell_context: Option<bool>,
pub world_restore_absolute_counter_reconstructible_from_save: Option<bool>, pub world_restore_absolute_counter_reconstructible_from_save: Option<bool>,
pub world_restore_current_calendar_tuple_word_raw_u32: Option<u32>,
pub world_restore_current_calendar_tuple_word_2_raw_u32: Option<u32>,
pub world_restore_absolute_counter_raw_u32: Option<u32>,
pub world_restore_absolute_counter_mirror_raw_u32: Option<u32>,
pub world_restore_disable_cargo_economy_special_condition_slot: Option<u8>, pub world_restore_disable_cargo_economy_special_condition_slot: Option<u8>,
pub world_restore_disable_cargo_economy_special_condition_reconstructible_from_save: pub world_restore_disable_cargo_economy_special_condition_reconstructible_from_save:
Option<bool>, Option<bool>,
@ -178,6 +182,18 @@ impl RuntimeSummary {
world_restore_absolute_counter_reconstructible_from_save: state world_restore_absolute_counter_reconstructible_from_save: state
.world_restore .world_restore
.absolute_counter_reconstructible_from_save, .absolute_counter_reconstructible_from_save,
world_restore_current_calendar_tuple_word_raw_u32: state
.world_restore
.current_calendar_tuple_word_raw_u32,
world_restore_current_calendar_tuple_word_2_raw_u32: state
.world_restore
.current_calendar_tuple_word_2_raw_u32,
world_restore_absolute_counter_raw_u32: state
.world_restore
.absolute_counter_raw_u32,
world_restore_absolute_counter_mirror_raw_u32: state
.world_restore
.absolute_counter_mirror_raw_u32,
world_restore_disable_cargo_economy_special_condition_slot: state world_restore_disable_cargo_economy_special_condition_slot: state
.world_restore .world_restore
.disable_cargo_economy_special_condition_slot, .disable_cargo_economy_special_condition_slot,
@ -1142,6 +1158,10 @@ mod tests {
world_flags: BTreeMap::new(), world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(), save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState { world_restore: RuntimeWorldRestoreState {
current_calendar_tuple_word_raw_u32: Some(0x0108_0210),
current_calendar_tuple_word_2_raw_u32: Some(0x35e6_3160),
absolute_counter_raw_u32: Some(5),
absolute_counter_mirror_raw_u32: Some(5),
issue_37_value: Some(3), issue_37_value: Some(3),
issue_37_multiplier_raw_u32: Some(0x3d75c28f), issue_37_multiplier_raw_u32: Some(0x3d75c28f),
issue_37_multiplier_value_f32_text: Some("0.06".to_string()), issue_37_multiplier_value_f32_text: Some("0.06".to_string()),
@ -1206,6 +1226,19 @@ mod tests {
let summary = RuntimeSummary::from_state(&state); let summary = RuntimeSummary::from_state(&state);
assert_eq!(
summary.world_restore_current_calendar_tuple_word_raw_u32,
Some(0x0108_0210)
);
assert_eq!(
summary.world_restore_current_calendar_tuple_word_2_raw_u32,
Some(0x35e6_3160)
);
assert_eq!(summary.world_restore_absolute_counter_raw_u32, Some(5));
assert_eq!(
summary.world_restore_absolute_counter_mirror_raw_u32,
Some(5)
);
assert_eq!(summary.world_restore_issue_37_value, Some(3)); assert_eq!(summary.world_restore_issue_37_value, Some(3));
assert_eq!( assert_eq!(
summary.world_restore_issue_37_multiplier_raw_u32, summary.world_restore_issue_37_multiplier_raw_u32,

View file

@ -108,7 +108,7 @@ The highest-value next passes are now:
`runtime inspect-save-company-chairman <save.gms>` now dumps those remaining raw record `runtime inspect-save-company-chairman <save.gms>` now dumps those remaining raw record
candidates directly from the rehosted parser, including fixed-world chairman slot / role-gate candidates directly from the rehosted parser, including fixed-world chairman slot / role-gate
context, the grounded fixed-world issue-`0x37` pair, the fixed-dword world finance context, the grounded fixed-world issue-`0x37` pair, the fixed-dword world finance
neighborhood around the absolute-calendar and issue lanes, the separate six-float economic neighborhood rooted at `[world+0x0d]` with the saved calendar tuple and absolute counter, the separate six-float economic
tuning band, derived holdings-at-share-price and cached purchasing-power totals, tuning band, derived holdings-at-share-price and cached purchasing-power totals,
context, company dword candidate windows, and richer chairman qword cache views; the current context, company dword candidate windows, and richer chairman qword cache views; the current
rehosted company-side owner state now also includes a typed market/cache map carrying saved rehosted company-side owner state now also includes a typed market/cache map carrying saved
@ -122,8 +122,9 @@ The highest-value next passes are now:
unassigned share pool derived from outstanding shares minus chairman-held shares for later annual unassigned share pool derived from outstanding shares minus chairman-held shares for later annual
finance logic; that same owned company market state now also backs a bundled annual-finance finance logic; that same owned company market state now also backs a bundled annual-finance
reader seam for assigned shares, public float, and rounded cached share price; the fixed-world reader seam for assigned shares, public float, and rounded cached share price; the fixed-world
finance neighborhood is now widened to 16 dwords rooted at `[world+0x11]` so later issue-family finance neighborhood is now widened to 17 dwords rooted at `[world+0x0d]` so later issue-family
closure can target a broader owned restore-state window; the same annual-finance state now also closure can target a broader owned restore-state window, and the saved absolute counter now
flows through normal runtime restore state instead of staying on shell-context metadata; the same annual-finance state now also
feeds a shared company market reader for stock-capital, salary, bonus, and the full two-word feeds a shared company market reader for stock-capital, salary, bonus, and the full two-word
current/prior issue-calendar tuples, and now derives elapsed years since founding, last dividend, current/prior issue-calendar tuples, and now derives elapsed years since founding, last dividend,
and last bankruptcy for later annual finance-policy rehosting; live bond-slot count now travels through that same owned annual-finance and last bankruptcy for later annual finance-policy rehosting; live bond-slot count now travels through that same owned annual-finance

View file

@ -209,9 +209,11 @@ chairman-held shares, so later dividend / stock-capital work can extend a shared
reader instead of guessing another finance leaf. The same owned company market state now also reader instead of guessing another finance leaf. The same owned company market state now also
supports a bundled annual-finance reader seam for assigned shares, public float, and rounded supports a bundled annual-finance reader seam for assigned shares, public float, and rounded
cached share price, which is a better base for later dividend / issue-calendar simulation than cached share price, which is a better base for later dividend / issue-calendar simulation than
scattered single-field helpers. The fixed-world finance neighborhood is now widened to 16 dwords scattered single-field helpers. The fixed-world finance neighborhood is now widened to 17 dwords
rooted at `[world+0x11]`, so later issue-`0x38/0x39` closure can build on a broader owned rooted at `[world+0x0d]`, so later issue-`0x38/0x39` closure can build on a broader owned
restore-state window instead of another narrow probe. The same owned company annual-finance state restore-state window instead of another narrow probe; that same owner surface now also carries
the saved world absolute counter as first-class runtime restore state instead of shell-context
metadata. The same owned company annual-finance state
now also drives a shared company market reader seam for stock-capital, salary, bonus, and the full now also drives a shared company market reader seam for stock-capital, salary, bonus, and the full
two-word current/prior issue-calendar tuples, which is a better base for shellless finance two-word current/prior issue-calendar tuples, which is a better base for shellless finance
simulation than summary-only helpers. That same owned annual-finance state now also derives elapsed simulation than summary-only helpers. That same owned annual-finance state now also derives elapsed