Sample placed-structure side-buffer rows
This commit is contained in:
parent
a669edcaa8
commit
e43731c0ef
2 changed files with 63 additions and 0 deletions
|
|
@ -1800,9 +1800,27 @@ pub struct SmpSavePlacedStructureDynamicSideBufferProbe {
|
|||
pub first_embedded_primary_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub first_embedded_secondary_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub embedded_name_row_samples: Vec<SmpSavePlacedStructureDynamicSideBufferSampleEntry>,
|
||||
pub evidence: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpSavePlacedStructureDynamicSideBufferSampleEntry {
|
||||
pub sample_index: usize,
|
||||
pub name_tag_relative_offset: usize,
|
||||
pub prefix_leading_dword: u32,
|
||||
pub prefix_leading_dword_hex: String,
|
||||
pub prefix_trailing_word: u16,
|
||||
pub prefix_trailing_word_hex: String,
|
||||
pub prefix_separator_byte: u8,
|
||||
pub prefix_separator_byte_hex: String,
|
||||
#[serde(default)]
|
||||
pub primary_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub secondary_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpRt3105SaveNameTableProbe {
|
||||
pub profile_family: String,
|
||||
|
|
@ -10699,6 +10717,45 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe(
|
|||
else {
|
||||
continue;
|
||||
};
|
||||
let embedded_name_row_samples = embedded_name_tag_offsets
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.filter_map(|(sample_index, name_tag_relative_offset)| {
|
||||
let prefix_payload = records_payload.get(..name_tag_relative_offset)?;
|
||||
if prefix_payload.len() < 7 {
|
||||
return None;
|
||||
}
|
||||
let prefix_leading_dword = read_u32_at(prefix_payload, prefix_payload.len() - 7)?;
|
||||
let prefix_trailing_word = read_u16_at(prefix_payload, prefix_payload.len() - 3)?;
|
||||
let prefix_separator_byte = *prefix_payload.last()?;
|
||||
let mut parsed_names = None;
|
||||
for relative_name_offset in [4usize, 6usize] {
|
||||
let Some(name_payload) = records_payload
|
||||
.get(name_tag_relative_offset + relative_name_offset..) else {
|
||||
continue;
|
||||
};
|
||||
if let Some(names) = parse_save_len_prefixed_ascii_name_pair(name_payload) {
|
||||
parsed_names = Some(names);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let (primary_name, secondary_name) = parsed_names.unwrap_or_default();
|
||||
Some(SmpSavePlacedStructureDynamicSideBufferSampleEntry {
|
||||
sample_index,
|
||||
name_tag_relative_offset,
|
||||
prefix_leading_dword,
|
||||
prefix_leading_dword_hex: format!("0x{prefix_leading_dword:08x}"),
|
||||
prefix_trailing_word,
|
||||
prefix_trailing_word_hex: format!("0x{prefix_trailing_word:04x}"),
|
||||
prefix_separator_byte,
|
||||
prefix_separator_byte_hex: format!("0x{prefix_separator_byte:02x}"),
|
||||
primary_name: (!primary_name.is_empty()).then_some(primary_name),
|
||||
secondary_name: (!secondary_name.is_empty()).then_some(secondary_name),
|
||||
})
|
||||
})
|
||||
.take(8)
|
||||
.collect::<Vec<_>>();
|
||||
return Some(SmpSavePlacedStructureDynamicSideBufferProbe {
|
||||
profile_family: profile.profile_family.clone(),
|
||||
source_kind: "save-placed-structure-dynamic-side-buffer-records".to_string(),
|
||||
|
|
@ -10723,6 +10780,7 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe(
|
|||
embedded_name_tag_count: embedded_name_tag_offsets.len(),
|
||||
first_embedded_primary_name: Some(first_embedded_primary_name.clone()),
|
||||
first_embedded_secondary_name: Some(first_embedded_secondary_name.clone()),
|
||||
embedded_name_row_samples,
|
||||
evidence: vec![
|
||||
"exact little-endian u32 tag family 0x38a5/0x38a6/0x38a7 appears as a separate save-side tagged collection on grounded saves".to_string(),
|
||||
"records payload begins with a compact 6-byte prefix plus one separator byte before the first embedded 0x55f1 name row".to_string(),
|
||||
|
|
|
|||
|
|
@ -97,6 +97,11 @@ Working rule:
|
|||
`q.gms` exposes `live_record_count=3865`, prefix `0x0005d368/0x0001/0xff`, and first embedded
|
||||
names `TrackCapST_Cap.3dp` / `Infrastructure`; `p.gms` exposes the same structure with
|
||||
`live_record_count=2467`.
|
||||
- That same direct `0x38a5` probe now also samples multiple embedded name rows with their
|
||||
preceding compact prefixes, showing that the seam is not a one-off wrapper: grounded `q.gms`
|
||||
samples include repeated `TunnelSTBrick_*` names under `Infrastructure` with compact leading
|
||||
dwords like `0x000055f3` and `0xff0000ff`, so the next pass can target the semantics of those
|
||||
compact prefix patterns instead of hunting the owner seam 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue