From ae67719ac6b0eee3fcd7fc953b5f46b27ede6fc6 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sat, 18 Apr 2026 13:50:07 -0700 Subject: [PATCH] Probe infrastructure payload 0x55f1 triplets --- crates/rrt-runtime/src/smp.rs | 100 ++++++++++++++++-- ...ntime-roots-camera-and-support-families.md | 7 ++ docs/rehost-queue.md | 8 +- 3 files changed, 104 insertions(+), 11 deletions(-) diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index 0f0bb88..a29e157 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -1838,6 +1838,7 @@ pub struct SmpSavePlacedStructureDynamicSideBufferProbe { pub first_embedded_name_tag_relative_offset: usize, pub embedded_name_tag_count: usize, pub decoded_embedded_name_row_count: usize, + pub decoded_embedded_name_row_with_tertiary_name_count: usize, pub unique_compact_prefix_pattern_count: usize, pub prefix_leading_dword_matching_embedded_profile_tag_count: usize, pub unique_embedded_name_pair_count: usize, @@ -1846,6 +1847,8 @@ pub struct SmpSavePlacedStructureDynamicSideBufferProbe { #[serde(default)] pub first_embedded_secondary_name: Option, #[serde(default)] + pub first_embedded_tertiary_name: Option, + #[serde(default)] pub embedded_name_row_samples: Vec, #[serde(default)] pub compact_prefix_pattern_summaries: @@ -1869,6 +1872,8 @@ pub struct SmpSavePlacedStructureDynamicSideBufferSampleEntry { pub primary_name: Option, #[serde(default)] pub secondary_name: Option, + #[serde(default)] + pub tertiary_name: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -3960,6 +3965,13 @@ fn build_infrastructure_asset_trace_report( "direct disassembly now also shows 0x005181f0/0x00518260 treating the same 12-byte rows as a live-entry directory: dword +0 is the payload pointer, dword +4 is previous live id, and dword +8 is next live id, with collection head/tail caches alongside them".to_string(), "direct disassembly now also shows 0x00493be0 iterating live-entry ordinals through 0x00518380(ordinal, 0), converting each ordinal to a live id, then resolving that live id through 0x00518140 before handing the resulting payload pointer to 0x0048dcf0".to_string(), "direct disassembly now shows 0x00518680 loading the non-direct collection header, tombstone bitset, and live-id-bound-scaled 12-byte tables for the non-direct path before 0x00493be0 starts iterating".to_string(), + "direct disassembly now also shows the shared child payload callback 0x00455fc0 opening 0x55f1, parsing three len-prefixed strings through 0x531380, opening 0x55f2, seeding the child through 0x455b70, dispatching slot +0x48, and then opening 0x55f3".to_string(), + format!( + "current save-side probe reports {} embedded 0x55f1 rows with a third decoded string", + side_buffer + .map(|probe| probe.decoded_embedded_name_row_with_tertiary_name_count) + .unwrap_or_default() + ), "local .rdata at 0x005cfd00 now also proves the infrastructure child table uses the shared tagged callback strip directly: slot +0x40 = 0x455fc0, slot +0x48 = 0x455870, and slot +0x4c = 0x455930".to_string(), "direct disassembly now shows 0x0048a1e0 cloning the first child triplet bands through 0x52e880/0x52e720, destroying the prior child, seeding a new literal Infrastructure child through 0x455b70 with payload seed 0x5c87a8, attaching through 0x5395d0 or 0x53a5d0, and republishing the two bands through 0x52e8b0/0x530720".to_string(), "direct disassembly now also shows the outer owner at 0x0048dcf0 reading a child count plus optional primary-child ordinal from the tagged stream through 0x531150, zeroing [this+0x08], dispatching each fresh child through 0x455a50 -> vtable slot +0x40, culling ordinals above 5, and restoring cached primary-child slot [this+0x248] from the saved ordinal".to_string(), @@ -3968,6 +3980,7 @@ fn build_infrastructure_asset_trace_report( blockers: vec![ "how the payload streams reached through 0x00518380 -> 0x00518140 align with the embedded 0x55f1 name-pair groups and compact-prefix regimes surfaced by the save-side probe".to_string(), "which tagged values inside each payload stream correspond to the child count, optional primary-child ordinal, and the per-child shared tagged callback sequence consumed by 0x0048dcf0".to_string(), + "whether the third 0x55f1 string parsed by 0x00455fc0 is absent on grounded saves, stored under a different framing than the current probe, or only populated on a narrower infrastructure subset".to_string(), "which restored child fields or grouped rows retain the 0x38a5 embedded name-pair semantics before route/local-runtime follow-ons take over".to_string(), ], }, @@ -4436,7 +4449,7 @@ pub fn load_save_slice_from_report( if let Some(probe) = &placed_structure_dynamic_side_buffer_probe { let dominant_pattern = probe.compact_prefix_pattern_summaries.first(); notes.push(format!( - "Raw save also exposes the separate placed-structure dynamic-side-buffer candidate 0x38a5/0x38a6/0x38a7: live_record_count={}, owner-shared 0x38a6 dword={} at relative offset 0x{:x}, first compact prefix=({},{},{}), first embedded names={:?}/{:?}, embedded 0x55f1 row count={}, unique compact prefix patterns={}, 0x55f3-leading rows={}, dominant compact pattern={}/{}/{} x{}.", + "Raw save also exposes the separate placed-structure dynamic-side-buffer candidate 0x38a5/0x38a6/0x38a7: live_record_count={}, owner-shared 0x38a6 dword={} at relative offset 0x{:x}, first compact prefix=({},{},{}), first embedded names={:?}/{:?}/{:?}, embedded 0x55f1 row count={}, rows with tertiary 0x55f1 string={}, unique compact prefix patterns={}, 0x55f3-leading rows={}, dominant compact pattern={}/{}/{} x{}.", probe.live_record_count, probe.owner_shared_dword_hex, probe.owner_shared_dword_relative_offset, @@ -4445,7 +4458,9 @@ pub fn load_save_slice_from_report( probe.prefix_separator_byte_hex, probe.first_embedded_primary_name.as_deref(), probe.first_embedded_secondary_name.as_deref(), + probe.first_embedded_tertiary_name.as_deref(), probe.embedded_name_tag_count, + probe.decoded_embedded_name_row_with_tertiary_name_count, probe.unique_compact_prefix_pattern_count, probe.prefix_leading_dword_matching_embedded_profile_tag_count, dominant_pattern @@ -4932,10 +4947,11 @@ pub fn inspect_save_company_and_chairman_analysis_bytes( if let Some(side_buffer) = placed_structure_dynamic_side_buffer.as_ref() { let dominant_pattern = side_buffer.compact_prefix_pattern_summaries.first(); notes.push(format!( - "Placed-structure analysis now also exports the separate 0x38a5 dynamic side-buffer owner seam with {} embedded name rows, {} decoded rows across {} unique name pairs, {} unique compact prefix patterns, {} rows whose leading dword matches 0x55f3, and dominant compact pattern={}/{}/{} x{}.", + "Placed-structure analysis now also exports the separate 0x38a5 dynamic side-buffer owner seam with {} embedded name rows, {} decoded rows across {} unique name pairs, {} rows with a tertiary 0x55f1 string, {} unique compact prefix patterns, {} rows whose leading dword matches 0x55f3, and dominant compact pattern={}/{}/{} x{}.", side_buffer.embedded_name_tag_count, side_buffer.decoded_embedded_name_row_count, side_buffer.unique_embedded_name_pair_count, + side_buffer.decoded_embedded_name_row_with_tertiary_name_count, side_buffer.unique_compact_prefix_pattern_count, side_buffer.prefix_leading_dword_matching_embedded_profile_tag_count, dominant_pattern @@ -11725,6 +11741,7 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( prefix_separator_byte: u8, primary_name: Option, secondary_name: Option, + tertiary_name: Option, } #[derive(Default)] @@ -11851,13 +11868,16 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( else { continue; }; - if let Some(names) = parse_save_len_prefixed_ascii_name_pair(name_payload) { + if let Some(names) = parse_save_len_prefixed_ascii_name_triplet(name_payload) { parsed_embedded_names = Some(names); break; } } - let Some((first_embedded_primary_name, first_embedded_secondary_name)) = - parsed_embedded_names + let Some(( + first_embedded_primary_name, + first_embedded_secondary_name, + first_embedded_tertiary_name, + )) = parsed_embedded_names else { continue; }; @@ -11879,12 +11899,13 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( else { continue; }; - if let Some(names) = parse_save_len_prefixed_ascii_name_pair(name_payload) { + if let Some(names) = parse_save_len_prefixed_ascii_name_triplet(name_payload) { parsed_names = Some(names); break; } } - let (primary_name, secondary_name) = parsed_names.unwrap_or_default(); + let (primary_name, secondary_name, tertiary_name) = + parsed_names.unwrap_or_default(); Some(EmbeddedNameRow { name_tag_relative_offset, prefix_leading_dword, @@ -11892,6 +11913,7 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( prefix_separator_byte, primary_name: (!primary_name.is_empty()).then_some(primary_name), secondary_name: (!secondary_name.is_empty()).then_some(secondary_name), + tertiary_name, }) }) .collect::>(); @@ -11911,6 +11933,7 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( prefix_separator_byte_hex: format!("0x{:02x}", row.prefix_separator_byte), primary_name: row.primary_name.clone(), secondary_name: row.secondary_name.clone(), + tertiary_name: row.tertiary_name.clone(), }, ) .collect::>(); @@ -12061,6 +12084,14 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( .iter() .filter(|row| row.primary_name.is_some() && row.secondary_name.is_some()) .count(); + let decoded_embedded_name_row_with_tertiary_name_count = embedded_name_rows + .iter() + .filter(|row| { + row.primary_name.is_some() + && row.secondary_name.is_some() + && row.tertiary_name.is_some() + }) + .count(); return Some(SmpSavePlacedStructureDynamicSideBufferProbe { profile_family: profile.profile_family.clone(), source_kind: "save-placed-structure-dynamic-side-buffer-records".to_string(), @@ -12089,11 +12120,13 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( first_embedded_name_tag_relative_offset, embedded_name_tag_count: embedded_name_tag_offsets.len(), decoded_embedded_name_row_count, + decoded_embedded_name_row_with_tertiary_name_count, unique_compact_prefix_pattern_count: compact_prefix_pattern_summaries.len(), prefix_leading_dword_matching_embedded_profile_tag_count, unique_embedded_name_pair_count, first_embedded_primary_name: Some(first_embedded_primary_name.clone()), first_embedded_secondary_name: Some(first_embedded_secondary_name.clone()), + first_embedded_tertiary_name: first_embedded_tertiary_name.clone(), embedded_name_row_samples, compact_prefix_pattern_summaries, name_pair_summaries, @@ -12105,9 +12138,10 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( "records payload begins with a compact 6-byte prefix plus one separator byte before the first embedded 0x55f1 name row".to_string(), "first embedded 0x55f1 row decodes with placed-structure-style dual names, which makes this the strongest current candidate for the separate placed-structure dynamic side-buffer owner seam".to_string(), format!( - "grounded first embedded names are {:?}/{:?} with {} embedded 0x55f1 name rows in the tagged records span", + "grounded first embedded names are {:?}/{:?}/{:?} with {} embedded 0x55f1 name rows in the tagged records span", Some(first_embedded_primary_name), Some(first_embedded_secondary_name), + first_embedded_tertiary_name, embedded_name_tag_offsets.len() ), format!( @@ -12117,8 +12151,10 @@ fn parse_save_placed_structure_dynamic_side_buffer_probe( u32::from(SAVE_REGION_RECORD_PROFILE_TAG) ), format!( - "{decoded_embedded_name_row_count} decoded embedded name rows collapse into {} unique placed-structure name pairs", + "{decoded_embedded_name_row_count} decoded embedded name rows collapse into {} unique placed-structure name pairs; {} rows also expose a third embedded 0x55f1 string", unique_embedded_name_pair_count + , + decoded_embedded_name_row_with_tertiary_name_count ), dominant_compact_prefix_pattern .map(|pattern| { @@ -12511,6 +12547,13 @@ fn parse_save_len_prefixed_ascii_name(bytes: &[u8]) -> Option { } fn parse_save_len_prefixed_ascii_name_pair(bytes: &[u8]) -> Option<(String, String)> { + let (first, second, _) = parse_save_len_prefixed_ascii_name_triplet(bytes)?; + Some((first, second)) +} + +fn parse_save_len_prefixed_ascii_name_triplet( + bytes: &[u8], +) -> Option<(String, String, Option)> { let first_len = *bytes.first()? as usize; let first_end = 1 + first_len; let first = std::str::from_utf8(bytes.get(1..first_end)?) @@ -12530,7 +12573,24 @@ fn parse_save_len_prefixed_ascii_name_pair(bytes: &[u8]) -> Option<(String, Stri if first.is_empty() || second.is_empty() { return None; } - Some((first, second)) + let mut third_len_offset = second_start + second_len; + while matches!(bytes.get(third_len_offset), Some(0)) { + third_len_offset += 1; + } + let third = bytes + .get(third_len_offset) + .copied() + .filter(|len| *len != 0) + .and_then(|third_len| { + let third_len = third_len as usize; + let third_start = third_len_offset + 1; + let text = std::str::from_utf8(bytes.get(third_start..third_start + third_len)?) + .ok()? + .trim_end_matches('\0') + .to_string(); + (!text.is_empty()).then_some(text) + }); + Some((first, second, third)) } fn parse_save_fixed_ascii_name(bytes: &[u8]) -> Option { @@ -20205,6 +20265,7 @@ mod tests { assert_eq!(probe.first_embedded_name_tag_relative_offset, 7); assert_eq!(probe.embedded_name_tag_count, 1); assert_eq!(probe.decoded_embedded_name_row_count, 1); + assert_eq!(probe.decoded_embedded_name_row_with_tertiary_name_count, 0); assert_eq!(probe.unique_compact_prefix_pattern_count, 1); assert_eq!( probe.prefix_leading_dword_matching_embedded_profile_tag_count, @@ -20219,6 +20280,7 @@ mod tests { probe.first_embedded_secondary_name.as_deref(), Some("Infrastructure") ); + assert_eq!(probe.first_embedded_tertiary_name.as_deref(), None); assert_eq!(probe.compact_prefix_pattern_summaries.len(), 1); assert_eq!( probe.compact_prefix_pattern_summaries[0].prefix_leading_dword_hex, @@ -20295,6 +20357,7 @@ mod tests { assert_eq!(probe.embedded_name_tag_count, 3); assert_eq!(probe.decoded_embedded_name_row_count, 3); + assert_eq!(probe.decoded_embedded_name_row_with_tertiary_name_count, 0); assert_eq!(probe.unique_compact_prefix_pattern_count, 2); assert_eq!( probe.prefix_leading_dword_matching_embedded_profile_tag_count, @@ -20333,6 +20396,19 @@ mod tests { assert_eq!(probe.name_pair_summaries[1].count, 1); } + #[test] + fn parses_save_len_prefixed_ascii_name_triplet_with_optional_third_name() { + let bytes = [ + 5u8, b'F', b'i', b'r', b's', b't', 0, 6, b'S', b'e', b'c', b'o', b'n', b'd', 0, 5, + b'T', b'h', b'i', b'r', b'd', + ]; + let parsed = parse_save_len_prefixed_ascii_name_triplet(&bytes) + .expect("triplet parser should decode three len-prefixed ascii names"); + assert_eq!(parsed.0, "First"); + assert_eq!(parsed.1, "Second"); + assert_eq!(parsed.2.as_deref(), Some("Third")); + } + #[test] fn aligns_placed_structure_dynamic_side_buffer_name_pairs_with_triplets() { let side_buffer = SmpSavePlacedStructureDynamicSideBufferProbe { @@ -20363,11 +20439,13 @@ mod tests { first_embedded_name_tag_relative_offset: 7, embedded_name_tag_count: 3, decoded_embedded_name_row_count: 3, + decoded_embedded_name_row_with_tertiary_name_count: 0, unique_compact_prefix_pattern_count: 2, prefix_leading_dword_matching_embedded_profile_tag_count: 2, unique_embedded_name_pair_count: 2, first_embedded_primary_name: Some("TunnelSTBrick_Section.3dp".to_string()), first_embedded_secondary_name: Some("Infrastructure".to_string()), + first_embedded_tertiary_name: None, embedded_name_row_samples: vec![], compact_prefix_pattern_summaries: vec![], name_pair_summaries: vec![ @@ -22015,11 +22093,13 @@ mod tests { first_embedded_name_tag_relative_offset: 0x20, embedded_name_tag_count: 138, decoded_embedded_name_row_count: 138, + decoded_embedded_name_row_with_tertiary_name_count: 0, unique_compact_prefix_pattern_count: 7, prefix_leading_dword_matching_embedded_profile_tag_count: 17, unique_embedded_name_pair_count: 5, first_embedded_primary_name: Some("TrackCapST_Cap.3dp".to_string()), first_embedded_secondary_name: Some("Infrastructure".to_string()), + first_embedded_tertiary_name: None, embedded_name_row_samples: Vec::new(), compact_prefix_pattern_summaries: Vec::new(), name_pair_summaries: vec![SmpSavePlacedStructureDynamicSideBufferNamePairSummary { diff --git a/docs/control-loop-atlas/runtime-roots-camera-and-support-families.md b/docs/control-loop-atlas/runtime-roots-camera-and-support-families.md index 3d0f646..45c58b6 100644 --- a/docs/control-loop-atlas/runtime-roots-camera-and-support-families.md +++ b/docs/control-loop-atlas/runtime-roots-camera-and-support-families.md @@ -2933,6 +2933,13 @@ The low helper strip beneath that shared family is tighter now too: `0x0052ecd0` next unknowns are no longer inside that row layout itself; they are in the payload streams those rows point at, and how those streams align with the save-side `0x55f1` name-pair groups and compact prefix regimes. + The shared child payload callback is tighter now too: `0x00455fc0` zeroes the route-entry and + cached bridge bands, opens `0x55f1`, parses three len-prefixed strings through `0x00531380`, + opens `0x55f2`, seeds the child through `0x00455b70`, dispatches slot `+0x48`, runs the local + follow-on `0x0052ebd0`, and then opens `0x55f3`. The widened save-side probe currently still + sees only two embedded `0x55f1` strings on grounded `q.gms`, so the remaining payload question is + whether that third parsed string is absent on ordinary saves, hidden behind different framing, or + only populated on a narrower infrastructure subset. The child loader family is explicit now too: local `.rdata` at `0x005cfd00` proves the `Infrastructure` child vtable uses the shared tagged callback strip directly, with `+0x40 = 0x00455fc0`, `+0x48 = 0x00455870`, and `+0x4c = 0x00455930`. So the remaining diff --git a/docs/rehost-queue.md b/docs/rehost-queue.md index 7401016..9014cdb 100644 --- a/docs/rehost-queue.md +++ b/docs/rehost-queue.md @@ -65,7 +65,13 @@ Working rule: `(payload pointer, previous live id, next live id)`, so the next infrastructure question is only how those payload streams align with the embedded `0x55f1` name-pair groups and compact-prefix regimes, and which tagged values inside each payload stream become the child count, optional - primary-child ordinal, and per-child callback sequence that `0x0048dcf0` consumes. + primary-child ordinal, and per-child callback sequence that `0x0048dcf0` consumes. Direct + disassembly now also shows the shared child payload callback `0x00455fc0` opening + `0x55f1 -> 0x55f2 -> 0x55f3`, parsing three `0x55f1` strings through `0x00531380`, seeding the + child through `0x00455b70`, and then dispatching slot `+0x48`; the widened save-side probe + currently sees `0` third `0x55f1` strings on grounded `q.gms`, so the next pass should ask + whether that third string is genuinely absent on ordinary saves or stored under different + framing than the current embedded-row scan. - The child loader identity is closed now too: local `.rdata` at `0x005cfd00` proves the `Infrastructure` child vtable uses the shared tagged callback strip directly, with `+0x40 = 0x00455fc0`, `+0x48 = 0x00455870`, and `+0x4c = 0x00455930`. So the remaining