Ground farm growth buckets in placed-structure saves
This commit is contained in:
parent
f13d710556
commit
4cec28e092
2 changed files with 60 additions and 4 deletions
|
|
@ -1726,6 +1726,8 @@ pub struct SmpSavePlacedStructureRecordTripletEntryProbe {
|
|||
pub profile_payload_dword: u32,
|
||||
pub profile_payload_dword_hex: String,
|
||||
pub profile_sentinel_i32: i32,
|
||||
pub profile_status_kind: String,
|
||||
pub farm_growth_stage_index: Option<u8>,
|
||||
pub profile_close_marker: u32,
|
||||
pub profile_close_marker_hex: String,
|
||||
}
|
||||
|
|
@ -3287,7 +3289,7 @@ pub fn load_save_slice_from_report(
|
|||
}
|
||||
if let Some(probe) = &report.save_placed_structure_record_triplet_probe {
|
||||
notes.push(format!(
|
||||
"Raw save tagged placed-structure records also expose {} repeated 0x55f1/0x55f2/0x55f3 triplets; first stems={:?}/{:?}, first policy lanes=({:.3}, {:.3}, {:.3}, {:.3}, {:.3}), first footer payload={}.",
|
||||
"Raw save tagged placed-structure records also expose {} repeated 0x55f1/0x55f2/0x55f3 triplets; first stems={:?}/{:?}, first policy lanes=({:.3}, {:.3}, {:.3}, {:.3}, {:.3}), first footer payload={}, first footer status kind={:?}.",
|
||||
probe.record_count,
|
||||
probe.entries.first().map(|entry| entry.primary_name.as_str()),
|
||||
probe.entries.first().map(|entry| entry.secondary_name.as_str()),
|
||||
|
|
@ -3296,7 +3298,8 @@ pub fn load_save_slice_from_report(
|
|||
probe.entries.first().map(|entry| entry.policy_f32_lane_2).unwrap_or_default(),
|
||||
probe.entries.first().map(|entry| entry.policy_f32_lane_3).unwrap_or_default(),
|
||||
probe.entries.first().map(|entry| entry.policy_f32_lane_4).unwrap_or_default(),
|
||||
probe.entries.first().map(|entry| entry.profile_payload_dword_hex.as_str()).unwrap_or("0x00000000")
|
||||
probe.entries.first().map(|entry| entry.profile_payload_dword_hex.as_str()).unwrap_or("0x00000000"),
|
||||
probe.entries.first().map(|entry| entry.profile_status_kind.as_str())
|
||||
));
|
||||
}
|
||||
if let Some(roster) = &report.save_company_roster_probe {
|
||||
|
|
@ -3709,11 +3712,12 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
|
|||
}
|
||||
if let Some(triplets) = placed_structure_record_triplets.as_ref() {
|
||||
notes.push(format!(
|
||||
"Placed-structure analysis now also exports {} tagged 0x55f1/0x55f2/0x55f3 record triplets; first stems={:?}/{:?}, first footer payload={}.",
|
||||
"Placed-structure analysis now also exports {} tagged 0x55f1/0x55f2/0x55f3 record triplets; first stems={:?}/{:?}, first footer payload={}, first footer status kind={:?}.",
|
||||
triplets.record_count,
|
||||
triplets.entries.first().map(|entry| entry.primary_name.as_str()),
|
||||
triplets.entries.first().map(|entry| entry.secondary_name.as_str()),
|
||||
triplets.entries.first().map(|entry| entry.profile_payload_dword_hex.as_str()).unwrap_or("0x00000000")
|
||||
triplets.entries.first().map(|entry| entry.profile_payload_dword_hex.as_str()).unwrap_or("0x00000000"),
|
||||
triplets.entries.first().map(|entry| entry.profile_status_kind.as_str())
|
||||
));
|
||||
}
|
||||
if !company_entries.is_empty() {
|
||||
|
|
@ -10230,6 +10234,12 @@ fn parse_save_placed_structure_record_triplet_probe(
|
|||
}
|
||||
}
|
||||
let (profile_payload_dword, profile_sentinel_i32, profile_close_marker) = matched_footer?;
|
||||
let (profile_status_kind, farm_growth_stage_index) =
|
||||
derive_save_placed_structure_profile_status(
|
||||
&primary_name,
|
||||
&secondary_name,
|
||||
profile_sentinel_i32,
|
||||
);
|
||||
entries.push(SmpSavePlacedStructureRecordTripletEntryProbe {
|
||||
record_index: index,
|
||||
primary_name,
|
||||
|
|
@ -10254,10 +10264,16 @@ fn parse_save_placed_structure_record_triplet_probe(
|
|||
profile_payload_dword,
|
||||
profile_payload_dword_hex: format!("0x{profile_payload_dword:08x}"),
|
||||
profile_sentinel_i32,
|
||||
profile_status_kind: profile_status_kind.to_string(),
|
||||
farm_growth_stage_index,
|
||||
profile_close_marker,
|
||||
profile_close_marker_hex: format!("0x{profile_close_marker:08x}"),
|
||||
});
|
||||
}
|
||||
let farm_growth_stage_entry_count = entries
|
||||
.iter()
|
||||
.filter(|entry| entry.farm_growth_stage_index.is_some())
|
||||
.count();
|
||||
Some(SmpSavePlacedStructureRecordTripletProbe {
|
||||
profile_family: header_probe.profile_family.clone(),
|
||||
source_kind: "save-placed-structure-record-triplets".to_string(),
|
||||
|
|
@ -10270,10 +10286,28 @@ fn parse_save_placed_structure_record_triplet_probe(
|
|||
"save-side placed-structure records are serialized as repeated 0x55f1/0x55f2/0x55f3 triplets inside the tagged records span".to_string(),
|
||||
"the 0x55f1 chunk currently exposes two len-prefixed structure-name stems before the fixed 0x55f2 policy row".to_string(),
|
||||
"each fixed placed-structure 0x55f2 policy chunk currently decodes as five f32-like lanes, one reserved dword, and one trailing u16 word".to_string(),
|
||||
format!(
|
||||
"the compact 0x55f3 footer status lane behaves like a farm growth-stage bucket on grounded saves: {farm_growth_stage_entry_count} entries expose nonnegative 0..11 values and all observed non-farm families stay at -1"
|
||||
),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn derive_save_placed_structure_profile_status(
|
||||
primary_name: &str,
|
||||
secondary_name: &str,
|
||||
raw_status: i32,
|
||||
) -> (&'static str, Option<u8>) {
|
||||
let looks_like_farm = primary_name.starts_with("Farm") || secondary_name.contains("Farm");
|
||||
if raw_status == -1 {
|
||||
return ("unset", None);
|
||||
}
|
||||
if looks_like_farm && (0..=11).contains(&raw_status) {
|
||||
return ("farm_growth_stage_bucket", Some(raw_status as u8));
|
||||
}
|
||||
("opaque_nondefault", None)
|
||||
}
|
||||
|
||||
fn parse_save_placed_structure_collection_header_probe(
|
||||
bytes: &[u8],
|
||||
file_extension_hint: Option<&str>,
|
||||
|
|
@ -17980,6 +18014,8 @@ mod tests {
|
|||
"0x0e373500"
|
||||
);
|
||||
assert_eq!(triplet_probe.entries[0].profile_sentinel_i32, -1);
|
||||
assert_eq!(triplet_probe.entries[0].profile_status_kind, "unset");
|
||||
assert_eq!(triplet_probe.entries[0].farm_growth_stage_index, None);
|
||||
assert_eq!(
|
||||
triplet_probe.entries[0].profile_close_marker_hex,
|
||||
"0x00005dc2"
|
||||
|
|
@ -17989,6 +18025,22 @@ mod tests {
|
|||
assert_eq!(triplet_probe.entries[1].policy_f32_lane_1, 1200.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derives_placed_structure_farm_growth_stage_from_nonnegative_status() {
|
||||
assert_eq!(
|
||||
derive_save_placed_structure_profile_status("FarmCorn", "FarmSet", 4),
|
||||
("farm_growth_stage_bucket", Some(4))
|
||||
);
|
||||
assert_eq!(
|
||||
derive_save_placed_structure_profile_status("StationA", "StationSetA", -1),
|
||||
("unset", None)
|
||||
);
|
||||
assert_eq!(
|
||||
derive_save_placed_structure_profile_status("StationA", "StationSetA", 4),
|
||||
("opaque_nondefault", None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_placed_structure_tagged_collection_header_probe_from_exact_u32_tags() {
|
||||
let mut bytes = vec![0u8; 0x400];
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@ Working rule:
|
|||
triplets with dual name stems, a fixed five-`f32` policy row, and a compact `0x5dc1...0x5dc2`
|
||||
footer carrying one raw `u32` payload lane plus one live `i32` status lane, so the remaining
|
||||
placed-structure work is semantic closure of those owned fields rather than envelope discovery.
|
||||
- That compact placed-structure `i32` footer status lane is now partially grounded as owned
|
||||
semantics too: observed non-farm families stay at `-1`, while farm families use nonnegative
|
||||
`0..11` buckets that are now exported as farm growth-stage indices instead of opaque raw status
|
||||
residue.
|
||||
- Stepped calendar progression now also refreshes save-world owner time fields, including packed
|
||||
year, packed tuple words, absolute counter, and the derived selected-year gap scalar.
|
||||
- Automatic year-rollover calendar stepping now invokes periodic-boundary service.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue