Probe save-world economic tuning and chairman power

This commit is contained in:
Jan Petykiewicz 2026-04-17 16:36:48 -07:00
commit a1ace18a0d
5 changed files with 328 additions and 13 deletions

View file

@ -34,12 +34,17 @@ supplies selected company/chairman ids plus the campaign override byte and chair
analysis bytes, and the tagged company / chairman-profile direct-record families now populate analysis bytes, and the tagged company / chairman-profile direct-record families now populate
save-native roster entries for real `.gms` imports and exports. The current raw-save boundary is save-native roster entries for real `.gms` imports and exports. The current raw-save boundary is
narrower now: company/chairman identity, active flags, links, chairman cash, chairman holdings, narrower now: company/chairman identity, active flags, links, chairman cash, chairman holdings,
company debt, and company track-laying capacity are grounded directly from save records, while chairman purchasing power, company debt, and company track-laying capacity are grounded directly
from save records, while
broader company finance/governance scalars and controller-kind reconstruction still remain broader company finance/governance scalars and controller-kind reconstruction still remain
conservative defaults until their raw lanes are pinned more strongly. The offline runtime analysis conservative defaults until their raw lanes are pinned more strongly. The offline runtime analysis
surface also now exposes `runtime inspect-save-company-chairman <save.gms>` for those remaining raw surface also now exposes `runtime inspect-save-company-chairman <save.gms>` for those remaining raw
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, and richer chairman qword cache views. A checked-in 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
block is now probed for its six-float economic tuning band too, but current atlas evidence still
keeps that editor-facing tuning family separate from the governance issue lanes behind investor
confidence and prime-rate math. A checked-in
`EventEffects` export now exists too in `EventEffects` export now exists too in
`artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer now `artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer now
exists beside it in `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`. Recovered exists beside it in `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`. Recovered

View file

