diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index 21700a5..47c821f 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -1720,6 +1720,8 @@ pub struct SmpSaveRegionRecordTripletEntryProbe { pub policy_leading_f32_2: f32, #[serde(default)] pub policy_reserved_dwords: Vec, + #[serde(default)] + pub policy_reserved_dword_candidates: Vec, pub policy_trailing_word: u16, pub policy_trailing_word_hex: String, #[serde(default)] @@ -13215,8 +13217,24 @@ fn parse_save_region_record_triplet_probe( let policy_leading_f32_1 = f32::from_bits(read_u32_at(policy_payload, 4)?); let policy_leading_f32_2 = f32::from_bits(read_u32_at(policy_payload, 8)?); let mut policy_reserved_dwords = Vec::with_capacity(3); + let mut policy_reserved_dword_candidates = Vec::with_capacity(3); for dword_index in 0..3 { - policy_reserved_dwords.push(read_u32_at(policy_payload, 12 + dword_index * 4)?); + let reserved_relative_offset = 12 + dword_index * 4; + let raw_u32 = read_u32_at(policy_payload, reserved_relative_offset)?; + policy_reserved_dwords.push(raw_u32); + let relative_offset = record_payload_relative_offset + + policy_tag_relative_offset + + 4 + + reserved_relative_offset; + policy_reserved_dword_candidates.push(SmpSaveDwordCandidate { + label: format!("policy_reserved_word_{}", dword_index + 1), + relative_offset, + relative_offset_hex: format!("0x{relative_offset:x}"), + raw_u32, + raw_u32_hex: format!("0x{raw_u32:08x}"), + value_i32: raw_u32 as i32, + value_f32: f32::from_bits(raw_u32), + }); } let policy_trailing_word = read_u16_at(policy_payload, 24)?; let profile_chunk_len = @@ -13244,6 +13262,7 @@ fn parse_save_region_record_triplet_probe( policy_leading_f32_1, policy_leading_f32_2, policy_reserved_dwords, + policy_reserved_dword_candidates, policy_trailing_word, policy_trailing_word_hex: format!("0x{policy_trailing_word:04x}"), profile_collection, @@ -13262,6 +13281,10 @@ fn parse_save_region_record_triplet_probe( .iter() .filter(|entry| entry.pre_name_prefix_len != 0) .count(); + let records_with_prefix_dword_candidates = entries + .iter() + .filter(|entry| !entry.pre_name_prefix_dword_candidates.is_empty()) + .count(); let unique_pre_name_prefix_lens = entries .iter() .map(|entry| entry.pre_name_prefix_len) @@ -13290,6 +13313,11 @@ fn parse_save_region_record_triplet_probe( record_count, unique_pre_name_prefix_lens ), + format!( + "structured pre-name prefix dword candidates are currently present on {} of {} decoded region records", + records_with_prefix_dword_candidates, + record_count + ), format!( "on grounded saves the 0x55f3 payload is fully consumed by that embedded profile collection: all {} decoded records currently have zero trailing padding beyond the direct profile rows", zero_trailing_padding_record_count @@ -23370,6 +23398,7 @@ mod tests { policy_leading_f32_1: 0.0, policy_leading_f32_2: 92.0, policy_reserved_dwords: vec![0, 0, 0], + policy_reserved_dword_candidates: Vec::new(), policy_trailing_word: 1, policy_trailing_word_hex: "0x0001".to_string(), profile_collection: Some(SmpSaveRegionProfileCollectionProbe { @@ -23412,6 +23441,7 @@ mod tests { policy_leading_f32_1: 0.0, policy_leading_f32_2: 276.0, policy_reserved_dwords: vec![0, 0, 0], + policy_reserved_dword_candidates: Vec::new(), policy_trailing_word: 1, policy_trailing_word_hex: "0x0001".to_string(), profile_collection: Some(SmpSaveRegionProfileCollectionProbe { @@ -23830,6 +23860,16 @@ mod tests { triplet_probe.entries[0].policy_reserved_dwords, vec![0, 0, 0] ); + assert_eq!( + triplet_probe.entries[0] + .policy_reserved_dword_candidates + .len(), + 3 + ); + assert_eq!( + triplet_probe.entries[0].policy_reserved_dword_candidates[0].relative_offset_hex, + "0x20" + ); assert_eq!(triplet_probe.entries[0].policy_trailing_word, 1); assert_eq!(triplet_probe.entries[1].name, "Marker10"); assert_eq!(triplet_probe.entries[1].policy_leading_f32_0, 552.0); @@ -23840,6 +23880,12 @@ mod tests { .pre_name_prefix_dword_candidates .is_empty() ); + assert_eq!( + triplet_probe.entries[1] + .policy_reserved_dword_candidates + .len(), + 3 + ); } #[test] @@ -23940,6 +23986,10 @@ mod tests { triplet_probe.entries[1].pre_name_prefix_dword_candidates[1].relative_offset_hex, "0x52" ); + assert_eq!( + triplet_probe.entries[1].policy_reserved_dword_candidates[2].relative_offset_hex, + "0xcc" + ); } #[test] @@ -26055,6 +26105,7 @@ mod tests { policy_leading_f32_1: 0.0, policy_leading_f32_2: 92.0, policy_reserved_dwords: Vec::new(), + policy_reserved_dword_candidates: Vec::new(), policy_trailing_word: 0, policy_trailing_word_hex: "0x0000".to_string(), profile_collection: Some(SmpSaveRegionProfileCollectionProbe { diff --git a/docs/rehost-queue.md b/docs/rehost-queue.md index 1418d46..4788e86 100644 --- a/docs/rehost-queue.md +++ b/docs/rehost-queue.md @@ -546,9 +546,16 @@ Working rule: owner that rebuilds those latches or the separate tagged body seam that persists them. - The save-side region payload probe is wider now too: the checked-in `region_record_triplets` surface no longer stops at raw pre-name prefix bytes and now also emits structured prefix dword - candidates per record. That gives the next region payload pass a direct way to compare the - opaque pre-`0x55f1` band against the remaining acquisition-side lane shapes instead of redoing - raw hex inspection by hand. + candidates per record, and the fixed `0x55f2` policy chunk now also carries structured reserved + dword candidates instead of raw integers only. That gives the next region payload pass a direct + way to compare both opaque payload bands against the remaining acquisition-side lane shapes + instead of redoing raw hex inspection by hand. +- Grounded real-save output already narrows that new probe once further: on `p.gms`, every decoded + region triplet currently still has `pre_name_prefix_len = 0` and an empty + `pre_name_prefix_dword_candidates` vector, so the remaining acquisition-side payload target does + not appear to live in the pre-`0x55f1` band on that save. That shifts the next payload-comparison + pass onto the fixed `0x55f2` policy chunk and any later separate body seam, not back onto the + pre-name prefix. - The rest of `0x00455fc0` is ruled down further now too: after the `+0x48` callback it only runs `0x0052ebd0`, which reads two one-byte generic flags through `0x531150` into base object bytes `[this+0x20]`, `[this+0x8d]`, `[this+0x5c..+0x61]`, `[this+0x1ee]`, `[this+0x1fa]`, and