Probe fixed-world finance neighborhood

This commit is contained in:
Jan Petykiewicz 2026-04-17 18:58:41 -07:00
commit dad9291308
4 changed files with 161 additions and 6 deletions

View file

@ -43,8 +43,9 @@ surface also now exposes `runtime inspect-save-company-chairman <save.gms>` for
company/chairman scalar candidates, including fixed-world chairman slot / role-gate context, company/chairman scalar candidates, including fixed-world chairman slot / role-gate context,
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 both the grounded issue-`0x37` pair at `[world+0x29/+0x2d]` and the block is now probed for the grounded issue-`0x37` pair at `[world+0x29/+0x2d]`, one broader
separate six-float economic tuning band, but current atlas evidence still keeps that editor-facing fixed-dword finance neighborhood around the absolute-calendar and issue lanes, and the separate
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
flow into a typed company market/cache map on runtime service state, carrying outstanding shares, flow into a typed company market/cache map on runtime service state, carrying outstanding shares,

View file

@ -97,6 +97,18 @@ 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] = [
("absolute_calendar_counter_candidate_0", 0x11),
("absolute_calendar_counter_candidate_1", 0x15),
("absolute_calendar_counter_candidate_2", 0x19),
("selection_context_candidate_0", 0x1d),
("selection_context_candidate_1", 0x21),
("issue_0x37_multiplier", 0x25),
("issue_0x37_value", 0x29),
("issue_neighbor_candidate_0", 0x2d),
("issue_neighbor_candidate_1", 0x31),
("issue_neighbor_candidate_2", 0x35),
];
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;
@ -1512,6 +1524,19 @@ pub struct SmpSaveWorldIssue37Probe {
pub evidence: Vec<String>, pub evidence: Vec<String>,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SmpSaveWorldFinanceNeighborhoodProbe {
pub profile_family: String,
pub source_kind: String,
pub semantic_family: String,
pub chunk_tag_offset: usize,
pub payload_offset: usize,
pub payload_len: usize,
pub payload_len_hex: String,
pub dword_candidates: Vec<SmpSaveDwordCandidate>,
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpSaveTaggedCollectionHeaderProbe { pub struct SmpSaveTaggedCollectionHeaderProbe {
pub profile_family: String, pub profile_family: String,
@ -2341,6 +2366,8 @@ pub struct SmpSaveCompanyChairmanAnalysisReport {
#[serde(default)] #[serde(default)]
pub world_economic_tuning: Option<SmpSaveWorldEconomicTuningProbe>, pub world_economic_tuning: Option<SmpSaveWorldEconomicTuningProbe>,
#[serde(default)] #[serde(default)]
pub world_finance_neighborhood: Option<SmpSaveWorldFinanceNeighborhoodProbe>,
#[serde(default)]
pub company_entries: Vec<SmpSaveCompanyRecordAnalysisEntry>, pub company_entries: Vec<SmpSaveCompanyRecordAnalysisEntry>,
#[serde(default)] #[serde(default)]
pub chairman_entries: Vec<SmpSaveChairmanRecordAnalysisEntry>, pub chairman_entries: Vec<SmpSaveChairmanRecordAnalysisEntry>,
@ -2592,6 +2619,7 @@ pub struct SmpInspectionReport {
pub save_world_selection_context_probe: Option<SmpSaveWorldSelectionContextProbe>, pub save_world_selection_context_probe: Option<SmpSaveWorldSelectionContextProbe>,
pub save_world_issue_37_probe: Option<SmpSaveWorldIssue37Probe>, pub save_world_issue_37_probe: Option<SmpSaveWorldIssue37Probe>,
pub save_world_economic_tuning_probe: Option<SmpSaveWorldEconomicTuningProbe>, pub save_world_economic_tuning_probe: Option<SmpSaveWorldEconomicTuningProbe>,
pub save_world_finance_neighborhood_probe: Option<SmpSaveWorldFinanceNeighborhoodProbe>,
pub save_company_collection_header_probe: Option<SmpSaveTaggedCollectionHeaderProbe>, pub save_company_collection_header_probe: Option<SmpSaveTaggedCollectionHeaderProbe>,
pub save_chairman_profile_collection_header_probe: Option<SmpSaveTaggedCollectionHeaderProbe>, pub save_chairman_profile_collection_header_probe: Option<SmpSaveTaggedCollectionHeaderProbe>,
#[serde(default)] #[serde(default)]
@ -2906,6 +2934,7 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
let world_selection_context = selection_probe.map(build_save_world_selection_role_analysis); let world_selection_context = selection_probe.map(build_save_world_selection_role_analysis);
let world_issue_37 = report.save_world_issue_37_probe.clone(); let world_issue_37 = report.save_world_issue_37_probe.clone();
let world_economic_tuning = report.save_world_economic_tuning_probe.clone(); let world_economic_tuning = report.save_world_economic_tuning_probe.clone();
let world_finance_neighborhood = report.save_world_finance_neighborhood_probe.clone();
let company_header_probe = report.save_company_collection_header_probe.as_ref(); let company_header_probe = report.save_company_collection_header_probe.as_ref();
let chairman_header_probe = report let chairman_header_probe = report
.save_chairman_profile_collection_header_probe .save_chairman_profile_collection_header_probe
@ -3128,6 +3157,12 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
.to_string(), .to_string(),
); );
} }
if world_finance_neighborhood.is_some() {
notes.push(
"World analysis now also exports one fixed dword finance neighborhood around the grounded issue/calendar lanes, so future issue-0x38/0x39 closure can build on rehosted owner-state candidates instead of ad hoc byte guesses."
.to_string(),
);
}
if !company_entries.is_empty() { if !company_entries.is_empty() {
notes.push( notes.push(
"Company debt is derived from the grounded bond table at [company+0x5b/+0x5f] by summing live principal slots.".to_string(), "Company debt is derived from the grounded bond table at [company+0x5b/+0x5f] by summing live principal slots.".to_string(),
@ -3165,6 +3200,7 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
world_selection_context, world_selection_context,
world_issue_37, world_issue_37,
world_economic_tuning, world_economic_tuning,
world_finance_neighborhood,
company_entries, company_entries,
chairman_entries, chairman_entries,
notes, notes,
@ -6664,6 +6700,11 @@ fn inspect_bundle_bytes(bytes: &[u8], file_extension_hint: Option<String>) -> Sm
file_extension_hint.as_deref(), file_extension_hint.as_deref(),
container_profile.as_ref(), container_profile.as_ref(),
); );
let save_world_finance_neighborhood_probe = parse_save_world_finance_neighborhood_probe(
bytes,
file_extension_hint.as_deref(),
container_profile.as_ref(),
);
let save_company_collection_header_probe = parse_save_company_collection_header_probe( let save_company_collection_header_probe = parse_save_company_collection_header_probe(
bytes, bytes,
file_extension_hint.as_deref(), file_extension_hint.as_deref(),
@ -6835,6 +6876,7 @@ fn inspect_bundle_bytes(bytes: &[u8], file_extension_hint: Option<String>) -> Sm
save_world_selection_context_probe, save_world_selection_context_probe,
save_world_issue_37_probe, save_world_issue_37_probe,
save_world_economic_tuning_probe, save_world_economic_tuning_probe,
save_world_finance_neighborhood_probe,
save_company_collection_header_probe, save_company_collection_header_probe,
save_chairman_profile_collection_header_probe, save_chairman_profile_collection_header_probe,
save_company_roster_probe, save_company_roster_probe,
@ -8436,6 +8478,64 @@ fn parse_save_world_issue_37_probe(
None None
} }
fn parse_save_world_finance_neighborhood_probe(
bytes: &[u8],
file_extension_hint: Option<&str>,
container_profile: Option<&SmpContainerProfile>,
) -> Option<SmpSaveWorldFinanceNeighborhoodProbe> {
if file_extension_hint != Some("gms") {
return None;
}
let profile = container_profile?;
let supported = matches!(
profile.profile_family.as_str(),
"rt3-classic-save-container-v1"
| "rt3-105-save-container-v1"
| "rt3-105-scenario-save-container-v1"
| "rt3-105-alt-save-container-v1"
);
if !supported {
return None;
}
for chunk_tag_offset in find_u32_le_offsets(bytes, RT3_SAVE_WORLD_BLOCK_CHUNK_TAG) {
let payload_offset = chunk_tag_offset + 4;
let next_chunk_tag_offset = payload_offset.checked_add(RT3_SAVE_WORLD_BLOCK_LEN)?;
if read_u32_at(bytes, next_chunk_tag_offset) != Some(RT3_SAVE_WORLD_BLOCK_NEXT_CHUNK_TAG) {
continue;
}
let dword_candidates = RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS
.iter()
.map(|(label, relative_offset)| {
build_save_dword_candidate(bytes, payload_offset, label, *relative_offset)
})
.collect::<Option<Vec<_>>>()?;
return Some(SmpSaveWorldFinanceNeighborhoodProbe {
profile_family: profile.profile_family.clone(),
source_kind: "save-direct-world-block".to_string(),
semantic_family: "scenario-save-world-finance-neighborhood".to_string(),
chunk_tag_offset,
payload_offset,
payload_len: RT3_SAVE_WORLD_BLOCK_LEN,
payload_len_hex: format!("0x{:x}", RT3_SAVE_WORLD_BLOCK_LEN),
dword_candidates,
evidence: vec![
format!(
"chunk tag 0x32c8 at 0x{chunk_tag_offset:x} matches the fixed [world+0x04] save block"
),
format!(
"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(),
],
});
}
None
}
fn parse_save_world_economic_tuning_probe( fn parse_save_world_economic_tuning_probe(
bytes: &[u8], bytes: &[u8],
file_extension_hint: Option<&str>, file_extension_hint: Option<&str>,
@ -15124,6 +15224,58 @@ mod tests {
assert!((probe.multiplier_lane.value_f32 - 0.06).abs() < f32::EPSILON); assert!((probe.multiplier_lane.value_f32 - 0.06).abs() < f32::EPSILON);
} }
#[test]
fn parses_save_world_finance_neighborhood_probe_from_fixed_world_block() {
let mut bytes = vec![0u8; 0x8000];
let chunk_tag_offset = 0x3ceusize;
let payload_offset = chunk_tag_offset + 4;
bytes[chunk_tag_offset..chunk_tag_offset + 4]
.copy_from_slice(&RT3_SAVE_WORLD_BLOCK_CHUNK_TAG.to_le_bytes());
for (index, (_, relative_offset)) in
RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS
.iter()
.enumerate()
{
bytes[payload_offset + *relative_offset..payload_offset + *relative_offset + 4]
.copy_from_slice(&((index as u32) + 1).to_le_bytes());
}
let next_chunk_offset = payload_offset + RT3_SAVE_WORLD_BLOCK_LEN;
bytes[next_chunk_offset..next_chunk_offset + 4]
.copy_from_slice(&RT3_SAVE_WORLD_BLOCK_NEXT_CHUNK_TAG.to_le_bytes());
let probe = parse_save_world_finance_neighborhood_probe(
&bytes,
Some("gms"),
Some(&SmpContainerProfile {
profile_family: "rt3-105-save-container-v1".to_string(),
profile_evidence: vec![],
is_known_profile: true,
}),
)
.expect("world finance neighborhood probe should parse");
assert_eq!(probe.chunk_tag_offset, chunk_tag_offset);
assert_eq!(probe.payload_offset, payload_offset);
assert_eq!(
probe.dword_candidates.len(),
RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS.len()
);
assert_eq!(
probe.dword_candidates[0].label,
"absolute_calendar_counter_candidate_0"
);
assert_eq!(probe.dword_candidates[0].relative_offset_hex, "0x11");
assert_eq!(probe.dword_candidates[0].value_i32, 1);
assert_eq!(probe.dword_candidates[5].label, "issue_0x37_multiplier");
assert_eq!(probe.dword_candidates[5].relative_offset_hex, "0x25");
assert_eq!(
probe.dword_candidates[9].label,
"issue_neighbor_candidate_2"
);
assert_eq!(probe.dword_candidates[9].relative_offset_hex, "0x35");
assert_eq!(probe.dword_candidates[9].value_i32, 10);
}
#[test] #[test]
fn loads_selection_only_company_and_chairman_context_from_save_world_probe() { fn loads_selection_only_company_and_chairman_context_from_save_world_probe() {
let mut report = inspect_smp_bytes(&[]); let mut report = inspect_smp_bytes(&[]);

View file

@ -107,8 +107,9 @@ The highest-value next passes are now:
defaults until their raw offsets are pinned more strongly; the offline analysis command defaults until their raw offsets are pinned more strongly; the offline analysis command
`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 separate six-float economic tuning context, the grounded fixed-world issue-`0x37` pair, the fixed-dword world finance
band, derived holdings-at-share-price and cached purchasing-power totals, neighborhood around the absolute-calendar and issue lanes, the separate six-float economic
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
outstanding-shares, support/share-price/cache words, salary lanes, calendar words, and outstanding-shares, support/share-price/cache words, salary lanes, calendar words, and

View file

@ -66,8 +66,9 @@ Implemented today:
from the bond table and raw company track-laying capacity from the record tail are grounded too, from the bond table and raw company track-laying capacity from the record tail are grounded too,
and chairman purchasing power now reuses the strongest nonnegative cached qword total from the and chairman purchasing power now reuses the strongest nonnegative cached qword total from the
`[profile+0x1e9..]` band plus current cash instead of collapsing to plain net worth; `[profile+0x1e9..]` band plus current cash instead of collapsing to plain net worth;
the same fixed world payload now exposes the grounded issue-`0x37` pair at `[world+0x29/+0x2d]` the same fixed world payload now exposes the grounded issue-`0x37` pair at `[world+0x29/+0x2d]`,
and the separate six-float economic tuning band `[world+0x0be2..+0x0bf6]` through save one broader fixed-dword finance neighborhood around the absolute-calendar and nearby issue
lanes, and the separate six-float economic tuning band `[world+0x0be2..+0x0bf6]` through save
inspection too, but current atlas evidence still keeps that editor-tuning family separate from inspection too, but current atlas evidence still keeps that editor-tuning family separate from
the company-governance issue lanes; the next shared company-side owning state is rehosted now the company-governance issue lanes; the next shared company-side owning state is rehosted now
too, because save-native company direct records now project into a typed runtime too, because save-native company direct records now project into a typed runtime