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: u32,
|
||||||
pub profile_payload_dword_hex: String,
|
pub profile_payload_dword_hex: String,
|
||||||
pub profile_sentinel_i32: i32,
|
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: u32,
|
||||||
pub profile_close_marker_hex: String,
|
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 {
|
if let Some(probe) = &report.save_placed_structure_record_triplet_probe {
|
||||||
notes.push(format!(
|
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.record_count,
|
||||||
probe.entries.first().map(|entry| entry.primary_name.as_str()),
|
probe.entries.first().map(|entry| entry.primary_name.as_str()),
|
||||||
probe.entries.first().map(|entry| entry.secondary_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_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_3).unwrap_or_default(),
|
||||||
probe.entries.first().map(|entry| entry.policy_f32_lane_4).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 {
|
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() {
|
if let Some(triplets) = placed_structure_record_triplets.as_ref() {
|
||||||
notes.push(format!(
|
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.record_count,
|
||||||
triplets.entries.first().map(|entry| entry.primary_name.as_str()),
|
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.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() {
|
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_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 {
|
entries.push(SmpSavePlacedStructureRecordTripletEntryProbe {
|
||||||
record_index: index,
|
record_index: index,
|
||||||
primary_name,
|
primary_name,
|
||||||
|
|
@ -10254,10 +10264,16 @@ fn parse_save_placed_structure_record_triplet_probe(
|
||||||
profile_payload_dword,
|
profile_payload_dword,
|
||||||
profile_payload_dword_hex: format!("0x{profile_payload_dword:08x}"),
|
profile_payload_dword_hex: format!("0x{profile_payload_dword:08x}"),
|
||||||
profile_sentinel_i32,
|
profile_sentinel_i32,
|
||||||
|
profile_status_kind: profile_status_kind.to_string(),
|
||||||
|
farm_growth_stage_index,
|
||||||
profile_close_marker,
|
profile_close_marker,
|
||||||
profile_close_marker_hex: format!("0x{profile_close_marker:08x}"),
|
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 {
|
Some(SmpSavePlacedStructureRecordTripletProbe {
|
||||||
profile_family: header_probe.profile_family.clone(),
|
profile_family: header_probe.profile_family.clone(),
|
||||||
source_kind: "save-placed-structure-record-triplets".to_string(),
|
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(),
|
"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(),
|
"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(),
|
"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(
|
fn parse_save_placed_structure_collection_header_probe(
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
file_extension_hint: Option<&str>,
|
file_extension_hint: Option<&str>,
|
||||||
|
|
@ -17980,6 +18014,8 @@ mod tests {
|
||||||
"0x0e373500"
|
"0x0e373500"
|
||||||
);
|
);
|
||||||
assert_eq!(triplet_probe.entries[0].profile_sentinel_i32, -1);
|
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!(
|
assert_eq!(
|
||||||
triplet_probe.entries[0].profile_close_marker_hex,
|
triplet_probe.entries[0].profile_close_marker_hex,
|
||||||
"0x00005dc2"
|
"0x00005dc2"
|
||||||
|
|
@ -17989,6 +18025,22 @@ mod tests {
|
||||||
assert_eq!(triplet_probe.entries[1].policy_f32_lane_1, 1200.0);
|
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]
|
#[test]
|
||||||
fn parses_placed_structure_tagged_collection_header_probe_from_exact_u32_tags() {
|
fn parses_placed_structure_tagged_collection_header_probe_from_exact_u32_tags() {
|
||||||
let mut bytes = vec![0u8; 0x400];
|
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`
|
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
|
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.
|
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
|
- 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.
|
year, packed tuple words, absolute counter, and the derived selected-year gap scalar.
|
||||||
- Automatic year-rollover calendar stepping now invokes periodic-boundary service.
|
- Automatic year-rollover calendar stepping now invokes periodic-boundary service.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue