Load placed-structure side-buffer summaries into save slices

This commit is contained in:
Jan Petykiewicz 2026-04-19 03:18:42 -07:00
commit 777e11e230
5 changed files with 337 additions and 15 deletions

View file

@ -5784,6 +5784,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: None,
notes: vec!["exported for test".to_string()],

View file

@ -1471,6 +1471,35 @@ fn project_save_slice_components(
.to_string(),
);
}
if let Some(summary) = &save_slice.placed_structure_dynamic_side_buffer_summary {
metadata.insert(
"save_slice.placed_structure_dynamic_side_buffer_source_kind".to_string(),
summary.source_kind.clone(),
);
metadata.insert(
"save_slice.placed_structure_dynamic_side_buffer_semantic_family".to_string(),
summary.semantic_family.clone(),
);
metadata.insert(
"save_slice.placed_structure_dynamic_side_buffer_entry_count".to_string(),
summary.observed_entry_count.to_string(),
);
metadata.insert(
"save_slice.placed_structure_dynamic_side_buffer_name_pair_count".to_string(),
summary.unique_embedded_name_pair_count.to_string(),
);
metadata.insert(
"save_slice.placed_structure_dynamic_side_buffer_triplet_overlap_count".to_string(),
summary.triplet_alignment_overlap_count.to_string(),
);
metadata.insert(
"save_slice.placed_structure_dynamic_side_buffer_side_buffer_only_name_pair_count"
.to_string(),
summary
.triplet_alignment_side_buffer_only_name_pair_count
.to_string(),
);
}
let named_locomotive_cost = BTreeMap::new();
let all_cargo_price_override = None;
@ -5540,6 +5569,26 @@ mod tests {
}
}
fn save_placed_structure_dynamic_side_buffer_summary()
-> crate::SmpLoadedPlacedStructureDynamicSideBufferSummary {
crate::SmpLoadedPlacedStructureDynamicSideBufferSummary {
source_kind: "save-placed-structure-dynamic-side-buffer-records".to_string(),
semantic_family: "scenario-save-placed-structure-dynamic-side-buffer-summary"
.to_string(),
observed_entry_count: 118,
owner_shared_dword_hex: "0xff0000ff".to_string(),
unique_embedded_name_pair_count: 9,
decoded_embedded_name_row_count: 118,
first_prefix_leading_dword_hex: "0xff0000ff".to_string(),
first_prefix_trailing_word_hex: "0x0001".to_string(),
first_prefix_separator_byte_hex: "0xff".to_string(),
triplet_alignment_overlap_count: 2,
triplet_alignment_side_buffer_only_name_pair_count: 7,
compact_prefix_pattern_summaries: vec![],
name_pair_summaries: vec![],
}
}
fn save_chairman_profile_table() -> crate::SmpLoadedChairmanProfileTable {
crate::SmpLoadedChairmanProfileTable {
source_kind: "tracked-save-slice-chairman-profile-table".to_string(),
@ -6040,6 +6089,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: None,
notes: vec![],
@ -6088,6 +6138,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: None,
notes: vec![],
@ -6318,6 +6369,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: Some(crate::SmpLoadedSpecialConditionsTable {
source_kind: "save-fixed-special-conditions-range".to_string(),
table_offset: 0x0d64,
@ -6866,6 +6918,7 @@ mod tests {
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: None,
notes: vec![],
@ -6917,6 +6970,9 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: Some(save_placed_structure_collection()),
placed_structure_dynamic_side_buffer_summary: Some(
save_placed_structure_dynamic_side_buffer_summary(),
),
special_conditions_table: None,
event_runtime_collection: None,
notes: vec![],
@ -6961,6 +7017,30 @@ mod tests {
.map(String::as_str),
Some("2")
);
assert_eq!(
import
.state
.metadata
.get("save_slice.placed_structure_dynamic_side_buffer_source_kind")
.map(String::as_str),
Some("save-placed-structure-dynamic-side-buffer-records")
);
assert_eq!(
import
.state
.metadata
.get("save_slice.placed_structure_dynamic_side_buffer_name_pair_count")
.map(String::as_str),
Some("9")
);
assert_eq!(
import
.state
.metadata
.get("save_slice.placed_structure_dynamic_side_buffer_triplet_overlap_count")
.map(String::as_str),
Some("2")
);
}
#[test]
@ -7022,6 +7102,7 @@ mod tests {
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: None,
notes: vec![],
@ -7194,6 +7275,7 @@ mod tests {
entries: Vec::new(),
}),
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: None,
notes: vec![],
@ -7244,6 +7326,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -7377,6 +7460,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -7488,6 +7572,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -7625,6 +7710,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -7723,6 +7809,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -7889,6 +7976,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -8145,6 +8233,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -8236,6 +8325,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -8351,6 +8441,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -8439,6 +8530,7 @@ mod tests {
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -8530,6 +8622,7 @@ mod tests {
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -8634,6 +8727,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -8754,6 +8848,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -8845,6 +8940,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9013,6 +9109,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9126,6 +9223,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9216,6 +9314,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9306,6 +9405,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9470,6 +9570,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9579,6 +9680,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9667,6 +9769,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9765,6 +9868,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9872,6 +9976,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -9987,6 +10092,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -10091,6 +10197,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -10181,6 +10288,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -10341,6 +10449,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -10511,6 +10620,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -10629,6 +10739,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -10728,6 +10839,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -10854,6 +10966,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -10974,6 +11087,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11084,6 +11198,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11190,6 +11305,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11310,6 +11426,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11415,6 +11532,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11502,6 +11620,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11594,6 +11713,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11691,6 +11811,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11788,6 +11909,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -11901,6 +12023,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -12005,6 +12128,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -12156,6 +12280,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -12306,6 +12431,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -12847,6 +12973,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -13042,6 +13169,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -13182,6 +13310,7 @@ mod tests {
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -13321,6 +13450,7 @@ mod tests {
company_roster: Some(save_company_roster()),
chairman_profile_table: Some(save_chairman_profile_table()),
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -13461,6 +13591,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -13585,6 +13716,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -13784,6 +13916,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -13891,6 +14024,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -14000,6 +14134,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -14180,6 +14315,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -14343,6 +14479,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -14447,6 +14584,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -14584,6 +14722,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -14709,6 +14848,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -14908,6 +15048,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
@ -15117,6 +15258,7 @@ mod tests {
company_roster: None,
chairman_profile_table: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),

View file

@ -110,20 +110,21 @@ pub use smp::{
SmpLoadedPackedEventConditionRowSummary, SmpLoadedPackedEventGroupedEffectRowSummary,
SmpLoadedPackedEventNegativeSentinelScopeSummary, SmpLoadedPackedEventRecordSummary,
SmpLoadedPackedEventTextBandSummary, SmpLoadedPlacedStructureCollection,
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,
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,
SmpSaveChairmanRecordAnalysisEntry, SmpSaveCompanyChairmanAnalysisReport,
SmpSaveCompanyRecordAnalysisEntry, SmpSaveDwordCandidate, SmpSaveLoadCandidateTableSummary,
SmpSaveLoadSummary, SmpSavePlacedStructureDynamicSideBufferAlignmentProbe,

View file

@ -2019,6 +2019,26 @@ pub struct SmpSavePlacedStructureDynamicSideBufferProbe {
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpLoadedPlacedStructureDynamicSideBufferSummary {
pub source_kind: String,
pub semantic_family: String,
pub observed_entry_count: u32,
pub owner_shared_dword_hex: String,
pub unique_embedded_name_pair_count: usize,
pub decoded_embedded_name_row_count: usize,
pub first_prefix_leading_dword_hex: String,
pub first_prefix_trailing_word_hex: String,
pub first_prefix_separator_byte_hex: String,
pub triplet_alignment_overlap_count: usize,
pub triplet_alignment_side_buffer_only_name_pair_count: usize,
#[serde(default)]
pub compact_prefix_pattern_summaries:
Vec<SmpSavePlacedStructureDynamicSideBufferPrefixPatternSummary>,
#[serde(default)]
pub name_pair_summaries: Vec<SmpSavePlacedStructureDynamicSideBufferNamePairSummary>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpSavePlacedStructureDynamicSideBufferSampleEntry {
pub sample_index: usize,
@ -4067,6 +4087,9 @@ pub struct SmpLoadedSaveSlice {
pub chairman_profile_table: Option<SmpLoadedChairmanProfileTable>,
#[serde(default)]
pub placed_structure_collection: Option<SmpLoadedPlacedStructureCollection>,
#[serde(default)]
pub placed_structure_dynamic_side_buffer_summary:
Option<SmpLoadedPlacedStructureDynamicSideBufferSummary>,
pub special_conditions_table: Option<SmpLoadedSpecialConditionsTable>,
pub event_runtime_collection: Option<SmpLoadedEventRuntimeCollectionSummary>,
pub notes: Vec<String>,
@ -6886,6 +6909,35 @@ fn derive_loaded_placed_structure_collection_from_probe(
}
}
fn derive_loaded_placed_structure_dynamic_side_buffer_summary(
probe: &SmpSavePlacedStructureDynamicSideBufferProbe,
alignment: Option<&SmpSavePlacedStructureDynamicSideBufferAlignmentProbe>,
) -> SmpLoadedPlacedStructureDynamicSideBufferSummary {
SmpLoadedPlacedStructureDynamicSideBufferSummary {
source_kind: probe.source_kind.clone(),
semantic_family: "scenario-save-placed-structure-dynamic-side-buffer-summary".to_string(),
observed_entry_count: probe.live_record_count,
owner_shared_dword_hex: probe.owner_shared_dword_hex.clone(),
unique_embedded_name_pair_count: probe.unique_embedded_name_pair_count,
decoded_embedded_name_row_count: probe.decoded_embedded_name_row_count,
first_prefix_leading_dword_hex: probe.prefix_leading_dword_hex.clone(),
first_prefix_trailing_word_hex: probe.prefix_trailing_word_hex.clone(),
first_prefix_separator_byte_hex: probe.prefix_separator_byte_hex.clone(),
triplet_alignment_overlap_count: alignment
.map(|alignment| alignment.overlapping_name_pair_count)
.unwrap_or_default(),
triplet_alignment_side_buffer_only_name_pair_count: alignment
.map(|alignment| {
alignment
.unique_side_buffer_name_pair_count
.saturating_sub(alignment.overlapping_name_pair_count)
})
.unwrap_or_default(),
compact_prefix_pattern_summaries: probe.compact_prefix_pattern_summaries.clone(),
name_pair_summaries: probe.name_pair_summaries.clone(),
}
}
pub fn load_save_slice_file(path: &Path) -> Result<SmpLoadedSaveSlice, Box<dyn std::error::Error>> {
let inspection = inspect_smp_file(path)?;
load_save_slice_from_report(&inspection)
@ -7049,6 +7101,22 @@ pub fn load_save_slice_from_report(
let placed_structure_dynamic_side_buffer_probe = report
.save_placed_structure_dynamic_side_buffer_probe
.clone();
let placed_structure_dynamic_side_buffer_alignment = report
.save_placed_structure_dynamic_side_buffer_probe
.as_ref()
.zip(report.save_placed_structure_record_triplet_probe.as_ref())
.map(|(side_buffer, triplets)| {
summarize_placed_structure_dynamic_side_buffer_alignment(side_buffer, triplets)
});
let placed_structure_dynamic_side_buffer_summary = report
.save_placed_structure_dynamic_side_buffer_probe
.as_ref()
.map(|probe| {
derive_loaded_placed_structure_dynamic_side_buffer_summary(
probe,
placed_structure_dynamic_side_buffer_alignment.as_ref(),
)
});
let mut notes = summary.notes.clone();
if let Some(probe) = &report.save_world_selection_context_probe {
notes.push(format!(
@ -7285,6 +7353,14 @@ pub fn load_save_slice_from_report(
);
}
}
if let Some(summary) = &placed_structure_dynamic_side_buffer_summary {
notes.push(format!(
"Save-slice projection now also carries the placed-structure dynamic side-buffer summary with {} decoded name rows, {} unique name pairs, and {} overlapping triplet name pairs.",
summary.decoded_embedded_name_row_count,
summary.unique_embedded_name_pair_count,
summary.triplet_alignment_overlap_count
));
}
if let Some(roster) = &report.save_company_roster_probe {
notes.push(format!(
"Raw save inspection reconstructed {} company direct records from the tagged company collection.",
@ -7317,6 +7393,7 @@ pub fn load_save_slice_from_report(
company_roster,
chairman_profile_table,
placed_structure_collection,
placed_structure_dynamic_side_buffer_summary,
special_conditions_table,
event_runtime_collection: report.event_runtime_collection_summary.clone(),
notes,
@ -25446,7 +25523,86 @@ mod tests {
],
evidence: vec![],
});
report.save_placed_structure_dynamic_side_buffer_probe =
Some(SmpSavePlacedStructureDynamicSideBufferProbe {
profile_family: "rt3-classic-save-container-v1".to_string(),
source_kind: "save-placed-structure-dynamic-side-buffer-records".to_string(),
semantic_family: "scenario-save-placed-structure-dynamic-side-buffer-records"
.to_string(),
metadata_tag_offset: 0x3800,
records_tag_offset: 0x3900,
close_tag_offset: 0x3d00,
records_span_len: 0x400,
direct_record_stride: 6,
direct_record_stride_hex: "0x6".to_string(),
live_id_bound: 0x80,
live_id_bound_hex: "0x00000080".to_string(),
live_record_count: 118,
live_record_count_hex: "0x00000076".to_string(),
owner_shared_dword: 0xff0000ff,
owner_shared_dword_hex: "0xff0000ff".to_string(),
owner_shared_dword_relative_offset: 0,
owner_shared_dword_matches_first_compact_prefix_leading_dword: true,
first_record_child_count_after_owner_shared: Some(1),
first_record_child_count_after_owner_shared_hex: Some("0x0001".to_string()),
first_record_saved_primary_child_byte_after_owner_shared: Some(0xff),
first_record_saved_primary_child_byte_after_owner_shared_hex: Some(
"0xff".to_string(),
),
first_record_first_name_tag_relative_offset_after_owner_shared: Some(3),
prefix_leading_dword: 0xff0000ff,
prefix_leading_dword_hex: "0xff0000ff".to_string(),
prefix_trailing_word: 1,
prefix_trailing_word_hex: "0x0001".to_string(),
prefix_separator_byte: 0xff,
prefix_separator_byte_hex: "0xff".to_string(),
first_embedded_name_tag_relative_offset: 3,
embedded_name_tag_count: 118,
decoded_embedded_name_row_count: 118,
decoded_embedded_name_row_with_tertiary_name_count: 0,
unique_compact_prefix_pattern_count: 4,
prefix_leading_dword_matching_embedded_profile_tag_count: 0,
unique_embedded_name_pair_count: 9,
first_embedded_primary_name: Some("StationA".to_string()),
first_embedded_secondary_name: Some("StationSetA".to_string()),
first_embedded_tertiary_name: None,
embedded_name_row_samples: vec![],
compact_prefix_pattern_summaries: vec![
SmpSavePlacedStructureDynamicSideBufferPrefixPatternSummary {
prefix_leading_dword: 0xff0000ff,
prefix_leading_dword_hex: "0xff0000ff".to_string(),
prefix_trailing_word: 1,
prefix_trailing_word_hex: "0x0001".to_string(),
prefix_separator_byte: 0xff,
prefix_separator_byte_hex: "0xff".to_string(),
count: 62,
first_name_tag_relative_offset: 3,
prefix_leading_dword_matches_embedded_profile_tag: false,
section_like_primary_name_count: 12,
cap_like_primary_name_count: 21,
other_primary_name_count: 29,
first_primary_name: Some("StationA".to_string()),
first_secondary_name: Some("StationSetA".to_string()),
},
],
name_pair_summaries: vec![SmpSavePlacedStructureDynamicSideBufferNamePairSummary {
primary_name: "StationA".to_string(),
secondary_name: "StationSetA".to_string(),
count: 14,
first_name_tag_relative_offset: 3,
unique_compact_prefix_pattern_count: 1,
dominant_prefix_leading_dword: 0xff0000ff,
dominant_prefix_leading_dword_hex: "0xff0000ff".to_string(),
dominant_prefix_trailing_word: 1,
dominant_prefix_trailing_word_hex: "0x0001".to_string(),
dominant_prefix_separator_byte: 0xff,
dominant_prefix_separator_byte_hex: "0xff".to_string(),
dominant_prefix_count: 14,
}],
payload_envelope_summary: None,
live_entry_prelude_summary: None,
evidence: vec![],
});
let slice = load_save_slice_from_report(&report).expect("classic save slice");
let collection = slice
.placed_structure_collection
@ -25459,10 +25615,28 @@ mod tests {
assert_eq!(collection.entries[0].primary_name, "FarmCorn");
assert_eq!(collection.entries[0].farm_growth_stage_index, Some(4));
assert_eq!(collection.entries[1].profile_companion_byte_u8, Some(7));
let side_buffer_summary = slice
.placed_structure_dynamic_side_buffer_summary
.expect("side-buffer summary should project");
assert_eq!(
side_buffer_summary.source_kind,
"save-placed-structure-dynamic-side-buffer-records"
);
assert_eq!(side_buffer_summary.observed_entry_count, 118);
assert_eq!(side_buffer_summary.unique_embedded_name_pair_count, 9);
assert_eq!(side_buffer_summary.triplet_alignment_overlap_count, 1);
assert_eq!(
side_buffer_summary.triplet_alignment_side_buffer_only_name_pair_count,
0
);
assert!(slice.notes.iter().any(|line| {
line.contains("placed-structure triplet rows as first-class context")
&& line.contains("2")
}));
assert!(slice.notes.iter().any(|line| {
line.contains("placed-structure dynamic side-buffer summary")
&& line.contains("118 decoded name rows")
}));
}
#[test]