Decode save-side placed-structure footers
This commit is contained in:
parent
5928815465
commit
f13d710556
2 changed files with 109 additions and 6 deletions
|
|
@ -1719,6 +1719,15 @@ pub struct SmpSavePlacedStructureRecordTripletEntryProbe {
|
|||
pub policy_reserved_dword: u32,
|
||||
pub policy_trailing_word: u16,
|
||||
pub policy_trailing_word_hex: String,
|
||||
pub profile_open_marker: u32,
|
||||
pub profile_open_marker_hex: String,
|
||||
pub profile_repeated_primary_name: String,
|
||||
pub profile_repeated_secondary_name: String,
|
||||
pub profile_payload_dword: u32,
|
||||
pub profile_payload_dword_hex: String,
|
||||
pub profile_sentinel_i32: i32,
|
||||
pub profile_close_marker: u32,
|
||||
pub profile_close_marker_hex: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
@ -3278,7 +3287,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}).",
|
||||
"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={}.",
|
||||
probe.record_count,
|
||||
probe.entries.first().map(|entry| entry.primary_name.as_str()),
|
||||
probe.entries.first().map(|entry| entry.secondary_name.as_str()),
|
||||
|
|
@ -3286,7 +3295,8 @@ pub fn load_save_slice_from_report(
|
|||
probe.entries.first().map(|entry| entry.policy_f32_lane_1).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_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")
|
||||
));
|
||||
}
|
||||
if let Some(roster) = &report.save_company_roster_probe {
|
||||
|
|
@ -3699,10 +3709,11 @@ 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={:?}/{:?}.",
|
||||
"Placed-structure analysis now also exports {} tagged 0x55f1/0x55f2/0x55f3 record triplets; first stems={:?}/{:?}, first footer payload={}.",
|
||||
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.secondary_name.as_str()),
|
||||
triplets.entries.first().map(|entry| entry.profile_payload_dword_hex.as_str()).unwrap_or("0x00000000")
|
||||
));
|
||||
}
|
||||
if !company_entries.is_empty() {
|
||||
|
|
@ -10181,6 +10192,44 @@ fn parse_save_placed_structure_record_triplet_probe(
|
|||
let policy_trailing_word = read_u16_at(policy_payload, 24)?;
|
||||
let profile_chunk_len =
|
||||
next_record_relative_offset.checked_sub(profile_tag_relative_offset + 4)?;
|
||||
let profile_payload =
|
||||
records_payload.get(profile_tag_relative_offset + 4..next_record_relative_offset)?;
|
||||
let profile_open_marker = read_u32_at(profile_payload, 0)?;
|
||||
if profile_open_marker != 0x00005dc1 {
|
||||
return None;
|
||||
}
|
||||
let (profile_repeated_primary_name, profile_repeated_secondary_name) =
|
||||
parse_save_len_prefixed_ascii_name_pair(profile_payload.get(4..)?)?;
|
||||
let mut trailer_offset = 4usize;
|
||||
let repeated_primary_len = *profile_payload.get(trailer_offset)? as usize;
|
||||
trailer_offset += 1 + repeated_primary_len;
|
||||
while matches!(profile_payload.get(trailer_offset), Some(0)) {
|
||||
trailer_offset += 1;
|
||||
}
|
||||
let repeated_secondary_len = *profile_payload.get(trailer_offset)? as usize;
|
||||
trailer_offset += 1 + repeated_secondary_len;
|
||||
let mut matched_footer = None;
|
||||
for candidate_offset in [trailer_offset, trailer_offset + 1] {
|
||||
if let (
|
||||
Some(profile_payload_dword),
|
||||
Some(profile_sentinel_i32),
|
||||
Some(profile_close_marker),
|
||||
) = (
|
||||
read_u32_at(profile_payload, candidate_offset),
|
||||
read_i32_at(profile_payload, candidate_offset + 4),
|
||||
read_u32_at(profile_payload, candidate_offset + 8),
|
||||
) {
|
||||
if profile_close_marker == 0x00005dc2 {
|
||||
matched_footer = Some((
|
||||
profile_payload_dword,
|
||||
profile_sentinel_i32,
|
||||
profile_close_marker,
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let (profile_payload_dword, profile_sentinel_i32, profile_close_marker) = matched_footer?;
|
||||
entries.push(SmpSavePlacedStructureRecordTripletEntryProbe {
|
||||
record_index: index,
|
||||
primary_name,
|
||||
|
|
@ -10198,6 +10247,15 @@ fn parse_save_placed_structure_record_triplet_probe(
|
|||
policy_reserved_dword,
|
||||
policy_trailing_word,
|
||||
policy_trailing_word_hex: format!("0x{policy_trailing_word:04x}"),
|
||||
profile_open_marker,
|
||||
profile_open_marker_hex: format!("0x{profile_open_marker:08x}"),
|
||||
profile_repeated_primary_name,
|
||||
profile_repeated_secondary_name,
|
||||
profile_payload_dword,
|
||||
profile_payload_dword_hex: format!("0x{profile_payload_dword:08x}"),
|
||||
profile_sentinel_i32,
|
||||
profile_close_marker,
|
||||
profile_close_marker_hex: format!("0x{profile_close_marker:08x}"),
|
||||
});
|
||||
}
|
||||
Some(SmpSavePlacedStructureRecordTripletProbe {
|
||||
|
|
@ -17855,7 +17913,25 @@ mod tests {
|
|||
cursor += 0x1e;
|
||||
bytes[cursor..cursor + 2]
|
||||
.copy_from_slice(&SAVE_REGION_RECORD_PROFILE_TAG.to_le_bytes());
|
||||
cursor += 0x10;
|
||||
bytes[cursor + 4..cursor + 8].copy_from_slice(&0x5dc1u32.to_le_bytes());
|
||||
let mut payload_cursor = cursor + 8;
|
||||
bytes[payload_cursor] = primary.len() as u8;
|
||||
bytes[payload_cursor + 1..payload_cursor + 1 + primary.len()]
|
||||
.copy_from_slice(primary.as_bytes());
|
||||
payload_cursor += 1 + primary.len();
|
||||
bytes[payload_cursor] = 0;
|
||||
payload_cursor += 1;
|
||||
bytes[payload_cursor] = secondary.len() as u8;
|
||||
bytes[payload_cursor + 1..payload_cursor + 1 + secondary.len()]
|
||||
.copy_from_slice(secondary.as_bytes());
|
||||
payload_cursor += 1 + secondary.len();
|
||||
bytes[payload_cursor] = 0;
|
||||
payload_cursor += 1;
|
||||
bytes[payload_cursor..payload_cursor + 4].copy_from_slice(&0x0e373500u32.to_le_bytes());
|
||||
bytes[payload_cursor + 4..payload_cursor + 8].copy_from_slice(&(-1i32).to_le_bytes());
|
||||
bytes[payload_cursor + 8..payload_cursor + 12]
|
||||
.copy_from_slice(&0x5dc2u32.to_le_bytes());
|
||||
cursor += 0x18 + primary.len() + secondary.len();
|
||||
}
|
||||
|
||||
let header_probe = SmpSaveTaggedCollectionHeaderProbe {
|
||||
|
|
@ -17887,6 +17963,27 @@ mod tests {
|
|||
assert_eq!(triplet_probe.entries[0].policy_chunk_len, 0x1a);
|
||||
assert_eq!(triplet_probe.entries[0].policy_f32_lane_4, 5.9760494);
|
||||
assert_eq!(triplet_probe.entries[0].policy_trailing_word, 0x0101);
|
||||
assert_eq!(
|
||||
triplet_probe.entries[0].profile_open_marker_hex,
|
||||
"0x00005dc1"
|
||||
);
|
||||
assert_eq!(
|
||||
triplet_probe.entries[0].profile_repeated_primary_name,
|
||||
"StationA"
|
||||
);
|
||||
assert_eq!(
|
||||
triplet_probe.entries[0].profile_repeated_secondary_name,
|
||||
"StationSetA"
|
||||
);
|
||||
assert_eq!(
|
||||
triplet_probe.entries[0].profile_payload_dword_hex,
|
||||
"0x0e373500"
|
||||
);
|
||||
assert_eq!(triplet_probe.entries[0].profile_sentinel_i32, -1);
|
||||
assert_eq!(
|
||||
triplet_probe.entries[0].profile_close_marker_hex,
|
||||
"0x00005dc2"
|
||||
);
|
||||
assert_eq!(triplet_probe.entries[1].primary_name, "StationB");
|
||||
assert_eq!(triplet_probe.entries[1].secondary_name, "StationSetB");
|
||||
assert_eq!(triplet_probe.entries[1].policy_f32_lane_1, 1200.0);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ Working rule:
|
|||
or class discriminator that can drive shellless city-connection service.
|
||||
- Reconstruct the save-side placed-structure collection body on top of the newly grounded
|
||||
`0x36b1/0x36b2/0x36b3` header seam so the blocked city-connection / linked-transit branch can
|
||||
stop depending on atlas-only placed-structure and local-runtime refresh notes.
|
||||
stop depending on atlas-only placed-structure and local-runtime refresh notes, especially the
|
||||
semantics of the now-grounded compact `0x55f3` footer dword/status lane and any deeper side
|
||||
buffers beyond the repeated `0x55f1/0x55f2/0x55f3` triplet envelope.
|
||||
- Extend shellless clock advancement so more periodic-company service branches consume owned
|
||||
runtime time state directly instead of only the explicit periodic service command.
|
||||
- Keep widening selected-year world-owner state only when a full owning reader/rebuild family is
|
||||
|
|
@ -69,6 +71,10 @@ Working rule:
|
|||
live-id/count headers, fixed `0x22`-byte rows, profile names, and trailing weight scalars, so
|
||||
the remaining region work is on the unresolved payload fields above that collection rather than
|
||||
on the profile subcollection itself.
|
||||
- The placed-structure tagged save stream now also exposes repeated `0x55f1/0x55f2/0x55f3`
|
||||
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.
|
||||
- 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