Load region triplets into save slices

This commit is contained in:
Jan Petykiewicz 2026-04-19 03:26:50 -07:00
commit 9a4dd5d8d4
5 changed files with 478 additions and 16 deletions

View file

@ -5783,6 +5783,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,

View file

@ -54,7 +54,7 @@ pub struct RuntimeSaveSliceDocumentSource {
pub notes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RuntimeSaveSliceDocument {
pub format_version: u32,
pub save_slice_id: String,
@ -1500,6 +1500,53 @@ fn project_save_slice_components(
.to_string(),
);
}
if let Some(collection) = &save_slice.region_collection {
metadata.insert(
"save_slice.region_collection_source_kind".to_string(),
collection.source_kind.clone(),
);
metadata.insert(
"save_slice.region_collection_semantic_family".to_string(),
collection.semantic_family.clone(),
);
metadata.insert(
"save_slice.region_collection_entry_count".to_string(),
collection.observed_entry_count.to_string(),
);
metadata.insert(
"save_slice.region_collection_profile_entry_count".to_string(),
collection
.entries
.iter()
.map(|entry| {
entry
.profile_collection
.as_ref()
.map(|collection| collection.entries.len())
.unwrap_or_default()
})
.sum::<usize>()
.to_string(),
);
metadata.insert(
"save_slice.region_collection_nonzero_prefix_count".to_string(),
collection
.entries
.iter()
.filter(|entry| entry.pre_name_prefix_len != 0)
.count()
.to_string(),
);
metadata.insert(
"save_slice.region_collection_nonzero_reserved_policy_count".to_string(),
collection
.entries
.iter()
.filter(|entry| entry.policy_reserved_dwords.iter().any(|raw| *raw != 0))
.count()
.to_string(),
);
}
let named_locomotive_cost = BTreeMap::new();
let all_cargo_price_override = None;
@ -5589,6 +5636,69 @@ mod tests {
}
}
fn save_region_collection() -> crate::SmpLoadedRegionCollection {
crate::SmpLoadedRegionCollection {
source_kind: "save-region-record-triplets".to_string(),
semantic_family: "scenario-save-region-triplet-collection".to_string(),
observed_entry_count: 2,
entries: vec![
crate::SmpLoadedRegionEntry {
record_index: 0,
name: "Marker09".to_string(),
pre_name_prefix_len: 0,
policy_leading_f32_0: 368.0,
policy_leading_f32_1: 0.0,
policy_leading_f32_2: 92.0,
policy_reserved_dwords: vec![0, 0, 0],
policy_trailing_word: 1,
policy_trailing_word_hex: "0x0001".to_string(),
profile_collection: Some(crate::SmpLoadedRegionProfileCollection {
direct_collection_flag: 1,
entry_stride: 0x22,
live_id_bound: 18,
live_record_count: 17,
trailing_padding_len: 2,
entries: vec![
crate::SmpLoadedRegionProfileEntry {
entry_index: 0,
name: "House".to_string(),
trailing_weight_f32: 0.2,
},
crate::SmpLoadedRegionProfileEntry {
entry_index: 1,
name: "Farm Corn".to_string(),
trailing_weight_f32: 0.2,
},
],
}),
},
crate::SmpLoadedRegionEntry {
record_index: 1,
name: "Marker10".to_string(),
pre_name_prefix_len: 8,
policy_leading_f32_0: 552.0,
policy_leading_f32_1: 0.0,
policy_leading_f32_2: 276.0,
policy_reserved_dwords: vec![0, 4, 0],
policy_trailing_word: 1,
policy_trailing_word_hex: "0x0001".to_string(),
profile_collection: Some(crate::SmpLoadedRegionProfileCollection {
direct_collection_flag: 1,
entry_stride: 0x22,
live_id_bound: 26,
live_record_count: 24,
trailing_padding_len: 0,
entries: vec![crate::SmpLoadedRegionProfileEntry {
entry_index: 0,
name: "Farm Corn".to_string(),
trailing_weight_f32: 0.2,
}],
}),
},
],
}
}
fn save_chairman_profile_table() -> crate::SmpLoadedChairmanProfileTable {
crate::SmpLoadedChairmanProfileTable {
source_kind: "tracked-save-slice-chairman-profile-table".to_string(),
@ -6088,6 +6198,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -6137,6 +6248,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -6368,6 +6480,7 @@ mod tests {
}),
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: Some(crate::SmpLoadedSpecialConditionsTable {
@ -6917,6 +7030,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -6969,6 +7083,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: Some(save_region_collection()),
placed_structure_collection: Some(save_placed_structure_collection()),
placed_structure_dynamic_side_buffer_summary: Some(
save_placed_structure_dynamic_side_buffer_summary(),
@ -6985,6 +7100,46 @@ mod tests {
)
.expect("save slice should project");
assert_eq!(
import
.state
.metadata
.get("save_slice.region_collection_source_kind")
.map(String::as_str),
Some("save-region-record-triplets")
);
assert_eq!(
import
.state
.metadata
.get("save_slice.region_collection_entry_count")
.map(String::as_str),
Some("2")
);
assert_eq!(
import
.state
.metadata
.get("save_slice.region_collection_profile_entry_count")
.map(String::as_str),
Some("3")
);
assert_eq!(
import
.state
.metadata
.get("save_slice.region_collection_nonzero_prefix_count")
.map(String::as_str),
Some("1")
);
assert_eq!(
import
.state
.metadata
.get("save_slice.region_collection_nonzero_reserved_policy_count")
.map(String::as_str),
Some("1")
);
assert_eq!(
import
.state
@ -7101,6 +7256,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -7274,6 +7430,7 @@ mod tests {
selected_chairman_profile_id: Some(1),
entries: Vec::new(),
}),
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -7325,6 +7482,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -7459,6 +7617,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -7571,6 +7730,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -7709,6 +7869,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -7808,6 +7969,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -7975,6 +8137,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -8232,6 +8395,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -8324,6 +8488,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -8440,6 +8605,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -8529,6 +8695,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -8621,6 +8788,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -8726,6 +8894,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -8847,6 +9016,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -8939,6 +9109,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9108,6 +9279,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9222,6 +9394,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9313,6 +9486,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9404,6 +9578,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9569,6 +9744,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9679,6 +9855,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9768,6 +9945,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9867,6 +10045,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -9975,6 +10154,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -10091,6 +10271,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -10196,6 +10377,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -10287,6 +10469,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -10448,6 +10631,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -10619,6 +10803,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -10738,6 +10923,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -10838,6 +11024,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -10965,6 +11152,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11086,6 +11274,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11197,6 +11386,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11304,6 +11494,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11425,6 +11616,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11531,6 +11723,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11619,6 +11812,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11712,6 +11906,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11810,6 +12005,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -11908,6 +12104,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -12022,6 +12219,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -12127,6 +12325,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -12279,6 +12478,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -12430,6 +12630,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -12972,6 +13173,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -13168,6 +13370,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -13309,6 +13512,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -13449,6 +13653,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -13590,6 +13795,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -13715,6 +13921,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -13915,6 +14122,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -14023,6 +14231,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -14133,6 +14342,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -14314,6 +14524,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -14478,6 +14689,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -14583,6 +14795,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -14721,6 +14934,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -14847,6 +15061,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -15047,6 +15262,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
@ -15257,6 +15473,7 @@ mod tests {
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,

View file

@ -111,20 +111,21 @@ pub use smp::{
SmpLoadedPackedEventNegativeSentinelScopeSummary, SmpLoadedPackedEventRecordSummary,
SmpLoadedPackedEventTextBandSummary, SmpLoadedPlacedStructureCollection,
SmpLoadedPlacedStructureDynamicSideBufferSummary, SmpLoadedPlacedStructureEntry,
SmpLoadedProfile, SmpLoadedSaveSlice, SmpLoadedSpecialConditionsTable,
SmpLoadedWorldEconomicTuningState, SmpLoadedWorldFinanceNeighborhoodState,
SmpLoadedWorldIssue37State, SmpLocomotivePolicyFieldObservation,
SmpLocomotivePolicyFloatAlignmentCandidate, SmpLocomotivePolicyNeighborhoodProbe,
SmpPackedProfileWordLane, SmpPeriodicCompanyServiceTraceReport,
SmpPostSpecialConditionsScalarLane, SmpPostSpecialConditionsScalarProbe,
SmpPostTextFieldNeighborhoodProbe, SmpPostTextFloatAlignmentCandidate,
SmpPostTextGroundedFieldObservation, SmpPreRecipeScalarPlateauLane,
SmpPreRecipeScalarPlateauProbe, SmpPreamble, SmpPreambleWord, SmpRecipeBookLineSummary,
SmpRecipeBookSummaryBook, SmpRecipeBookSummaryProbe, SmpRegionServiceTraceReport,
SmpRt3105PackedProfileBlock, SmpRt3105PackedProfileProbe, SmpRt3105PostSpanBridgeProbe,
SmpRt3105SaveBridgePayloadProbe, SmpRt3105SaveNameTableEntry, SmpRt3105SaveNameTableProbe,
SmpRuntimeAnchorCycleBlock, SmpRuntimePostSpanHeaderCandidate, SmpRuntimePostSpanProbe,
SmpRuntimeTrailerBlock, SmpSaveAnchorRunBlock, SmpSaveBootstrapBlock,
SmpLoadedProfile, SmpLoadedRegionCollection, SmpLoadedRegionEntry,
SmpLoadedRegionProfileCollection, SmpLoadedRegionProfileEntry, SmpLoadedSaveSlice,
SmpLoadedSpecialConditionsTable, SmpLoadedWorldEconomicTuningState,
SmpLoadedWorldFinanceNeighborhoodState, SmpLoadedWorldIssue37State,
SmpLocomotivePolicyFieldObservation, SmpLocomotivePolicyFloatAlignmentCandidate,
SmpLocomotivePolicyNeighborhoodProbe, SmpPackedProfileWordLane,
SmpPeriodicCompanyServiceTraceReport, SmpPostSpecialConditionsScalarLane,
SmpPostSpecialConditionsScalarProbe, SmpPostTextFieldNeighborhoodProbe,
SmpPostTextFloatAlignmentCandidate, SmpPostTextGroundedFieldObservation,
SmpPreRecipeScalarPlateauLane, SmpPreRecipeScalarPlateauProbe, SmpPreamble, SmpPreambleWord,
SmpRecipeBookLineSummary, SmpRecipeBookSummaryBook, SmpRecipeBookSummaryProbe,
SmpRegionServiceTraceReport, SmpRt3105PackedProfileBlock, SmpRt3105PackedProfileProbe,
SmpRt3105PostSpanBridgeProbe, SmpRt3105SaveBridgePayloadProbe, SmpRt3105SaveNameTableEntry,
SmpRt3105SaveNameTableProbe, SmpRuntimeAnchorCycleBlock, SmpRuntimePostSpanHeaderCandidate,
SmpRuntimePostSpanProbe, SmpRuntimeTrailerBlock, SmpSaveAnchorRunBlock, SmpSaveBootstrapBlock,
SmpSaveChairmanRecordAnalysisEntry, SmpSaveCompanyChairmanAnalysisReport,
SmpSaveCompanyRecordAnalysisEntry, SmpSaveDwordCandidate, SmpSaveLoadCandidateTableSummary,
SmpSaveLoadSummary, SmpSavePlacedStructureDynamicSideBufferAlignmentProbe,

View file

@ -1749,6 +1749,49 @@ pub struct SmpSaveRegionRecordTripletProbe {
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SmpLoadedRegionProfileEntry {
pub entry_index: usize,
pub name: String,
pub trailing_weight_f32: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SmpLoadedRegionProfileCollection {
pub direct_collection_flag: u32,
pub entry_stride: u32,
pub live_id_bound: u32,
pub live_record_count: u32,
pub trailing_padding_len: usize,
#[serde(default)]
pub entries: Vec<SmpLoadedRegionProfileEntry>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SmpLoadedRegionEntry {
pub record_index: usize,
pub name: String,
pub pre_name_prefix_len: usize,
pub policy_leading_f32_0: f32,
pub policy_leading_f32_1: f32,
pub policy_leading_f32_2: f32,
#[serde(default)]
pub policy_reserved_dwords: Vec<u32>,
pub policy_trailing_word: u16,
pub policy_trailing_word_hex: String,
#[serde(default)]
pub profile_collection: Option<SmpLoadedRegionProfileCollection>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SmpLoadedRegionCollection {
pub source_kind: String,
pub semantic_family: String,
pub observed_entry_count: usize,
#[serde(default)]
pub entries: Vec<SmpLoadedRegionEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpSaveRegionQueuedNoticeRecordEntryProbe {
pub node_base_offset: usize,
@ -4058,7 +4101,7 @@ enum RealGroupedTargetSubject {
WholeGame,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SmpLoadedSaveSlice {
pub file_extension_hint: Option<String>,
pub container_profile_family: Option<String>,
@ -4086,6 +4129,8 @@ pub struct SmpLoadedSaveSlice {
#[serde(default)]
pub chairman_profile_table: Option<SmpLoadedChairmanProfileTable>,
#[serde(default)]
pub region_collection: Option<SmpLoadedRegionCollection>,
#[serde(default)]
pub placed_structure_collection: Option<SmpLoadedPlacedStructureCollection>,
#[serde(default)]
pub placed_structure_dynamic_side_buffer_summary:
@ -6909,6 +6954,49 @@ fn derive_loaded_placed_structure_collection_from_probe(
}
}
fn derive_loaded_region_collection_from_probe(
probe: &SmpSaveRegionRecordTripletProbe,
) -> SmpLoadedRegionCollection {
SmpLoadedRegionCollection {
source_kind: probe.source_kind.clone(),
semantic_family: "scenario-save-region-triplet-collection".to_string(),
observed_entry_count: probe.record_count,
entries: probe
.entries
.iter()
.map(|entry| SmpLoadedRegionEntry {
record_index: entry.record_index,
name: entry.name.clone(),
pre_name_prefix_len: entry.pre_name_prefix_len,
policy_leading_f32_0: entry.policy_leading_f32_0,
policy_leading_f32_1: entry.policy_leading_f32_1,
policy_leading_f32_2: entry.policy_leading_f32_2,
policy_reserved_dwords: entry.policy_reserved_dwords.clone(),
policy_trailing_word: entry.policy_trailing_word,
policy_trailing_word_hex: entry.policy_trailing_word_hex.clone(),
profile_collection: entry.profile_collection.as_ref().map(|collection| {
SmpLoadedRegionProfileCollection {
direct_collection_flag: collection.direct_collection_flag,
entry_stride: collection.entry_stride,
live_id_bound: collection.live_id_bound,
live_record_count: collection.live_record_count,
trailing_padding_len: collection.trailing_padding_len,
entries: collection
.entries
.iter()
.map(|entry| SmpLoadedRegionProfileEntry {
entry_index: entry.entry_index,
name: entry.name.clone(),
trailing_weight_f32: entry.trailing_weight_f32,
})
.collect(),
}
}),
})
.collect(),
}
}
fn derive_loaded_placed_structure_dynamic_side_buffer_summary(
probe: &SmpSavePlacedStructureDynamicSideBufferProbe,
alignment: Option<&SmpSavePlacedStructureDynamicSideBufferAlignmentProbe>,
@ -7082,6 +7170,10 @@ pub fn load_save_slice_from_report(
)
})
});
let region_collection = report
.save_region_record_triplet_probe
.as_ref()
.map(derive_loaded_region_collection_from_probe);
let placed_structure_collection = report
.save_placed_structure_record_triplet_probe
.as_ref()
@ -7246,6 +7338,36 @@ pub fn load_save_slice_from_report(
})
));
}
if let Some(collection) = &region_collection {
let total_profile_rows = collection
.entries
.iter()
.map(|entry| {
entry
.profile_collection
.as_ref()
.map(|collection| collection.entries.len())
.unwrap_or_default()
})
.sum::<usize>();
let nonzero_prefix_count = collection
.entries
.iter()
.filter(|entry| entry.pre_name_prefix_len != 0)
.count();
let nonzero_reserved_count = collection
.entries
.iter()
.filter(|entry| entry.policy_reserved_dwords.iter().any(|raw| *raw != 0))
.count();
notes.push(format!(
"Save-slice projection now carries {} loaded region triplet rows as first-class context, with {} embedded profile rows, {} rows with nonzero pre-name prefixes, and {} rows with nonzero reserved policy dwords.",
collection.observed_entry_count,
total_profile_rows,
nonzero_prefix_count,
nonzero_reserved_count
));
}
if let Some(probe) = &report.save_placed_structure_collection_header_probe {
notes.push(format!(
"Raw save tagged placed-structure header reports live_record_count={} and live_id_bound={} with serialized stride hint 0x{:x} at file offsets 0x{:x}/0x{:x}/0x{:x}.",
@ -7392,6 +7514,7 @@ pub fn load_save_slice_from_report(
world_locomotive_policy_state,
company_roster,
chairman_profile_table,
region_collection,
placed_structure_collection,
placed_structure_dynamic_side_buffer_summary,
special_conditions_table,
@ -25441,6 +25564,100 @@ mod tests {
None,
None,
);
report.save_region_record_triplet_probe = Some(SmpSaveRegionRecordTripletProbe {
profile_family: "rt3-classic-save-container-v1".to_string(),
source_kind: "save-region-record-triplets".to_string(),
semantic_family: "scenario-save-region-record-triplets".to_string(),
records_tag_offset: 0x3400,
close_tag_offset: 0x3500,
record_count: 2,
entries: vec![
SmpSaveRegionRecordTripletEntryProbe {
record_index: 0,
name: "Marker09".to_string(),
record_payload_relative_offset: 0,
record_payload_relative_offset_hex: "0x0".to_string(),
name_tag_relative_offset: 0,
policy_tag_relative_offset: 0x10,
profile_tag_relative_offset: 0x2e,
pre_name_prefix_len: 0,
pre_name_prefix_hex_bytes: Vec::new(),
pre_name_prefix_dword_candidates: Vec::new(),
policy_chunk_len: 0x1a,
profile_chunk_len: 0x40,
policy_leading_f32_0: 368.0,
policy_leading_f32_1: 0.0,
policy_leading_f32_2: 92.0,
policy_reserved_dwords: vec![0, 0, 0],
policy_reserved_dword_candidates: Vec::new(),
policy_trailing_word: 1,
policy_trailing_word_hex: "0x0001".to_string(),
profile_collection: Some(SmpSaveRegionProfileCollectionProbe {
direct_collection_flag: 1,
entry_stride: 0x22,
live_id_bound: 18,
live_record_count: 17,
entry_start_relative_offset: 0x4d,
trailing_padding_len: 2,
entries: vec![
SmpSaveRegionProfileEntryProbe {
entry_index: 0,
row_relative_offset: 0x4d,
name: "House".to_string(),
trailing_weight_f32: 0.2,
},
SmpSaveRegionProfileEntryProbe {
entry_index: 1,
row_relative_offset: 0x6f,
name: "Farm Corn".to_string(),
trailing_weight_f32: 0.2,
},
],
}),
},
SmpSaveRegionRecordTripletEntryProbe {
record_index: 1,
name: "Marker10".to_string(),
record_payload_relative_offset: 0x6e,
record_payload_relative_offset_hex: "0x6e".to_string(),
name_tag_relative_offset: 0x76,
policy_tag_relative_offset: 0x86,
profile_tag_relative_offset: 0xa4,
pre_name_prefix_len: 8,
pre_name_prefix_hex_bytes: vec![
"0xaa".to_string(),
"0xbb".to_string(),
"0xcc".to_string(),
"0xdd".to_string(),
],
pre_name_prefix_dword_candidates: Vec::new(),
policy_chunk_len: 0x1a,
profile_chunk_len: 0x20,
policy_leading_f32_0: 552.0,
policy_leading_f32_1: 0.0,
policy_leading_f32_2: 276.0,
policy_reserved_dwords: vec![0, 4, 0],
policy_reserved_dword_candidates: Vec::new(),
policy_trailing_word: 1,
policy_trailing_word_hex: "0x0001".to_string(),
profile_collection: Some(SmpSaveRegionProfileCollectionProbe {
direct_collection_flag: 1,
entry_stride: 0x22,
live_id_bound: 26,
live_record_count: 24,
entry_start_relative_offset: 0x50,
trailing_padding_len: 0,
entries: vec![SmpSaveRegionProfileEntryProbe {
entry_index: 0,
row_relative_offset: 0x50,
name: "Farm Corn".to_string(),
trailing_weight_f32: 0.2,
}],
}),
},
],
evidence: vec![],
});
report.save_placed_structure_record_triplet_probe =
Some(SmpSavePlacedStructureRecordTripletProbe {
profile_family: "rt3-classic-save-container-v1".to_string(),
@ -25604,6 +25821,24 @@ mod tests {
evidence: vec![],
});
let slice = load_save_slice_from_report(&report).expect("classic save slice");
let region_collection = slice
.region_collection
.expect("region collection should project");
assert_eq!(region_collection.source_kind, "save-region-record-triplets");
assert_eq!(region_collection.observed_entry_count, 2);
assert_eq!(region_collection.entries[0].name, "Marker09");
assert_eq!(region_collection.entries[1].pre_name_prefix_len, 8);
assert_eq!(
region_collection.entries[1].policy_reserved_dwords,
vec![0, 4, 0]
);
assert_eq!(
region_collection.entries[0]
.profile_collection
.as_ref()
.map(|collection| collection.entries.len()),
Some(2)
);
let collection = slice
.placed_structure_collection
.expect("placed structure collection should project");
@ -25629,6 +25864,10 @@ mod tests {
side_buffer_summary.triplet_alignment_side_buffer_only_name_pair_count,
0
);
assert!(slice.notes.iter().any(|line| {
line.contains("loaded region triplet rows as first-class context")
&& line.contains("3 embedded profile rows")
}));
assert!(slice.notes.iter().any(|line| {
line.contains("placed-structure triplet rows as first-class context")
&& line.contains("2")

View file

@ -84,6 +84,10 @@ Working rule:
model as first-class `placed_structure_dynamic_side_buffer_summary` context, carrying the
grounded owner-shared dword, compact-prefix summaries, name-pair summaries, and overlap counts
against the triplet corpus instead of remaining trace-only evidence
- the save-side `0x55f1/0x55f2/0x55f3` region triplet seam is now also loaded into the
save-slice model as first-class `region_collection` context, carrying region names, the
grounded policy lanes, reserved-policy dwords, and embedded profile rows instead of leaving
region triplets inspection-only
- Direct disassembly now narrows that acquisition strip further:
- `0x004014b0` scans the live placed-structure collection at `0x0062b26c`
- `0x0041f6e0 -> 0x0042b2d0` is the center-cell token gate over the current region