diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index d706e63..235859d 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -1648,7 +1648,7 @@ pub struct SmpSaveTrainCollectionDirectoryProbe { pub evidence: Vec, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SmpSaveRegionRecordTripletEntryProbe { pub record_index: usize, pub name: String, @@ -1657,9 +1657,16 @@ pub struct SmpSaveRegionRecordTripletEntryProbe { pub profile_tag_relative_offset: usize, pub policy_chunk_len: usize, pub profile_chunk_len: usize, + pub policy_leading_f32_0: f32, + pub policy_leading_f32_1: f32, + pub policy_leading_f32_2: f32, + #[serde(default)] + pub policy_reserved_dwords: Vec, + pub policy_trailing_word: u16, + pub policy_trailing_word_hex: String, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SmpSaveRegionRecordTripletProbe { pub profile_family: String, pub source_kind: String, @@ -3175,9 +3182,25 @@ pub fn load_save_slice_from_report( } if let Some(probe) = &report.save_region_record_triplet_probe { notes.push(format!( - "Raw save tagged region records also expose {} repeated 0x55f1/0x55f2/0x55f3 triplets in the records span; first name={:?}.", + "Raw save tagged region records also expose {} repeated 0x55f1/0x55f2/0x55f3 triplets in the records span; first name={:?}, first policy lanes=({:.3}, {:.3}, {:.3}), trailing_word={}.", probe.record_count, - probe.entries.first().map(|entry| entry.name.as_str()) + probe.entries.first().map(|entry| entry.name.as_str()), + probe.entries + .first() + .map(|entry| entry.policy_leading_f32_0) + .unwrap_or_default(), + probe.entries + .first() + .map(|entry| entry.policy_leading_f32_1) + .unwrap_or_default(), + probe.entries + .first() + .map(|entry| entry.policy_leading_f32_2) + .unwrap_or_default(), + probe.entries + .first() + .map(|entry| entry.policy_trailing_word_hex.as_str()) + .unwrap_or("0x0000") )); } if let Some(probe) = &report.save_placed_structure_collection_header_probe { @@ -3565,9 +3588,24 @@ pub fn inspect_save_company_and_chairman_analysis_bytes( } if let Some(triplets) = region_record_triplets.as_ref() { notes.push(format!( - "Region analysis now also exports {} tagged 0x55f1/0x55f2/0x55f3 record triplets; first serialized region name={:?}.", + "Region analysis now also exports {} tagged 0x55f1/0x55f2/0x55f3 record triplets; first serialized region name={:?}, first policy lanes=({:.3}, {:.3}, {:.3}).", triplets.record_count, - triplets.entries.first().map(|entry| entry.name.as_str()) + triplets.entries.first().map(|entry| entry.name.as_str()), + triplets + .entries + .first() + .map(|entry| entry.policy_leading_f32_0) + .unwrap_or_default(), + triplets + .entries + .first() + .map(|entry| entry.policy_leading_f32_1) + .unwrap_or_default(), + triplets + .entries + .first() + .map(|entry| entry.policy_leading_f32_2) + .unwrap_or_default() )); } if let Some(header) = report @@ -9939,6 +9977,19 @@ fn parse_save_region_record_triplet_probe( let name = parse_save_len_prefixed_ascii_name(name_payload)?; let policy_chunk_len = profile_tag_relative_offset.checked_sub(policy_tag_relative_offset + 4)?; + if policy_chunk_len != 0x1a { + return None; + } + let policy_payload = + records_payload.get(policy_tag_relative_offset + 4..profile_tag_relative_offset)?; + let policy_leading_f32_0 = f32::from_bits(read_u32_at(policy_payload, 0)?); + 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); + for dword_index in 0..3 { + policy_reserved_dwords.push(read_u32_at(policy_payload, 12 + dword_index * 4)?); + } + 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)?; entries.push(SmpSaveRegionRecordTripletEntryProbe { @@ -9949,6 +10000,12 @@ fn parse_save_region_record_triplet_probe( profile_tag_relative_offset, policy_chunk_len, profile_chunk_len, + policy_leading_f32_0, + policy_leading_f32_1, + policy_leading_f32_2, + policy_reserved_dwords, + policy_trailing_word, + policy_trailing_word_hex: format!("0x{policy_trailing_word:04x}"), }); } Some(SmpSaveRegionRecordTripletProbe { @@ -9965,6 +10022,7 @@ fn parse_save_region_record_triplet_probe( "decoded {} region record triplets with one len-prefixed name chunk, one fixed policy chunk, and one trailing profile payload chunk per record", record_count ), + "each fixed 0x55f2 policy chunk currently decodes as three leading f32 lanes, three reserved dwords, and one trailing u16 word".to_string(), ], }) } @@ -16951,6 +17009,12 @@ mod tests { profile_tag_relative_offset: 0x2e, policy_chunk_len: 0x1a, profile_chunk_len: 0x40, + policy_leading_f32_0: 368.0, + policy_leading_f32_1: 0.0, + policy_leading_f32_2: 92.0, + policy_reserved_dwords: vec![0, 0, 0], + policy_trailing_word: 1, + policy_trailing_word_hex: "0x0001".to_string(), }, SmpSaveRegionRecordTripletEntryProbe { record_index: 1, @@ -16960,6 +17024,12 @@ mod tests { profile_tag_relative_offset: 0x9c, policy_chunk_len: 0x1a, profile_chunk_len: 0x20, + policy_leading_f32_0: 552.0, + policy_leading_f32_1: 0.0, + policy_leading_f32_2: 276.0, + policy_reserved_dwords: vec![0, 0, 0], + policy_trailing_word: 1, + policy_trailing_word_hex: "0x0001".to_string(), }, ], evidence: vec![], @@ -17305,12 +17375,19 @@ mod tests { .copy_from_slice(&0x0000520au32.to_le_bytes()); bytes[close_tag_offset..close_tag_offset + 4].copy_from_slice(&0x0000520bu32.to_le_bytes()); let mut cursor = records_tag_offset + 4; - for name in ["Marker09", "Marker10"] { + for (name, x, density, y) in [ + ("Marker09", 368.0f32, 0.0f32, 92.0f32), + ("Marker10", 552.0f32, 1.5f32, 276.0f32), + ] { bytes[cursor..cursor + 2].copy_from_slice(&SAVE_REGION_RECORD_NAME_TAG.to_le_bytes()); bytes[cursor + 4] = name.len() as u8; bytes[cursor + 5..cursor + 5 + name.len()].copy_from_slice(name.as_bytes()); cursor += 0x10; bytes[cursor..cursor + 2].copy_from_slice(&SAVE_REGION_RECORD_POLICY_TAG.to_le_bytes()); + bytes[cursor + 4..cursor + 8].copy_from_slice(&x.to_bits().to_le_bytes()); + bytes[cursor + 8..cursor + 12].copy_from_slice(&density.to_bits().to_le_bytes()); + bytes[cursor + 12..cursor + 16].copy_from_slice(&y.to_bits().to_le_bytes()); + bytes[cursor + 28..cursor + 30].copy_from_slice(&1u16.to_le_bytes()); cursor += 0x1e; bytes[cursor..cursor + 2] .copy_from_slice(&SAVE_REGION_RECORD_PROFILE_TAG.to_le_bytes()); @@ -17343,7 +17420,18 @@ mod tests { assert_eq!(triplet_probe.entries[0].name, "Marker09"); assert_eq!(triplet_probe.entries[0].policy_tag_relative_offset, 0x10); assert_eq!(triplet_probe.entries[0].profile_tag_relative_offset, 0x2e); + assert_eq!(triplet_probe.entries[0].policy_leading_f32_0, 368.0); + assert_eq!(triplet_probe.entries[0].policy_leading_f32_1, 0.0); + assert_eq!(triplet_probe.entries[0].policy_leading_f32_2, 92.0); + assert_eq!( + triplet_probe.entries[0].policy_reserved_dwords, + vec![0, 0, 0] + ); + 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); + assert_eq!(triplet_probe.entries[1].policy_leading_f32_1, 1.5); + assert_eq!(triplet_probe.entries[1].policy_leading_f32_2, 276.0); } #[test] diff --git a/docs/rehost-queue.md b/docs/rehost-queue.md index 4af4325..b1e9cf9 100644 --- a/docs/rehost-queue.md +++ b/docs/rehost-queue.md @@ -11,11 +11,12 @@ Working rule: - Reconstruct the save-side region record body on top of the newly corrected non-direct tagged region seam (`0x5209/0x520a/0x520b`, stride hint `0x06`, `Marker09` record stems) and its now - grounded repeated `0x55f1/0x55f2/0x55f3` record-triplet envelope, especially the fixed `0x55f2` - policy row behind `[region+0x272/+0x25a/+0x25e]`, the pending bonus lane `[region+0x276]`, - completion latch `[region+0x302]`, one-shot notice latch `[region+0x316]`, severity/source lane - `[region+0x25e]`, and any stable region-id or class discriminator that can drive shellless - city-connection service. + grounded repeated `0x55f1/0x55f2/0x55f3` record-triplet envelope, especially the large `0x55f3` + profile payload that should carry the pending bonus lane `[region+0x276]`, completion latch + `[region+0x302]`, one-shot notice latch `[region+0x316]`, severity/source lane `[region+0x25e]`, + and any stable region-id or class discriminator that can drive shellless city-connection + service, now that the fixed `0x55f2` policy row already exposes its three leading f32 lanes, + reserved dwords, and trailing word structurally. - 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. @@ -61,6 +62,10 @@ Working rule: - That same corrected region seam now also exposes repeated `0x55f1/0x55f2/0x55f3` serialized record triplets with len-prefixed names plus fixed policy/profile chunk lengths, so the next city-connection pass can target the real record envelope instead of another blind scan. +- The fixed `0x55f2` row inside each region triplet is now decoded structurally as three leading + `f32` lanes, three reserved `u32` lanes, and a trailing `u16` word, so the next save-region + slice can focus on the larger `0x55f3` payload where the pending/completion/one-shot latches are + most likely to live. - 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.