From e43731c0ef61ff8043bf1162e4f3da78656115fd Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sat, 18 Apr 2026 11:53:13 -0700 Subject: [PATCH] Sample placed-structure side-buffer rows --- crates/rrt-runtime/src/smp.rs | 58 +++++++++++++++++++++++++++++++++++ docs/rehost-queue.md | 5 +++ 2 files changed, 63 insertions(+) diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index a3f9564..b7cd165 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -1800,9 +1800,27 @@ pub struct SmpSavePlacedStructureDynamicSideBufferProbe { pub first_embedded_primary_name: Option, #[serde(default)] pub first_embedded_secondary_name: Option, + #[serde(default)] + pub embedded_name_row_samples: Vec, pub evidence: Vec, } +#[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, + #[serde(default)] + pub secondary_name: Option, +} + #[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::>(); 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(), diff --git a/docs/rehost-queue.md b/docs/rehost-queue.md index f8f0134..721e969 100644 --- a/docs/rehost-queue.md +++ b/docs/rehost-queue.md @@ -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