@ -85,8 +85,9 @@ pub use smp::{
SmpSaveChairmanRecordAnalysisEntry, SmpSaveCompanyChairmanAnalysisReport, SmpSaveChairmanRecordAnalysisEntry, SmpSaveCompanyChairmanAnalysisReport,
SmpSaveCompanyRecordAnalysisEntry, SmpSaveDwordCandidate, SmpSaveLoadCandidateTableSummary, SmpSaveCompanyRecordAnalysisEntry, SmpSaveDwordCandidate, SmpSaveLoadCandidateTableSummary,
SmpSaveLoadSummary, SmpSaveScalarCandidate, SmpSaveTaggedCollectionHeaderProbe, SmpSaveLoadSummary, SmpSaveScalarCandidate, SmpSaveTaggedCollectionHeaderProbe,
SmpSaveWorldSelectionRoleAnalysis, SmpSaveWorldSelectionRoleAnalysisEntry, SmpSaveWorldEconomicTuningProbe, SmpSaveWorldSelectionRoleAnalysis,
SmpSecondaryVariantProbe, SmpSharedHeader, SmpSpecialConditionEntry, SmpSpecialConditionsProbe, SmpSaveWorldSelectionRoleAnalysisEntry, SmpSecondaryVariantProbe, SmpSharedHeader,
SmpSpecialConditionEntry, SmpSpecialConditionsProbe,
inspect_save_company_and_chairman_analysis_bytes, inspect_save_company_and_chairman_analysis_bytes,
inspect_save_company_and_chairman_analysis_file, inspect_smp_bytes, inspect_smp_file, inspect_save_company_and_chairman_analysis_file, inspect_smp_bytes, inspect_smp_file,
load_save_slice_file, load_save_slice_from_report, load_save_slice_file, load_save_slice_from_report,

View file

@ -98,6 +98,9 @@ const RT3_SAVE_WORLD_BLOCK_SELECTED_CHAIRMAN_PROFILE_ID_RELATIVE_OFFSET: usize =
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;
const RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_MIRROR_RELATIVE_OFFSET: usize = 0x0bda;
const RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_PRIMARY_RELATIVE_OFFSETS: [usize; 6] =
[0x0bde, 0x0be2, 0x0be6, 0x0bea, 0x0bee, 0x0bf2];
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_COUNT: usize = 16; const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_COUNT: usize = 16;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_STRIDE: usize = 9; const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_STRIDE: usize = 9;
const EVENT_RUNTIME_COLLECTION_METADATA_TAG: u16 = 0x4e99; const EVENT_RUNTIME_COLLECTION_METADATA_TAG: u16 = 0x4e99;
@ -1479,6 +1482,20 @@ pub struct SmpSaveWorldSelectionContextProbe {
pub evidence: Vec<String>, pub evidence: Vec<String>,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SmpSaveWorldEconomicTuningProbe {
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 mirror_lane: SmpSaveDwordCandidate,
pub tuning_lanes: 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,
@ -2260,6 +2277,12 @@ pub struct SmpSaveChairmanRecordAnalysisEntry {
#[serde(default)] #[serde(default)]
pub holdings_by_company: BTreeMap<u32, u32>, pub holdings_by_company: BTreeMap<u32, u32>,
#[serde(default)] #[serde(default)]
pub derived_holdings_share_price_total: Option<i64>,
#[serde(default)]
pub derived_net_worth_share_price_total: Option<i64>,
#[serde(default)]
pub derived_cached_purchasing_power_total: Option<i64>,
#[serde(default)]
pub cached_scalar_candidates: Vec<SmpSaveScalarCandidate>, pub cached_scalar_candidates: Vec<SmpSaveScalarCandidate>,
} }
@ -2273,6 +2296,8 @@ pub struct SmpSaveCompanyChairmanAnalysisReport {
#[serde(default)] #[serde(default)]
pub world_selection_context: Option<SmpSaveWorldSelectionRoleAnalysis>, pub world_selection_context: Option<SmpSaveWorldSelectionRoleAnalysis>,
#[serde(default)] #[serde(default)]
pub world_economic_tuning: Option<SmpSaveWorldEconomicTuningProbe>,
#[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>,
@ -2497,7 +2522,7 @@ pub struct SmpPackedProfileWordLane {
pub value_hex: String, pub value_hex: String,
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SmpInspectionReport { pub struct SmpInspectionReport {
pub inspection_mode: String, pub inspection_mode: String,
pub file_extension_hint: Option<String>, pub file_extension_hint: Option<String>,
@ -2518,6 +2543,7 @@ pub struct SmpInspectionReport {
pub rt3_105_post_span_bridge_probe: Option<SmpRt3105PostSpanBridgeProbe>, pub rt3_105_post_span_bridge_probe: Option<SmpRt3105PostSpanBridgeProbe>,
pub rt3_105_save_bridge_payload_probe: Option<SmpRt3105SaveBridgePayloadProbe>, pub rt3_105_save_bridge_payload_probe: Option<SmpRt3105SaveBridgePayloadProbe>,
pub save_world_selection_context_probe: Option<SmpSaveWorldSelectionContextProbe>, pub save_world_selection_context_probe: Option<SmpSaveWorldSelectionContextProbe>,
pub save_world_economic_tuning_probe: Option<SmpSaveWorldEconomicTuningProbe>,
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)]
@ -2720,10 +2746,24 @@ pub fn load_save_slice_from_report(
{ {
notes.push( notes.push(
"Raw save inspection still does not reconstruct every company_roster or chairman_profile_table scalar lane; the grounded package-save path prefers direct-record reconstruction where it can and falls back to selection/header-only context otherwise." "Raw save inspection still does not reconstruct every company_roster or chairman_profile_table scalar lane; the grounded package-save path prefers direct-record reconstruction where it can and falls back to selection/header-only context otherwise."
.to_string(), .to_string(),
); );
} }
} }
if let Some(probe) = &report.save_world_economic_tuning_probe {
notes.push(format!(
"Raw save fixed world block also exposes the six-lane economic tuning float band at file offset 0x{:x} (mirror lane at 0x{:x}).",
probe.tuning_lanes
.first()
.map(|lane| probe.payload_offset + lane.relative_offset)
.unwrap_or(probe.payload_offset),
probe.payload_offset + probe.mirror_lane.relative_offset
));
notes.push(
"Current atlas evidence treats that fixed six-float world tuning band as the editor economic-cost family, not as the company-governance issue table behind investor confidence."
.to_string(),
);
}
if let Some(probe) = &report.save_company_collection_header_probe { if let Some(probe) = &report.save_company_collection_header_probe {
notes.push(format!( notes.push(format!(
"Raw save tagged company header reports live_record_count={} and live_id_bound={} at file offsets 0x{:x}/0x{:x}/0x{:x}.", "Raw save tagged company header reports live_record_count={} and live_id_bound={} at file offsets 0x{:x}/0x{:x}/0x{:x}.",
@ -2797,6 +2837,7 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
) -> Option<SmpSaveCompanyChairmanAnalysisReport> { ) -> Option<SmpSaveCompanyChairmanAnalysisReport> {
let selection_probe = report.save_world_selection_context_probe.as_ref(); let selection_probe = report.save_world_selection_context_probe.as_ref();
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_economic_tuning = report.save_world_economic_tuning_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
@ -2919,6 +2960,13 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
} else { } else {
Vec::new() Vec::new()
}; };
let company_share_prices = company_entries
.iter()
.filter_map(|entry| {
round_f64_to_i64(entry.cached_share_price_f32 as f64)
.map(|share_price| (entry.company_id, share_price))
})
.collect::<BTreeMap<_, _>>();
let chairman_entries = if let Some(header_probe) = chairman_header_probe { let chairman_entries = if let Some(header_probe) = chairman_header_probe {
let record_start_offset = let record_start_offset =
@ -2963,6 +3011,18 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
build_save_qword_candidate(&bytes, record_offset, *relative_offset) build_save_qword_candidate(&bytes, record_offset, *relative_offset)
}) })
.collect::<Option<Vec<_>>>()?; .collect::<Option<Vec<_>>>()?;
let rounded_current_cash = round_f64_to_i64(current_cash)?;
let derived_holdings_share_price_total = derive_chairman_holdings_share_price_total(
&holdings_by_company,
&company_share_prices,
);
let derived_net_worth_share_price_total = derived_holdings_share_price_total
.and_then(|holdings_total| rounded_current_cash.checked_add(holdings_total));
let derived_cached_purchasing_power_total =
derive_chairman_cached_purchasing_power_total(
rounded_current_cash,
&cached_scalar_candidates,
);
entries.push(SmpSaveChairmanRecordAnalysisEntry { entries.push(SmpSaveChairmanRecordAnalysisEntry {
profile_id, profile_id,
name, name,
@ -2970,6 +3030,9 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
current_cash, current_cash,
linked_company_id, linked_company_id,
holdings_by_company, holdings_by_company,
derived_holdings_share_price_total,
derived_net_worth_share_price_total,
derived_cached_purchasing_power_total,
cached_scalar_candidates, cached_scalar_candidates,
}); });
} }
@ -2985,6 +3048,12 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
.to_string(), .to_string(),
); );
} }
if world_economic_tuning.is_some() {
notes.push(
"World analysis now also exports the fixed six-lane economic tuning float block from the same 0x32c8 world payload; current atlas evidence still treats that band as distinct from the issue-0x37 investor-confidence family."
.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(),
@ -2995,11 +3064,19 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
notes.push( 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(), "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(
"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(),
);
} }
if !chairman_entries.is_empty() { if !chairman_entries.is_empty() {
notes.push( notes.push(
"Chairman cached_scalar_candidates expose the adjacent qword band rooted at [profile+0x1e9], now including raw qword hex and signed/f64 views for further purchasing-power analysis.".to_string(), "Chairman cached_scalar_candidates expose the adjacent qword band rooted at [profile+0x1e9], now including raw qword hex and signed/f64 views for further purchasing-power analysis.".to_string(),
); );
notes.push(
"Chairman analysis now also derives one holdings-at-cached-share-price total from the grounded company cached_share_price lane and one strongest-cached purchasing-power total from the nonnegative qword cache band."
.to_string(),
);
} }
Some(SmpSaveCompanyChairmanAnalysisReport { Some(SmpSaveCompanyChairmanAnalysisReport {
@ -3012,6 +3089,7 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
selected_chairman_profile_id: selection_probe selected_chairman_profile_id: selection_probe
.map(|probe| probe.selected_chairman_profile_id), .map(|probe| probe.selected_chairman_profile_id),
world_selection_context, world_selection_context,
world_economic_tuning,
company_entries, company_entries,
chairman_entries, chairman_entries,
notes, notes,
@ -3405,6 +3483,30 @@ fn build_save_qword_candidate(
}) })
} }
fn derive_chairman_holdings_share_price_total(
holdings_by_company: &BTreeMap<u32, u32>,
company_share_prices: &BTreeMap<u32, i64>,
) -> Option<i64> {
let mut total = 0i64;
for (company_id, units) in holdings_by_company {
let share_price = *company_share_prices.get(company_id)?;
total = total.checked_add((*units as i64).checked_mul(share_price)?)?;
}
Some(total)
}
fn derive_chairman_cached_purchasing_power_total(
current_cash: i64,
cached_scalar_candidates: &[SmpSaveScalarCandidate],
) -> Option<i64> {
let strongest_cached_total = cached_scalar_candidates
.iter()
.filter_map(|candidate| round_f64_to_i64(candidate.value_f64))
.filter(|value| *value >= 0)
.max()?;
current_cash.checked_add(strongest_cached_total)
}
fn build_save_world_selection_role_analysis( fn build_save_world_selection_role_analysis(
probe: &SmpSaveWorldSelectionContextProbe, probe: &SmpSaveWorldSelectionContextProbe,
) -> SmpSaveWorldSelectionRoleAnalysis { ) -> SmpSaveWorldSelectionRoleAnalysis {
@ -3490,8 +3592,17 @@ fn parse_save_chairman_profile_table_probe(
bytes, bytes,
record_offset + SAVE_CHAIRMAN_RECORD_CACHE_1_OFFSET, record_offset + SAVE_CHAIRMAN_RECORD_CACHE_1_OFFSET,
)?)?; )?)?;
let cached_scalar_candidates = SAVE_CHAIRMAN_RECORD_CACHE_CANDIDATE_OFFSETS
.iter()
.map(|relative_offset| {
build_save_qword_candidate(bytes, record_offset, *relative_offset)
})
.collect::<Option<Vec<_>>>()?;
let holdings_value_total = cache_0.max(cache_1).max(0); let holdings_value_total = cache_0.max(cache_1).max(0);
let net_worth_total = current_cash.saturating_add(holdings_value_total); let net_worth_total = current_cash.saturating_add(holdings_value_total);
let purchasing_power_total =
derive_chairman_cached_purchasing_power_total(current_cash, &cached_scalar_candidates)
.unwrap_or(net_worth_total);
let mut company_holdings = BTreeMap::new(); let mut company_holdings = BTreeMap::new();
for company_id in 1..=company_id_bound { for company_id in 1..=company_id_bound {
let slot_offset = record_offset let slot_offset = record_offset
@ -3511,7 +3622,7 @@ fn parse_save_chairman_profile_table_probe(
company_holdings, company_holdings,
holdings_value_total, holdings_value_total,
net_worth_total, net_worth_total,
purchasing_power_total: net_worth_total, purchasing_power_total,
}); });
} }
@ -6343,6 +6454,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_economic_tuning_probe = parse_save_world_economic_tuning_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(),
@ -6512,6 +6628,7 @@ fn inspect_bundle_bytes(bytes: &[u8], file_extension_hint: Option<String>) -> Sm
rt3_105_post_span_bridge_probe, rt3_105_post_span_bridge_probe,
rt3_105_save_bridge_payload_probe, rt3_105_save_bridge_payload_probe,
save_world_selection_context_probe, save_world_selection_context_probe,
save_world_economic_tuning_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,
@ -8043,6 +8160,88 @@ fn parse_save_world_selection_context_probe(
None None
} }
fn parse_save_world_economic_tuning_probe(
bytes: &[u8],
file_extension_hint: Option<&str>,
container_profile: Option<&SmpContainerProfile>,
) -> Option<SmpSaveWorldEconomicTuningProbe> {
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 mirror_lane = build_save_dword_candidate(
bytes,
payload_offset,
"economic_tuning_mirror_lane_0",
RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_MIRROR_RELATIVE_OFFSET,
)?;
let tuning_lanes = RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_PRIMARY_RELATIVE_OFFSETS
.iter()
.enumerate()
.map(|(lane_index, relative_offset)| {
build_save_dword_candidate(
bytes,
payload_offset,
&format!("economic_tuning_lane_{lane_index}"),
*relative_offset,
)
})
.collect::<Option<Vec<_>>>()?;
return Some(SmpSaveWorldEconomicTuningProbe {
profile_family: profile.profile_family.clone(),
source_kind: "save-direct-world-block".to_string(),
semantic_family: "scenario-save-world-economic-tuning".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),
mirror_lane,
tuning_lanes,
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"
),
format!(
"mirror lane uses payload +0x{:x} ([world+0x0bde])",
RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_MIRROR_RELATIVE_OFFSET
),
format!(
"primary tuning lanes use payload offsets {} matching the documented [world+0x0be2..+0x0bf6] float block",
RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_PRIMARY_RELATIVE_OFFSETS
.iter()
.map(|offset| format!("0x{offset:x}"))
.collect::<Vec<_>>()
.join(", ")
),
"Current atlas evidence keeps this fixed six-float world tuning band separate from the issue-0x37 investor-confidence lane."
.to_string(),
],
});
}
None
}
fn parse_save_company_collection_header_probe( fn parse_save_company_collection_header_probe(
bytes: &[u8], bytes: &[u8],
file_extension_hint: Option<&str>, file_extension_hint: Option<&str>,
@ -14567,6 +14766,52 @@ mod tests {
assert_eq!(probe.chairman_role_gate_bytes[..4], [2, 1, 0, 2]); assert_eq!(probe.chairman_role_gate_bytes[..4], [2, 1, 0, 2]);
} }
#[test]
fn parses_save_world_economic_tuning_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());
bytes[payload_offset + RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_MIRROR_RELATIVE_OFFSET
..payload_offset + RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_MIRROR_RELATIVE_OFFSET + 4]
.copy_from_slice(&1.5f32.to_bits().to_le_bytes());
for (lane_index, relative_offset) in
RT3_SAVE_WORLD_BLOCK_ECONOMIC_TUNING_PRIMARY_RELATIVE_OFFSETS
.iter()
.copied()
.enumerate()
{
let value = (lane_index as f32) + 10.25f32;
bytes[payload_offset + relative_offset..payload_offset + relative_offset + 4]
.copy_from_slice(&value.to_bits().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_economic_tuning_probe(
&bytes,
Some("gms"),
Some(&SmpContainerProfile {
profile_family: "rt3-105-save-container-v1".to_string(),
profile_evidence: vec![],
is_known_profile: true,
}),
)
.expect("world economic tuning probe should parse");
assert_eq!(probe.chunk_tag_offset, chunk_tag_offset);
assert_eq!(probe.payload_offset, payload_offset);
assert_eq!(probe.mirror_lane.relative_offset_hex, "0xbda");
assert_eq!(probe.mirror_lane.value_f32, 1.5);
assert_eq!(probe.tuning_lanes.len(), 6);
assert_eq!(probe.tuning_lanes[0].relative_offset_hex, "0xbde");
assert_eq!(probe.tuning_lanes[0].value_f32, 10.25);
assert_eq!(probe.tuning_lanes[5].relative_offset_hex, "0xbf2");
assert_eq!(probe.tuning_lanes[5].value_f32, 15.25);
}
#[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(&[]);
@ -14974,13 +15219,14 @@ mod tests {
.copy_from_slice(&0x0000520au32.to_le_bytes()); .copy_from_slice(&0x0000520au32.to_le_bytes());
bytes[close_tag_offset..close_tag_offset + 4].copy_from_slice(&0x0000520bu32.to_le_bytes()); bytes[close_tag_offset..close_tag_offset + 4].copy_from_slice(&0x0000520bu32.to_le_bytes());
for (index, (name, linked, cash, cache0, cache1, holdings)) in [ for (index, (name, linked, cash, cache0, cache1, cache4, holdings)) in [
( (
"Collis Huntington", "Collis Huntington",
1u32, 1u32,
-107644.0f64, -107644.0f64,
252508.0f64, 252508.0f64,
0.0f64, 0.0f64,
0.0f64,
vec![(1u32, 6000u32)], vec![(1u32, 6000u32)],
), ),
( (
@ -14989,6 +15235,7 @@ mod tests {
-382718.0f64, -382718.0f64,
-283562.0f64, -283562.0f64,
822000.0f64, 822000.0f64,
1_392_000.0f64,
vec![(2u32, 9000u32)], vec![(2u32, 9000u32)],
), ),
] ]
@ -15014,6 +15261,8 @@ mod tests {
bytes[record_offset + SAVE_CHAIRMAN_RECORD_CACHE_1_OFFSET bytes[record_offset + SAVE_CHAIRMAN_RECORD_CACHE_1_OFFSET
..record_offset + SAVE_CHAIRMAN_RECORD_CACHE_1_OFFSET + 8] ..record_offset + SAVE_CHAIRMAN_RECORD_CACHE_1_OFFSET + 8]
.copy_from_slice(&cache1.to_le_bytes()); .copy_from_slice(&cache1.to_le_bytes());
bytes[record_offset + 0x211..record_offset + 0x211 + 8]
.copy_from_slice(&cache4.to_le_bytes());
for (company_id, units) in holdings { for (company_id, units) in holdings {
let slot_offset = record_offset let slot_offset = record_offset
+ SAVE_CHAIRMAN_RECORD_HOLDINGS_BASE_OFFSET + SAVE_CHAIRMAN_RECORD_HOLDINGS_BASE_OFFSET
@ -15089,9 +15338,11 @@ mod tests {
assert_eq!(table.entries[0].company_holdings.get(&1), Some(&6000)); assert_eq!(table.entries[0].company_holdings.get(&1), Some(&6000));
assert_eq!(table.entries[0].current_cash, -107644); assert_eq!(table.entries[0].current_cash, -107644);
assert_eq!(table.entries[0].holdings_value_total, 252508); assert_eq!(table.entries[0].holdings_value_total, 252508);
assert_eq!(table.entries[0].purchasing_power_total, 144864);
assert_eq!(table.entries[1].profile_id, 2); assert_eq!(table.entries[1].profile_id, 2);
assert_eq!(table.entries[1].company_holdings.get(&2), Some(&9000)); assert_eq!(table.entries[1].company_holdings.get(&2), Some(&9000));
assert_eq!(table.entries[1].holdings_value_total, 822000); assert_eq!(table.entries[1].holdings_value_total, 822000);
assert_eq!(table.entries[1].purchasing_power_total, 1_009_282);
} }
#[test] #[test]
@ -15149,6 +15400,55 @@ mod tests {
assert_eq!(qword.value_f64, -2458.0); assert_eq!(qword.value_f64, -2458.0);
} }
#[test]
fn derives_chairman_holdings_share_price_total_from_grounded_company_prices() {
let holdings_by_company =
BTreeMap::from([(2u32, 19_000u32), (4u32, 1_000u32), (6u32, 2_000u32)]);
let company_share_prices = BTreeMap::from([(2u32, 66i64), (4u32, 69i64), (6u32, 27i64)]);
let total =
derive_chairman_holdings_share_price_total(&holdings_by_company, &company_share_prices)
.expect("derived holdings total should compute");
assert_eq!(total, 1_377_000);
}
#[test]
fn derives_chairman_cached_purchasing_power_from_strongest_nonnegative_cache() {
let cached_scalar_candidates = vec![
SmpSaveScalarCandidate {
relative_offset: 0x1e9,
relative_offset_hex: "0x1e9".to_string(),
raw_u64: (-343_508.0f64).to_bits(),
raw_u64_hex: format!("0x{:016x}", (-343_508.0f64).to_bits()),
value_i64: round_f64_to_i64(-343_508.0).expect("i64"),
value_f64: -343_508.0,
},
SmpSaveScalarCandidate {
relative_offset: 0x201,
relative_offset_hex: "0x201".to_string(),
raw_u64: 1_386_000.0f64.to_bits(),
raw_u64_hex: format!("0x{:016x}", 1_386_000.0f64.to_bits()),
value_i64: round_f64_to_i64(1_386_000.0).expect("i64"),
value_f64: 1_386_000.0,
},
SmpSaveScalarCandidate {
relative_offset: 0x211,
relative_offset_hex: "0x211".to_string(),
raw_u64: 1_392_000.0f64.to_bits(),
raw_u64_hex: format!("0x{:016x}", 1_392_000.0f64.to_bits()),
value_i64: round_f64_to_i64(1_392_000.0).expect("i64"),
value_f64: 1_392_000.0,
},
];
let total =
derive_chairman_cached_purchasing_power_total(-463_436, &cached_scalar_candidates)
.expect("derived purchasing power should compute");
assert_eq!(total, 928_564);
}
#[test] #[test]
fn classifies_rt3_105_post_span_bridge_variants() { fn classifies_rt3_105_post_span_bridge_variants() {
let base_trailer = SmpRuntimeTrailerBlock { let base_trailer = SmpRuntimeTrailerBlock {

View file

@ -100,12 +100,15 @@ The highest-value next passes are now:
directly from save-side tagged record families, while the fixed save-side `0x32c8` world block directly from save-side tagged record families, while the fixed save-side `0x32c8` world block
still provides selected company/chairman ids plus the grounded campaign-override and still provides selected company/chairman ids plus the grounded campaign-override and
chairman-slot / role-gate analysis bytes; the current raw-save frontier is narrower now: chairman-slot / role-gate analysis bytes; the current raw-save frontier is narrower now:
company/chairman identity, active flags, links, chairman cash, chairman holdings, company debt, company/chairman identity, active flags, links, chairman cash, chairman holdings, chairman
company track-laying capacity, and collection counts are grounded, while broader company purchasing power, company debt, company track-laying capacity, and collection counts are
grounded, while broader company
finance/governance scalar lanes plus controller-kind reconstruction still remain conservative finance/governance scalar lanes plus controller-kind reconstruction still remain conservative
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 fixed-world 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 context, company dword candidate windows, and richer chairman qword cache views
- a checked-in `EventEffects` export now exists at - a checked-in `EventEffects` export now exists at
`artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer `artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer

View file

@ -64,11 +64,17 @@ Implemented today:
chairman slot / role-gate analysis bytes, while the tagged company and chairman/profile chairman slot / role-gate analysis bytes, while the tagged company and chairman/profile
collections now provide save-native roster entries and `observed_entry_count`; raw company debt collections now provide save-native roster entries and `observed_entry_count`; raw company debt
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
`[profile+0x1e9..]` band plus current cash instead of collapsing to plain net worth;
the same fixed world payload now exposes the six-float economic tuning band
`[world+0x0be2..+0x0bf6]` through save inspection too, but current atlas evidence still keeps
that editor-tuning family separate from the company-governance issue lanes;
and `runtime inspect-save-company-chairman <save.gms>` now exposes the remaining raw and `runtime inspect-save-company-chairman <save.gms>` now exposes the remaining raw
company/chairman scalar candidates directly from the rehosted parser, including fixed-world company/chairman scalar candidates directly from the rehosted parser, including fixed-world
chairman slot / role-gate context, company dword candidate windows, and richer chairman qword chairman slot / role-gate context, company dword candidate windows, richer chairman qword
cache views; the remaining raw-save boundary is company-finance/governance scalar depth plus cache views, and derived holdings-at-share-price / cached purchasing-power comparisons; the
controller-kind closure, not roster absence remaining raw-save boundary is company-finance/governance scalar depth plus controller-kind
closure, not roster absence
- a checked-in `EventEffects` export now exists too at - a checked-in `EventEffects` export now exists too at
`artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer `artifacts/exports/rt3-1.06/event-effects-table.json`, and a checked-in semantic closure layer
now exists at `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json` now exists at `artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json`