rrt/crates/rrt-runtime/src/smp.rs

3370 lines
129 KiB
Rust
Raw Normal View History

use std::fs;
use std::path::Path;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
pub const SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION: u32 = 0x03ec;
const PREAMBLE_U32_WORD_COUNT: usize = 16;
const MIN_ASCII_RUN_LEN: usize = 8;
const ASCII_PREVIEW_CHAR_LIMIT: usize = 160;
const TAG_OFFSET_SAMPLE_LIMIT: usize = 8;
const EARLY_ZERO_RUN_THRESHOLD: usize = 16;
const EARLY_PREVIEW_BYTE_LIMIT: usize = 32;
const EARLY_ALIGNED_WORD_WINDOW_COUNT: usize = 8;
const SHARED_SIGNATURE_WORDS_1_TO_7: [u32; 7] = [
0x00002ee0, 0x00040001, 0x00028000, 0x00010000, 0x00000771, 0x00000771, 0x00000771,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct KnownTagDefinition {
tag_id: u16,
label: &'static str,
grounded_meaning: &'static str,
}
const KNOWN_TAG_DEFINITIONS: [KnownTagDefinition; 4] = [
KnownTagDefinition {
tag_id: 0x2cee,
label: "overlay_mask_plane_primary",
grounded_meaning: "Primary one-byte overlay mask plane restored into world offset +0x1655.",
},
KnownTagDefinition {
tag_id: 0x2d51,
label: "overlay_mask_plane_secondary",
grounded_meaning: "Secondary one-byte overlay mask plane restored into world offset +0x1659.",
},
KnownTagDefinition {
tag_id: 0x9471,
label: "sidecar_byte_plane_family_low",
grounded_meaning: "Lower bound of the grounded sidecar byte-plane chunk family.",
},
KnownTagDefinition {
tag_id: 0x9472,
label: "sidecar_byte_plane_family_high",
grounded_meaning: "Upper bound of the grounded sidecar byte-plane chunk family.",
},
];
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpKnownTagHit {
pub tag_id: u16,
pub tag_hex: String,
pub label: String,
pub grounded_meaning: String,
pub hit_count: usize,
pub sample_offsets: Vec<usize>,
pub last_offset: Option<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpPreambleWord {
pub index: usize,
pub offset: usize,
pub value_le: u32,
pub value_hex: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpPreamble {
pub byte_len: usize,
pub word_count: usize,
pub words: Vec<SmpPreambleWord>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpAsciiPreview {
pub offset: usize,
pub byte_len: usize,
pub preview: String,
pub truncated: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpSharedHeader {
pub byte_len: usize,
pub root_kind_word: u32,
pub root_kind_word_hex: String,
pub primary_family_tag: u32,
pub primary_family_tag_hex: String,
pub shared_signature_words_1_to_7: Vec<u32>,
pub shared_signature_hex_words_1_to_7: Vec<String>,
pub matches_grounded_common_signature: bool,
pub payload_window_words_8_to_9: Vec<u32>,
pub payload_window_hex_words_8_to_9: Vec<String>,
pub reserved_words_10_to_14: Vec<u32>,
pub reserved_words_10_to_14_all_zero: bool,
pub final_flag_word: u32,
pub final_flag_word_hex: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpHeaderVariantProbe {
pub variant_family: String,
pub variant_evidence: Vec<String>,
pub is_known_family: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpEarlyContentProbe {
pub first_post_text_nonzero_offset: usize,
pub zero_pad_after_text_len: usize,
pub first_post_text_block_len: usize,
pub first_post_text_block_hex: String,
pub trailing_zero_pad_after_first_block_len: usize,
pub secondary_nonzero_offset: Option<usize>,
pub secondary_aligned_word_window_offset: Option<usize>,
pub secondary_aligned_word_window_words: Vec<u32>,
pub secondary_aligned_word_window_hex_words: Vec<String>,
pub secondary_preview_hex: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpSecondaryVariantProbe {
pub aligned_window_offset: usize,
pub words: Vec<u32>,
pub hex_words: Vec<String>,
pub variant_family: String,
pub variant_evidence: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpContainerProfile {
pub profile_family: String,
pub profile_evidence: Vec<String>,
pub is_known_profile: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpSaveBootstrapBlock {
pub profile_family: String,
pub aligned_window_offset: usize,
pub leading_word: u32,
pub leading_word_hex: String,
pub anchor_word: u32,
pub anchor_word_hex: String,
pub descriptor_word_2: u32,
pub descriptor_word_2_hex: String,
pub descriptor_word_3: u32,
pub descriptor_word_3_hex: String,
pub descriptor_word_4: u32,
pub descriptor_word_4_hex: String,
pub descriptor_word_5: u32,
pub descriptor_word_5_hex: String,
pub descriptor_word_6: u32,
pub descriptor_word_6_hex: String,
pub descriptor_word_7: u32,
pub descriptor_word_7_hex: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpSaveAnchorRunBlock {
pub profile_family: String,
pub cycle_start_offset: usize,
pub cycle_words: Vec<u32>,
pub cycle_hex_words: Vec<String>,
pub full_cycle_count: usize,
pub partial_cycle_word_count: usize,
pub trailer_offset: usize,
pub trailer_words: Vec<u32>,
pub trailer_hex_words: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRuntimeAnchorCycleBlock {
pub profile_family: String,
pub cycle_start_offset: usize,
pub cycle_words: Vec<u32>,
pub cycle_hex_words: Vec<String>,
pub full_cycle_count: usize,
pub partial_cycle_word_count: usize,
pub trailer_offset: usize,
pub trailer_words: Vec<u32>,
pub trailer_hex_words: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRuntimeTrailerBlock {
pub profile_family: String,
pub trailer_family: String,
pub trailer_evidence: Vec<String>,
pub trailer_offset: usize,
pub prefix_words_0_to_5: Vec<u32>,
pub prefix_hex_words_0_to_5: Vec<String>,
pub tag_word_6: u32,
pub tag_word_6_hex: String,
pub tag_chunk_id_u16: u16,
pub tag_chunk_id_hex: String,
pub tag_chunk_id_grounded_alignment: Option<String>,
pub length_word_7: u32,
pub length_word_7_hex: String,
pub length_high_u16: u16,
pub length_high_hex: String,
pub selector_word_8: u32,
pub selector_word_8_hex: String,
pub selector_high_u16: u16,
pub selector_high_hex: String,
pub layout_word_9: u32,
pub layout_word_9_hex: String,
pub descriptor_word_10: u32,
pub descriptor_word_10_hex: String,
pub descriptor_high_u16: u16,
pub descriptor_high_hex: String,
pub descriptor_word_11: u32,
pub descriptor_word_11_hex: String,
pub counter_word_12: u32,
pub counter_word_12_hex: String,
pub offset_word_13: u32,
pub offset_word_13_hex: String,
pub span_word_14: u32,
pub span_word_14_hex: String,
pub mode_word_15: u32,
pub mode_word_15_hex: String,
pub words: Vec<u32>,
pub hex_words: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRuntimePostSpanProbe {
pub profile_family: String,
pub span_target_offset: usize,
pub next_nonzero_offset: Option<usize>,
pub next_aligned_candidate_offset: Option<usize>,
pub next_aligned_candidate_words: Vec<u32>,
pub next_aligned_candidate_hex_words: Vec<String>,
pub header_candidates: Vec<SmpRuntimePostSpanHeaderCandidate>,
pub grounded_progress_hits: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRuntimePostSpanHeaderCandidate {
pub offset: usize,
pub words: Vec<u32>,
pub hex_words: Vec<String>,
pub dense_word_count: usize,
pub high_u16_words: Vec<u16>,
pub high_hex_words: Vec<String>,
pub grounded_alignments: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRt3105PostSpanBridgeProbe {
pub profile_family: String,
pub bridge_family: String,
pub bridge_evidence: Vec<String>,
pub span_target_offset: usize,
pub next_candidate_offset: Option<usize>,
pub next_candidate_delta_from_span_target: Option<usize>,
pub packed_profile_offset: usize,
pub packed_profile_delta_from_span_target: usize,
pub next_candidate_delta_from_packed_profile: Option<i64>,
pub selector_high_u16: u16,
pub selector_high_hex: String,
pub descriptor_high_u16: u16,
pub descriptor_high_hex: String,
pub next_candidate_high_u16_words: Vec<u16>,
pub next_candidate_high_hex_words: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRt3105SaveBridgePayloadProbe {
pub profile_family: String,
pub bridge_family: String,
pub primary_block_offset: usize,
pub primary_block_len: usize,
pub primary_block_len_hex: String,
pub primary_words: Vec<u32>,
pub primary_hex_words: Vec<String>,
pub secondary_block_offset: usize,
pub secondary_block_delta_from_primary: usize,
pub secondary_block_delta_from_primary_hex: String,
pub secondary_block_end_offset: usize,
pub secondary_block_len: usize,
pub secondary_block_len_hex: String,
pub secondary_preview_word_count: usize,
pub secondary_words: Vec<u32>,
pub secondary_hex_words: Vec<String>,
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRt3105SaveNameTableProbe {
pub profile_family: String,
pub source_kind: String,
pub semantic_family: String,
pub semantic_alignment: Vec<String>,
pub header_offset: usize,
pub header_word_0: u32,
pub header_word_0_hex: String,
pub header_word_1: u32,
pub header_word_1_hex: String,
pub header_word_2: u32,
pub header_word_2_hex: String,
pub entry_stride: usize,
pub entry_stride_hex: String,
pub header_prefix_word_count: usize,
pub observed_entry_capacity: usize,
pub observed_entry_count: usize,
pub zero_trailer_entry_count: usize,
pub nonzero_trailer_entry_count: usize,
pub distinct_trailer_words: Vec<u32>,
pub distinct_trailer_hex_words: Vec<String>,
pub zero_trailer_entry_names: Vec<String>,
pub entries_offset: usize,
pub entries_end_offset: usize,
pub trailing_footer_hex: String,
pub footer_progress_word_0: u32,
pub footer_progress_word_0_hex: String,
pub footer_progress_word_1: u32,
pub footer_progress_word_1_hex: String,
pub footer_trailing_byte: u8,
pub footer_trailing_byte_hex: String,
pub footer_grounded_alignments: Vec<String>,
pub entries: Vec<SmpRt3105SaveNameTableEntry>,
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRt3105SaveNameTableEntry {
pub index: usize,
pub offset: usize,
pub text: String,
pub availability_dword: u32,
pub availability_dword_hex: String,
pub trailer_word: u32,
pub trailer_word_hex: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpClassicRehydrateProfileProbe {
pub profile_family: String,
pub progress_32dc_offset: usize,
pub progress_3714_offset: usize,
pub progress_3715_offset: usize,
pub packed_profile_offset: usize,
pub packed_profile_len: usize,
pub packed_profile_len_hex: String,
pub packed_profile_block: SmpClassicPackedProfileBlock,
pub ascii_runs: Vec<SmpAsciiPreview>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpClassicPackedProfileBlock {
pub relative_len: usize,
pub relative_len_hex: String,
pub leading_word_0: u32,
pub leading_word_0_hex: String,
pub trailing_zero_word_count_after_leading_word: usize,
pub map_path_offset: usize,
pub map_path: Option<String>,
pub display_name_offset: usize,
pub display_name: Option<String>,
pub profile_byte_0x77: u8,
pub profile_byte_0x77_hex: String,
pub profile_byte_0x82: u8,
pub profile_byte_0x82_hex: String,
pub profile_byte_0x97: u8,
pub profile_byte_0x97_hex: String,
pub profile_byte_0xc5: u8,
pub profile_byte_0xc5_hex: String,
pub stable_nonzero_words: Vec<SmpPackedProfileWordLane>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRt3105PackedProfileProbe {
pub profile_family: String,
pub packed_profile_offset: usize,
pub packed_profile_len: usize,
pub packed_profile_len_hex: String,
pub packed_profile_block: SmpRt3105PackedProfileBlock,
pub ascii_runs: Vec<SmpAsciiPreview>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpRt3105PackedProfileBlock {
pub relative_len: usize,
pub relative_len_hex: String,
pub leading_word_0: u32,
pub leading_word_0_hex: String,
pub trailing_zero_word_count_after_leading_word: usize,
pub header_flag_word_3: u32,
pub header_flag_word_3_hex: String,
pub map_path_offset: usize,
pub map_path: Option<String>,
pub display_name_offset: usize,
pub display_name: Option<String>,
pub profile_byte_0x77: u8,
pub profile_byte_0x77_hex: String,
pub profile_byte_0x82: u8,
pub profile_byte_0x82_hex: String,
pub profile_byte_0x97: u8,
pub profile_byte_0x97_hex: String,
pub profile_byte_0xc5: u8,
pub profile_byte_0xc5_hex: String,
pub stable_nonzero_words: Vec<SmpPackedProfileWordLane>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpPackedProfileWordLane {
pub relative_offset: usize,
pub relative_offset_hex: String,
pub value: u32,
pub value_hex: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SmpInspectionReport {
pub inspection_mode: String,
pub file_extension_hint: Option<String>,
pub file_size: usize,
pub sha256: String,
pub preamble: SmpPreamble,
pub shared_header: Option<SmpSharedHeader>,
pub header_variant_probe: Option<SmpHeaderVariantProbe>,
pub first_ascii_run: Option<SmpAsciiPreview>,
pub early_content_probe: Option<SmpEarlyContentProbe>,
pub secondary_variant_probe: Option<SmpSecondaryVariantProbe>,
pub container_profile: Option<SmpContainerProfile>,
pub save_bootstrap_block: Option<SmpSaveBootstrapBlock>,
pub save_anchor_run_block: Option<SmpSaveAnchorRunBlock>,
pub runtime_anchor_cycle_block: Option<SmpRuntimeAnchorCycleBlock>,
pub runtime_trailer_block: Option<SmpRuntimeTrailerBlock>,
pub runtime_post_span_probe: Option<SmpRuntimePostSpanProbe>,
pub rt3_105_post_span_bridge_probe: Option<SmpRt3105PostSpanBridgeProbe>,
pub rt3_105_save_bridge_payload_probe: Option<SmpRt3105SaveBridgePayloadProbe>,
pub rt3_105_save_name_table_probe: Option<SmpRt3105SaveNameTableProbe>,
pub classic_rehydrate_profile_probe: Option<SmpClassicRehydrateProfileProbe>,
pub rt3_105_packed_profile_probe: Option<SmpRt3105PackedProfileProbe>,
pub contains_grounded_runtime_tags: bool,
pub known_tag_hits: Vec<SmpKnownTagHit>,
pub notes: Vec<String>,
pub warnings: Vec<String>,
}
pub fn inspect_smp_file(path: &Path) -> Result<SmpInspectionReport, Box<dyn std::error::Error>> {
let bytes = fs::read(path)?;
Ok(inspect_bundle_bytes(
&bytes,
path.extension()
.and_then(|extension| extension.to_str())
.map(|extension| extension.to_ascii_lowercase()),
))
}
pub fn inspect_smp_bytes(bytes: &[u8]) -> SmpInspectionReport {
inspect_bundle_bytes(bytes, None)
}
fn inspect_bundle_bytes(bytes: &[u8], file_extension_hint: Option<String>) -> SmpInspectionReport {
let known_tag_hits = KNOWN_TAG_DEFINITIONS
.iter()
.filter_map(|definition| {
let offsets = find_u16_le_offsets(bytes, definition.tag_id);
if offsets.is_empty() {
return None;
}
Some(SmpKnownTagHit {
tag_id: definition.tag_id,
tag_hex: format!("0x{:04x}", definition.tag_id),
label: definition.label.to_string(),
grounded_meaning: definition.grounded_meaning.to_string(),
hit_count: offsets.len(),
sample_offsets: offsets
.iter()
.copied()
.take(TAG_OFFSET_SAMPLE_LIMIT)
.collect(),
last_offset: offsets.last().copied(),
})
})
.collect::<Vec<_>>();
let shared_header = parse_shared_header(bytes);
let header_variant_probe = shared_header.as_ref().map(classify_header_variant_probe);
let first_ascii_run = find_first_ascii_run(bytes);
let early_content_probe = first_ascii_run
.as_ref()
.and_then(|ascii_run| probe_early_content_layout(bytes, ascii_run));
let secondary_variant_probe = early_content_probe
.as_ref()
.and_then(classify_secondary_variant_probe);
let container_profile = classify_container_profile(
file_extension_hint.as_deref(),
header_variant_probe.as_ref(),
secondary_variant_probe.as_ref(),
);
let runtime_anchor_cycle_block = parse_runtime_anchor_cycle_block(
bytes,
container_profile.as_ref(),
secondary_variant_probe.as_ref(),
);
let save_bootstrap_block =
parse_save_bootstrap_block(container_profile.as_ref(), secondary_variant_probe.as_ref());
let save_anchor_run_block = parse_save_anchor_run_block(
bytes,
container_profile.as_ref(),
save_bootstrap_block.as_ref(),
);
let runtime_trailer_block = parse_runtime_trailer_block(
container_profile.as_ref(),
runtime_anchor_cycle_block.as_ref(),
);
let runtime_post_span_probe =
parse_runtime_post_span_probe(bytes, runtime_trailer_block.as_ref());
let rt3_105_packed_profile_probe = parse_rt3_105_packed_profile_probe(
bytes,
file_extension_hint.as_deref(),
header_variant_probe.as_ref(),
container_profile.as_ref(),
);
let rt3_105_post_span_bridge_probe = parse_rt3_105_post_span_bridge_probe(
runtime_trailer_block.as_ref(),
runtime_post_span_probe.as_ref(),
rt3_105_packed_profile_probe.as_ref(),
);
let rt3_105_save_bridge_payload_probe =
parse_rt3_105_save_bridge_payload_probe(bytes, rt3_105_post_span_bridge_probe.as_ref());
let rt3_105_save_name_table_probe = parse_rt3_105_save_name_table_probe(
bytes,
file_extension_hint.as_deref(),
container_profile.as_ref(),
rt3_105_save_bridge_payload_probe.as_ref(),
);
let classic_rehydrate_profile_probe =
parse_classic_rehydrate_profile_probe(bytes, runtime_post_span_probe.as_ref());
let mut warnings = Vec::new();
if bytes.is_empty() {
warnings
.push("File is empty, so no `.smp` container structure could be observed.".to_string());
}
if known_tag_hits.is_empty() {
warnings.push(
"No grounded runtime bundle tags were found in little-endian form. This does not prove the file is invalid."
.to_string(),
);
}
if shared_header.is_none() && !bytes.is_empty() {
warnings.push(
"File is shorter than the observed 64-byte common RT3 bundle preamble.".to_string(),
);
}
if let Some(shared_header) = &shared_header {
let header_family_is_known = header_variant_probe
.as_ref()
.map(|probe| probe.is_known_family)
.unwrap_or(false);
if !shared_header.matches_grounded_common_signature && !header_family_is_known {
warnings.push(
"The first 64-byte preamble does not match the currently observed shared RT3 bundle signature."
.to_string(),
);
}
}
if first_ascii_run.is_some() && early_content_probe.is_none() {
warnings.push(
"Found early text content but could not resolve the next stable nonzero region after its zero padding."
.to_string(),
);
}
if container_profile
.as_ref()
.is_some_and(|profile| !profile.is_known_profile)
{
warnings.push(
"The current probes did not match any known composite container profile.".to_string(),
);
}
if known_tag_hits
.iter()
.any(|hit| hit.hit_count > hit.sample_offsets.len())
{
warnings.push(
"Known-tag offsets are sampled in this report. Large hit counts usually mean byte-pattern noise, not validated chunk boundaries."
.to_string(),
);
}
warnings.push(
"Inspection scans raw bytes for a small grounded tag set only. It does not validate bundle layout or decode payloads."
.to_string(),
);
SmpInspectionReport {
inspection_mode: "grounded-tag-scan-plus-preamble".to_string(),
file_extension_hint,
file_size: bytes.len(),
sha256: sha256_hex(bytes),
preamble: parse_preamble(bytes),
shared_header,
header_variant_probe,
first_ascii_run,
early_content_probe,
secondary_variant_probe,
container_profile,
save_bootstrap_block,
save_anchor_run_block,
runtime_anchor_cycle_block,
runtime_trailer_block,
runtime_post_span_probe,
rt3_105_post_span_bridge_probe,
rt3_105_save_bridge_payload_probe,
rt3_105_save_name_table_probe,
classic_rehydrate_profile_probe,
rt3_105_packed_profile_probe,
contains_grounded_runtime_tags: !known_tag_hits.is_empty(),
known_tag_hits,
notes: vec![
"Grounded `.smp` runtime tags currently include mask-plane payload ids 0x2cee and 0x2d51.".to_string(),
"Grounded sidecar-byte-plane bundle family currently spans 0x9471..0x9472.".to_string(),
"The shared-header parse is intentionally conservative: it only names common preamble lanes and checks the observed RT3 bundle-family signature.".to_string(),
"The header-variant probe classifies the preamble into one of the currently observed install-era families when possible."
.to_string(),
"The early-content probe resolves the first stable nonzero block after the padded scenario text and then captures the next aligned word window."
.to_string(),
"The secondary-variant probe classifies that aligned word window into one of the currently observed file-family patterns."
.to_string(),
"The container-profile layer combines extension hint, header family, and second-window family into one observed container classification."
.to_string(),
"The save-bootstrap reader currently parses one conservative 8-word descriptor only for known save-container profiles."
.to_string(),
"The save-anchor-run reader follows that descriptor tail into the observed repeated 9-word anchor cycle and captures the first trailer words after the cycle diverges."
.to_string(),
"The runtime-anchor-cycle reader applies the same cycle/trailer scan across the currently known save and sandbox runtime container profiles."
.to_string(),
"The runtime-trailer reader classifies the first 16 words after the cycle divergence into the currently observed runtime trailer families."
.to_string(),
"The runtime post-span probe follows the trailer's high-16 span lane into the later file region and records the next nonzero bytes, the first aligned high-16-dense candidate window, and any grounded progress-id hits found nearby."
.to_string(),
"The RT3 1.05 post-span bridge probe correlates the trailer selector/descriptor lanes with the next candidate region and the later packed-profile block for the currently observed 1.05 save families."
.to_string(),
"The RT3 1.05 common-save bridge payload probe captures the two stable bridge-stage blocks currently observed under the base 1.05 save branch."
.to_string(),
"The RT3 1.05 candidate-availability table probe decodes the fixed-width trailing name table from either the common-save bridge payload or the fixed 0x6a70..0x73c0 source range when that header validates."
.to_string(),
"The classic rehydrate-profile probe recognizes the grounded 0x32dc -> 0x3714 -> 0x3715 progress-id sequence and captures the exact 0x108-byte block between the latter two ids when that pattern appears."
.to_string(),
"The classic packed-profile block reader exposes the stable map-path, display-name, atlas-tracked latch bytes, and the small set of nonzero word lanes observed inside that 0x108-byte block."
.to_string(),
"The RT3 1.05 packed-profile probe recognizes the later string-bearing save block rooted at the first post-header .gmp path and exposes the observed map-path, display-name, atlas-tracked byte lanes, and stable nonzero words."
.to_string(),
format!(
"Restore-side loading of the four sidecar byte planes is only grounded for bundle versions >= 0x{:04x}.",
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION
),
],
warnings,
}
}
fn parse_preamble(bytes: &[u8]) -> SmpPreamble {
let byte_len = bytes.len().min(PREAMBLE_U32_WORD_COUNT * 4);
let words = bytes[..byte_len]
.chunks_exact(4)
.enumerate()
.map(|(index, chunk)| {
let value_le = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
SmpPreambleWord {
index,
offset: index * 4,
value_le,
value_hex: format!("0x{value_le:08x}"),
}
})
.collect::<Vec<_>>();
SmpPreamble {
byte_len,
word_count: words.len(),
words,
}
}
fn parse_shared_header(bytes: &[u8]) -> Option<SmpSharedHeader> {
let words = read_preamble_words(bytes)?;
let shared_signature_words_1_to_7 = words[1..=7].to_vec();
let payload_window_words_8_to_9 = words[8..=9].to_vec();
let reserved_words_10_to_14 = words[10..=14].to_vec();
let final_flag_word = words[15];
Some(SmpSharedHeader {
byte_len: PREAMBLE_U32_WORD_COUNT * 4,
root_kind_word: words[0],
root_kind_word_hex: format!("0x{:08x}", words[0]),
primary_family_tag: words[1],
primary_family_tag_hex: format!("0x{:08x}", words[1]),
shared_signature_hex_words_1_to_7: shared_signature_words_1_to_7
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
matches_grounded_common_signature: shared_signature_words_1_to_7
== SHARED_SIGNATURE_WORDS_1_TO_7,
shared_signature_words_1_to_7,
payload_window_hex_words_8_to_9: payload_window_words_8_to_9
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
payload_window_words_8_to_9,
reserved_words_10_to_14_all_zero: reserved_words_10_to_14.iter().all(|word| *word == 0),
reserved_words_10_to_14,
final_flag_word,
final_flag_word_hex: format!("0x{final_flag_word:08x}"),
})
}
fn classify_header_variant_probe(shared_header: &SmpSharedHeader) -> SmpHeaderVariantProbe {
let words = &shared_header.shared_signature_words_1_to_7;
let root = shared_header.root_kind_word;
let final_flag = shared_header.final_flag_word;
let (variant_family, evidence, is_known_family) = match (root, words.as_slice(), final_flag) {
(
0x00002649,
[
0x00002ee0,
0x00040001,
0x00028000,
0x00010000,
0x00000771,
0x00000771,
0x00000771,
],
0x00000001,
) => (
"rt3-105-gmx-header-v1".to_string(),
vec![
"root kind word 0x00002649".to_string(),
"1.05 common signature words 1..7".to_string(),
"final flag 0x00000001".to_string(),
],
true,
),
(
0x000025e5,
[
0x00002ee0,
0x00040001,
0x00028000,
0x00010000,
0x00000771,
0x00000771,
0x00000771,
],
0x00000000,
) => (
"rt3-105-common-header-v1".to_string(),
vec![
"root kind word 0x000025e5".to_string(),
"1.05 common signature words 1..7".to_string(),
"final flag 0x00000000".to_string(),
],
true,
),
(
0x000025e5,
[
0x00002ee0,
0x00040001,
0x00018000,
0x00010000,
0x00000746,
0x00000746,
0x00000746,
],
0x00000000,
) => (
"rt3-105-scenario-save-header-v1".to_string(),
vec![
"root kind word 0x000025e5".to_string(),
"1.05 scenario-save signature words 1..7".to_string(),
"final flag 0x00000000".to_string(),
],
true,
),
(
0x000025e5,
[
0x00002ee0,
0x0001c001,
0x00018000,
0x00010000,
0x00000754,
0x00000754,
0x00000754,
],
0x00000000,
) => (
"rt3-105-alt-save-header-v1".to_string(),
vec![
"root kind word 0x000025e5".to_string(),
"1.05 alternate-save signature words 1..7".to_string(),
"final flag 0x00000000".to_string(),
],
true,
),
(
0x000026ad,
[
0x00002ee0,
0x00014001,
0x00020000,
0x00010000,
0x00000725,
0x00000725,
0x00000725,
],
0x00000100,
) => (
"rt3-classic-gms-header-v1".to_string(),
vec![
"root kind word 0x000026ad".to_string(),
"classic save signature words 1..7".to_string(),
"final flag 0x00000100".to_string(),
],
true,
),
(
0x000026ad,
[
0x00002ee0,
0x0001c001,
0x00018000,
0x00010000,
0x00000765,
0x00000765,
0x00000765,
],
0x00000001,
) => (
"rt3-classic-gmx-header-v1".to_string(),
vec![
"root kind word 0x000026ad".to_string(),
"classic sandbox signature words 1..7".to_string(),
"final flag 0x00000001".to_string(),
],
true,
),
(0x000025e5, [0x00002ee0, _, _, 0x00010000, _, _, _], 0x00000000 | 0x00000100) => (
"rt3-map-header-family".to_string(),
vec![
"root kind word 0x000025e5".to_string(),
"map-family anchor 0x00002ee0".to_string(),
"word4 0x00010000".to_string(),
],
true,
),
_ => (
"unknown".to_string(),
vec![format!(
"root=0x{root:08x}, words1..7={}, final=0x{final_flag:08x}",
words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect::<Vec<_>>()
.join(", ")
)],
false,
),
};
SmpHeaderVariantProbe {
variant_family,
variant_evidence: evidence,
is_known_family,
}
}
fn read_preamble_words(bytes: &[u8]) -> Option<[u32; PREAMBLE_U32_WORD_COUNT]> {
if bytes.len() < PREAMBLE_U32_WORD_COUNT * 4 {
return None;
}
let mut words = [0u32; PREAMBLE_U32_WORD_COUNT];
for (index, chunk) in bytes[..PREAMBLE_U32_WORD_COUNT * 4]
.chunks_exact(4)
.enumerate()
{
words[index] = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
}
Some(words)
}
fn probe_early_content_layout(
bytes: &[u8],
ascii_run: &SmpAsciiPreview,
) -> Option<SmpEarlyContentProbe> {
let search_start = ascii_run.offset + ascii_run.byte_len;
let first_post_text_nonzero_offset = find_next_nonzero_offset(bytes, search_start)?;
let zero_pad_after_text_len = first_post_text_nonzero_offset.saturating_sub(search_start);
let first_zero_run_after_block = find_zero_run(
bytes,
first_post_text_nonzero_offset,
EARLY_ZERO_RUN_THRESHOLD,
)
.unwrap_or(bytes.len());
let first_post_text_block = &bytes[first_post_text_nonzero_offset..first_zero_run_after_block];
let secondary_nonzero_offset = find_next_nonzero_offset(bytes, first_zero_run_after_block);
let trailing_zero_pad_after_first_block_len = secondary_nonzero_offset
.map(|offset| offset.saturating_sub(first_zero_run_after_block))
.unwrap_or_else(|| bytes.len().saturating_sub(first_zero_run_after_block));
let secondary_aligned_word_window_offset = secondary_nonzero_offset.map(|offset| offset & !0x3);
let secondary_aligned_word_window_words = secondary_aligned_word_window_offset
.map(|offset| read_u32_window(bytes, offset, EARLY_ALIGNED_WORD_WINDOW_COUNT))
.unwrap_or_default();
let secondary_preview_hex = secondary_nonzero_offset
.map(|offset| {
hex_encode(&bytes[offset..bytes.len().min(offset + EARLY_PREVIEW_BYTE_LIMIT)])
})
.unwrap_or_default();
Some(SmpEarlyContentProbe {
first_post_text_nonzero_offset,
zero_pad_after_text_len,
first_post_text_block_len: first_post_text_block.len(),
first_post_text_block_hex: hex_encode(first_post_text_block),
trailing_zero_pad_after_first_block_len,
secondary_nonzero_offset,
secondary_aligned_word_window_offset,
secondary_aligned_word_window_hex_words: secondary_aligned_word_window_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
secondary_aligned_word_window_words,
secondary_preview_hex,
})
}
fn classify_secondary_variant_probe(
probe: &SmpEarlyContentProbe,
) -> Option<SmpSecondaryVariantProbe> {
let aligned_window_offset = probe.secondary_aligned_word_window_offset?;
let words = probe.secondary_aligned_word_window_words.clone();
if words.is_empty() {
return None;
}
let mut evidence = Vec::new();
let variant_family = match words.as_slice() {
[0x001e0000, 0x86a00100, 0x03000001, 0xf0000100, ..] => {
evidence.push("leading word 0x001e0000".to_string());
evidence.push("anchor word 0x86a00100".to_string());
evidence.push("third/fourth words 0x03000001 and 0xf0000100".to_string());
"rt3-gms-family-v1".to_string()
}
[0x000a0000, 0x49f00100, 0x00000002, 0xa0000000, ..] => {
evidence.push("leading word 0x000a0000".to_string());
evidence.push("anchor word 0x49f00100".to_string());
evidence.push("third/fourth words 0x00000002 and 0xa0000000".to_string());
"rt3-gmx-family-v1".to_string()
}
[0x001c0000, 0x86a00100, 0x00000001, 0xa0000000, ..] => {
evidence.push("leading word 0x001c0000".to_string());
evidence.push("anchor word 0x86a00100".to_string());
evidence.push("third/fourth words 0x00000001 and 0xa0000000".to_string());
"rt3-105-gms-family-v1".to_string()
}
[0x00190000, 0x86a00100, 0x00000001, 0xa0000000, ..] => {
evidence.push("leading word 0x00190000".to_string());
evidence.push("anchor word 0x86a00100".to_string());
evidence.push("third/fourth words 0x00000001 and 0xa0000000".to_string());
"rt3-105-gmx-family-v1".to_string()
}
[0x00130000, 0x86a00100, 0x21000001, 0xa0000100, ..] => {
evidence.push("leading word 0x00130000".to_string());
evidence.push("anchor word 0x86a00100".to_string());
evidence.push("third/fourth words 0x21000001 and 0xa0000100".to_string());
"rt3-105-gms-scenario-family-v1".to_string()
}
[0x00010000, 0x49f00100, 0x00000002, 0xa0000000, ..] => {
evidence.push("leading word 0x00010000".to_string());
evidence.push("anchor word 0x49f00100".to_string());
evidence.push("third/fourth words 0x00000002 and 0xa0000000".to_string());
"rt3-105-gms-alt-family-v1".to_string()
}
[0x86a00100, 0x00000001, 0xa0000000, 0x00000186, ..] => {
evidence.push("window starts directly on 0x86a00100".to_string());
evidence.push("likely same family with missing leading unaligned word".to_string());
"rt3-family-unaligned-anchor".to_string()
}
_ => {
evidence.push(format!(
"unrecognized leading words: {}",
words
.iter()
.take(4)
.map(|word| format!("0x{word:08x}"))
.collect::<Vec<_>>()
.join(", ")
));
"unknown".to_string()
}
};
Some(SmpSecondaryVariantProbe {
aligned_window_offset,
hex_words: words.iter().map(|word| format!("0x{word:08x}")).collect(),
words,
variant_family,
variant_evidence: evidence,
})
}
fn classify_container_profile(
file_extension_hint: Option<&str>,
header_variant_probe: Option<&SmpHeaderVariantProbe>,
secondary_variant_probe: Option<&SmpSecondaryVariantProbe>,
) -> Option<SmpContainerProfile> {
let header_family = header_variant_probe.map(|probe| probe.variant_family.as_str())?;
let secondary_family = secondary_variant_probe.map(|probe| probe.variant_family.as_str())?;
let extension = file_extension_hint.unwrap_or("");
let (profile_family, profile_evidence, is_known_profile) =
match (extension, header_family, secondary_family) {
("gms", "rt3-classic-gms-header-v1", "rt3-gms-family-v1") => (
"rt3-classic-save-container-v1".to_string(),
vec![
"extension .gms".to_string(),
"classic save header family".to_string(),
"classic save secondary window family".to_string(),
],
true,
),
("gmx", "rt3-classic-gmx-header-v1", "rt3-gmx-family-v1") => (
"rt3-classic-sandbox-container-v1".to_string(),
vec![
"extension .gmx".to_string(),
"classic sandbox header family".to_string(),
"classic sandbox secondary window family".to_string(),
],
true,
),
("gms", "rt3-105-common-header-v1", "rt3-105-gms-family-v1") => (
"rt3-105-save-container-v1".to_string(),
vec![
"extension .gms".to_string(),
"1.05 common header family".to_string(),
"1.05 save secondary window family".to_string(),
],
true,
),
("gms", "rt3-105-scenario-save-header-v1", "rt3-105-gms-scenario-family-v1") => (
"rt3-105-scenario-save-container-v1".to_string(),
vec![
"extension .gms".to_string(),
"1.05 scenario-save header family".to_string(),
"1.05 scenario-save secondary window family".to_string(),
],
true,
),
("gms", "rt3-105-alt-save-header-v1", "rt3-105-gms-alt-family-v1") => (
"rt3-105-alt-save-container-v1".to_string(),
vec![
"extension .gms".to_string(),
"1.05 alternate-save header family".to_string(),
"1.05 alternate-save secondary window family".to_string(),
],
true,
),
("gmx", "rt3-105-gmx-header-v1", "rt3-105-gmx-family-v1") => (
"rt3-105-sandbox-container-v1".to_string(),
vec![
"extension .gmx".to_string(),
"1.05 sandbox header family".to_string(),
"1.05 sandbox secondary window family".to_string(),
],
true,
),
("gmp", "rt3-105-common-header-v1", "rt3-family-unaligned-anchor") => (
"rt3-105-map-container-v1".to_string(),
vec![
"extension .gmp".to_string(),
"1.05 common header family".to_string(),
"map-style secondary unaligned anchor".to_string(),
],
true,
),
("gmp", "rt3-105-scenario-save-header-v1", "unknown") => (
"rt3-105-scenario-map-container-v1".to_string(),
vec![
"extension .gmp".to_string(),
"1.05 scenario-map header family".to_string(),
"fixed candidate-availability table range present despite unknown early secondary window".to_string(),
],
true,
),
("gmp", "rt3-105-alt-save-header-v1", "unknown") => (
"rt3-105-alt-map-container-v1".to_string(),
vec![
"extension .gmp".to_string(),
"1.05 alternate-map header family".to_string(),
"fixed candidate-availability table range present despite unknown early secondary window".to_string(),
],
true,
),
("gmp", "rt3-map-header-family", "rt3-family-unaligned-anchor") => (
"rt3-map-container-family".to_string(),
vec![
"extension .gmp".to_string(),
"map header family".to_string(),
"map-style secondary unaligned anchor".to_string(),
],
true,
),
(_, header_family, secondary_family) => (
"unknown".to_string(),
vec![
format!(
"extension {}",
if extension.is_empty() {
"<none>"
} else {
extension
}
),
format!("header family {header_family}"),
format!("secondary family {secondary_family}"),
],
false,
),
};
Some(SmpContainerProfile {
profile_family,
profile_evidence,
is_known_profile,
})
}
fn parse_save_bootstrap_block(
container_profile: Option<&SmpContainerProfile>,
secondary_variant_probe: Option<&SmpSecondaryVariantProbe>,
) -> Option<SmpSaveBootstrapBlock> {
let profile = container_profile?;
let secondary = secondary_variant_probe?;
let words = &secondary.words;
if words.len() < 8 {
return None;
}
let supported = matches!(
profile.profile_family.as_str(),
"rt3-classic-save-container-v1"
| "rt3-105-save-container-v1"
| "rt3-105-scenario-save-container-v1"
| "rt3-105-alt-save-container-v1"
);
if !supported {
return None;
}
Some(SmpSaveBootstrapBlock {
profile_family: profile.profile_family.clone(),
aligned_window_offset: secondary.aligned_window_offset,
leading_word: words[0],
leading_word_hex: format!("0x{:08x}", words[0]),
anchor_word: words[1],
anchor_word_hex: format!("0x{:08x}", words[1]),
descriptor_word_2: words[2],
descriptor_word_2_hex: format!("0x{:08x}", words[2]),
descriptor_word_3: words[3],
descriptor_word_3_hex: format!("0x{:08x}", words[3]),
descriptor_word_4: words[4],
descriptor_word_4_hex: format!("0x{:08x}", words[4]),
descriptor_word_5: words[5],
descriptor_word_5_hex: format!("0x{:08x}", words[5]),
descriptor_word_6: words[6],
descriptor_word_6_hex: format!("0x{:08x}", words[6]),
descriptor_word_7: words[7],
descriptor_word_7_hex: format!("0x{:08x}", words[7]),
})
}
fn parse_runtime_anchor_cycle_block(
bytes: &[u8],
container_profile: Option<&SmpContainerProfile>,
secondary_variant_probe: Option<&SmpSecondaryVariantProbe>,
) -> Option<SmpRuntimeAnchorCycleBlock> {
let profile = container_profile?;
let secondary = secondary_variant_probe?;
let supported = matches!(
profile.profile_family.as_str(),
"rt3-classic-save-container-v1"
| "rt3-classic-sandbox-container-v1"
| "rt3-105-save-container-v1"
| "rt3-105-scenario-save-container-v1"
| "rt3-105-alt-save-container-v1"
| "rt3-105-sandbox-container-v1"
);
if !supported {
return None;
}
let cycle_start_offset = secondary.aligned_window_offset + 0x1c;
let cycle_words = read_u32_window(bytes, cycle_start_offset, 9);
if cycle_words.len() < 9 {
return None;
}
let mut full_cycle_count = 0usize;
let mut cursor = cycle_start_offset;
while read_u32_window(bytes, cursor, cycle_words.len()) == cycle_words {
full_cycle_count += 1;
cursor += cycle_words.len() * 4;
}
if full_cycle_count == 0 {
return None;
}
let mut partial_cycle_word_count = 0usize;
while partial_cycle_word_count < cycle_words.len() {
let offset = cursor + partial_cycle_word_count * 4;
if read_u32_at(bytes, offset) == Some(cycle_words[partial_cycle_word_count]) {
partial_cycle_word_count += 1;
} else {
break;
}
}
let trailer_offset = cursor + partial_cycle_word_count * 4;
let trailer_words = read_u32_window(bytes, trailer_offset, 16);
Some(SmpRuntimeAnchorCycleBlock {
profile_family: profile.profile_family.clone(),
cycle_start_offset,
cycle_hex_words: cycle_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
cycle_words,
full_cycle_count,
partial_cycle_word_count,
trailer_offset,
trailer_hex_words: trailer_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
trailer_words,
})
}
fn parse_save_anchor_run_block(
bytes: &[u8],
container_profile: Option<&SmpContainerProfile>,
save_bootstrap_block: Option<&SmpSaveBootstrapBlock>,
) -> Option<SmpSaveAnchorRunBlock> {
let profile = container_profile?;
let bootstrap = save_bootstrap_block?;
let supported = matches!(
profile.profile_family.as_str(),
"rt3-classic-save-container-v1"
| "rt3-105-save-container-v1"
| "rt3-105-scenario-save-container-v1"
| "rt3-105-alt-save-container-v1"
);
if !supported {
return None;
}
let cycle_start_offset = bootstrap.aligned_window_offset + 0x1c;
let cycle_words = read_u32_window(bytes, cycle_start_offset, 9);
if cycle_words.len() < 9 {
return None;
}
let mut full_cycle_count = 0usize;
let mut cursor = cycle_start_offset;
while read_u32_window(bytes, cursor, cycle_words.len()) == cycle_words {
full_cycle_count += 1;
cursor += cycle_words.len() * 4;
}
if full_cycle_count == 0 {
return None;
}
let mut partial_cycle_word_count = 0usize;
while partial_cycle_word_count < cycle_words.len() {
let offset = cursor + partial_cycle_word_count * 4;
if read_u32_at(bytes, offset) == Some(cycle_words[partial_cycle_word_count]) {
partial_cycle_word_count += 1;
} else {
break;
}
}
let trailer_offset = cursor + partial_cycle_word_count * 4;
let trailer_words = read_u32_window(bytes, trailer_offset, 12);
Some(SmpSaveAnchorRunBlock {
profile_family: profile.profile_family.clone(),
cycle_start_offset,
cycle_hex_words: cycle_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
cycle_words,
full_cycle_count,
partial_cycle_word_count,
trailer_offset,
trailer_hex_words: trailer_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
trailer_words,
})
}
fn parse_runtime_trailer_block(
container_profile: Option<&SmpContainerProfile>,
runtime_anchor_cycle_block: Option<&SmpRuntimeAnchorCycleBlock>,
) -> Option<SmpRuntimeTrailerBlock> {
let profile = container_profile?;
let anchor = runtime_anchor_cycle_block?;
let words = &anchor.trailer_words;
if words.len() < 16 {
return None;
}
let trailer_family = match profile.profile_family.as_str() {
"rt3-classic-save-container-v1"
if words[..6]
== [
0x00020000, 0x00030000, 0x00010000, 0x00010000, 0x00010000, 0x00020000,
] =>
{
"rt3-classic-save-trailer-v1"
}
"rt3-classic-sandbox-container-v1"
if words[..6]
== [
0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00000000, 0x00000000,
] =>
{
"rt3-classic-sandbox-trailer-v1"
}
"rt3-105-save-container-v1"
| "rt3-105-scenario-save-container-v1"
| "rt3-105-alt-save-container-v1"
if words[..6]
== [
0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00000000, 0x00000000,
] =>
{
"rt3-105-save-trailer-v1"
}
"rt3-105-sandbox-container-v1"
if words[..6]
== [
0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00000000, 0x00000000,
] =>
{
"rt3-105-sandbox-trailer-v1"
}
_ => "unknown",
}
.to_string();
let tag_chunk_id_u16 = (words[6] >> 16) as u16;
let length_high_u16 = (words[7] >> 16) as u16;
let selector_high_u16 = (words[8] >> 16) as u16;
let descriptor_high_u16 = (words[10] >> 16) as u16;
let tag_chunk_id_grounded_alignment =
classify_runtime_trailer_chunk_id_grounded_alignment(tag_chunk_id_u16).map(str::to_string);
let mut trailer_evidence = vec![
format!("container profile {}", profile.profile_family),
format!(
"prefix words {}",
words[..6]
.iter()
.map(|word| format!("0x{word:08x}"))
.collect::<Vec<_>>()
.join(", ")
),
format!("high-16 chunk id 0x{tag_chunk_id_u16:04x} from trailer word 6"),
format!("high-16 span 0x{length_high_u16:04x} from trailer word 7"),
format!("high-16 selector 0x{selector_high_u16:04x} from trailer word 8"),
format!("high-16 descriptor 0x{descriptor_high_u16:04x} from trailer word 10"),
];
if let Some(alignment) = &tag_chunk_id_grounded_alignment {
trailer_evidence.push(alignment.clone());
}
Some(SmpRuntimeTrailerBlock {
profile_family: profile.profile_family.clone(),
trailer_family,
trailer_evidence,
trailer_offset: anchor.trailer_offset,
prefix_words_0_to_5: words[..6].to_vec(),
prefix_hex_words_0_to_5: words[..6]
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
tag_word_6: words[6],
tag_word_6_hex: format!("0x{:08x}", words[6]),
tag_chunk_id_u16,
tag_chunk_id_hex: format!("0x{tag_chunk_id_u16:04x}"),
tag_chunk_id_grounded_alignment,
length_word_7: words[7],
length_word_7_hex: format!("0x{:08x}", words[7]),
length_high_u16,
length_high_hex: format!("0x{length_high_u16:04x}"),
selector_word_8: words[8],
selector_word_8_hex: format!("0x{:08x}", words[8]),
selector_high_u16,
selector_high_hex: format!("0x{selector_high_u16:04x}"),
layout_word_9: words[9],
layout_word_9_hex: format!("0x{:08x}", words[9]),
descriptor_word_10: words[10],
descriptor_word_10_hex: format!("0x{:08x}", words[10]),
descriptor_high_u16,
descriptor_high_hex: format!("0x{descriptor_high_u16:04x}"),
descriptor_word_11: words[11],
descriptor_word_11_hex: format!("0x{:08x}", words[11]),
counter_word_12: words[12],
counter_word_12_hex: format!("0x{:08x}", words[12]),
offset_word_13: words[13],
offset_word_13_hex: format!("0x{:08x}", words[13]),
span_word_14: words[14],
span_word_14_hex: format!("0x{:08x}", words[14]),
mode_word_15: words[15],
mode_word_15_hex: format!("0x{:08x}", words[15]),
words: words.to_vec(),
hex_words: words.iter().map(|word| format!("0x{word:08x}")).collect(),
})
}
fn classify_runtime_trailer_chunk_id_grounded_alignment(
tag_chunk_id_u16: u16,
) -> Option<&'static str> {
match tag_chunk_id_u16 {
0x2ee1 => Some(
"High-16 chunk id 0x2ee1 matches the disassembly-grounded map-style bundle family already read by shell_setup_load_selected_profile_bundle_into_payload_record.",
),
_ => None,
}
}
fn parse_runtime_post_span_probe(
bytes: &[u8],
runtime_trailer_block: Option<&SmpRuntimeTrailerBlock>,
) -> Option<SmpRuntimePostSpanProbe> {
let trailer = runtime_trailer_block?;
let span_target_offset = trailer.trailer_offset + trailer.length_high_u16 as usize;
let next_nonzero_offset = find_next_nonzero_offset(bytes, span_target_offset);
let header_candidates =
collect_runtime_post_span_header_candidates(bytes, span_target_offset, 0x8000);
let next_aligned_candidate_offset = header_candidates.first().map(|candidate| candidate.offset);
let next_aligned_candidate_words = header_candidates
.first()
.map(|candidate| candidate.words.clone())
.unwrap_or_default();
let grounded_progress_hits =
find_grounded_progress_high16_hits(bytes, span_target_offset, 0x8000);
Some(SmpRuntimePostSpanProbe {
profile_family: trailer.profile_family.clone(),
span_target_offset,
next_nonzero_offset,
next_aligned_candidate_offset,
next_aligned_candidate_hex_words: next_aligned_candidate_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
next_aligned_candidate_words,
header_candidates,
grounded_progress_hits,
})
}
fn parse_classic_rehydrate_profile_probe(
bytes: &[u8],
runtime_post_span_probe: Option<&SmpRuntimePostSpanProbe>,
) -> Option<SmpClassicRehydrateProfileProbe> {
let post_span = runtime_post_span_probe?;
if post_span.profile_family != "rt3-classic-save-container-v1" {
return None;
}
let progress_32dc_offset =
parse_grounded_progress_hit_offset(&post_span.grounded_progress_hits, 0x32dc)?;
let progress_3714_offset =
parse_grounded_progress_hit_offset(&post_span.grounded_progress_hits, 0x3714)?;
let progress_3715_offset =
parse_grounded_progress_hit_offset(&post_span.grounded_progress_hits, 0x3715)?;
let packed_profile_offset = progress_3714_offset + 4;
let packed_profile_len = progress_3715_offset.checked_sub(packed_profile_offset)?;
if packed_profile_len != 0x108 {
return None;
}
let ascii_runs =
collect_ascii_previews_in_range(bytes, packed_profile_offset, progress_3715_offset, 4);
let packed_profile_block =
parse_classic_packed_profile_block(bytes, packed_profile_offset, packed_profile_len)?;
Some(SmpClassicRehydrateProfileProbe {
profile_family: post_span.profile_family.clone(),
progress_32dc_offset,
progress_3714_offset,
progress_3715_offset,
packed_profile_offset,
packed_profile_len,
packed_profile_len_hex: format!("0x{packed_profile_len:03x}"),
packed_profile_block,
ascii_runs,
})
}
fn parse_classic_packed_profile_block(
bytes: &[u8],
packed_profile_offset: usize,
packed_profile_len: usize,
) -> Option<SmpClassicPackedProfileBlock> {
let block_end = packed_profile_offset.checked_add(packed_profile_len)?;
if block_end > bytes.len() || packed_profile_len != 0x108 {
return None;
}
let leading_word_0 = read_u32_at(bytes, packed_profile_offset)?;
let trailing_zero_word_count_after_leading_word = (1..4)
.take_while(|index| {
read_u32_at(bytes, packed_profile_offset + (index * 4)).is_some_and(|word| word == 0)
})
.count();
let map_path_offset = 0x13;
let display_name_offset = 0x46;
let stable_nonzero_word_offsets = [0x00usize, 0x10, 0x78, 0x7c, 0x84, 0x88];
let stable_nonzero_words = stable_nonzero_word_offsets
.iter()
.filter_map(|relative_offset| {
let value = read_u32_at(bytes, packed_profile_offset + relative_offset)?;
if value == 0 {
return None;
}
Some(SmpPackedProfileWordLane {
relative_offset: *relative_offset,
relative_offset_hex: format!("0x{relative_offset:02x}"),
value,
value_hex: format!("0x{value:08x}"),
})
})
.collect::<Vec<_>>();
Some(SmpClassicPackedProfileBlock {
relative_len: packed_profile_len,
relative_len_hex: format!("0x{packed_profile_len:03x}"),
leading_word_0,
leading_word_0_hex: format!("0x{leading_word_0:08x}"),
trailing_zero_word_count_after_leading_word,
map_path_offset,
map_path: read_c_string_in_range(bytes, packed_profile_offset + map_path_offset, block_end),
display_name_offset,
display_name: read_c_string_in_range(
bytes,
packed_profile_offset + display_name_offset,
block_end,
),
profile_byte_0x77: bytes[packed_profile_offset + 0x77],
profile_byte_0x77_hex: format!("0x{:02x}", bytes[packed_profile_offset + 0x77]),
profile_byte_0x82: bytes[packed_profile_offset + 0x82],
profile_byte_0x82_hex: format!("0x{:02x}", bytes[packed_profile_offset + 0x82]),
profile_byte_0x97: bytes[packed_profile_offset + 0x97],
profile_byte_0x97_hex: format!("0x{:02x}", bytes[packed_profile_offset + 0x97]),
profile_byte_0xc5: bytes[packed_profile_offset + 0xc5],
profile_byte_0xc5_hex: format!("0x{:02x}", bytes[packed_profile_offset + 0xc5]),
stable_nonzero_words,
})
}
fn parse_rt3_105_packed_profile_probe(
bytes: &[u8],
file_extension_hint: Option<&str>,
header_variant_probe: Option<&SmpHeaderVariantProbe>,
container_profile: Option<&SmpContainerProfile>,
) -> Option<SmpRt3105PackedProfileProbe> {
let profile_family = if container_profile.is_some_and(|profile| {
matches!(
profile.profile_family.as_str(),
"rt3-105-save-container-v1"
| "rt3-105-scenario-save-container-v1"
| "rt3-105-alt-save-container-v1"
)
}) {
container_profile
.expect("checked above")
.profile_family
.clone()
} else if file_extension_hint == Some("gms")
&& header_variant_probe.is_some_and(|probe| {
matches!(
probe.variant_family.as_str(),
"rt3-105-common-header-v1"
| "rt3-105-scenario-save-header-v1"
| "rt3-105-alt-save-header-v1"
| "rt3-map-header-family"
)
})
{
"rt3-105-save-analog-block-inferred".to_string()
} else {
return None;
};
if file_extension_hint != Some("gms") {
return None;
}
let map_path_offset = find_c_string_with_suffix_in_range(bytes, 0x7000, 0x9000, ".gmp")?;
let packed_profile_offset = map_path_offset.checked_sub(0x10)?;
let packed_profile_len = 0x108usize;
let block_end = packed_profile_offset.checked_add(packed_profile_len)?;
if block_end > bytes.len() {
return None;
}
let packed_profile_block =
parse_rt3_105_packed_profile_block(bytes, packed_profile_offset, packed_profile_len)?;
let ascii_runs = collect_ascii_previews_in_range(bytes, packed_profile_offset, block_end, 4);
Some(SmpRt3105PackedProfileProbe {
profile_family,
packed_profile_offset,
packed_profile_len,
packed_profile_len_hex: format!("0x{packed_profile_len:03x}"),
packed_profile_block,
ascii_runs,
})
}
fn parse_rt3_105_post_span_bridge_probe(
runtime_trailer_block: Option<&SmpRuntimeTrailerBlock>,
runtime_post_span_probe: Option<&SmpRuntimePostSpanProbe>,
rt3_105_packed_profile_probe: Option<&SmpRt3105PackedProfileProbe>,
) -> Option<SmpRt3105PostSpanBridgeProbe> {
let trailer = runtime_trailer_block?;
let post_span = runtime_post_span_probe?;
let packed_profile = rt3_105_packed_profile_probe?;
let supported = matches!(
trailer.profile_family.as_str(),
"rt3-105-save-container-v1"
| "rt3-105-scenario-save-container-v1"
| "rt3-105-alt-save-container-v1"
| "rt3-105-save-analog-block-inferred"
);
if !supported || trailer.profile_family != post_span.profile_family {
return None;
}
let next_candidate_high_u16_words = post_span
.header_candidates
.first()
.map(|candidate| candidate.high_u16_words.clone())
.unwrap_or_default();
let next_candidate_high_hex_words = next_candidate_high_u16_words
.iter()
.map(|word| format!("0x{word:04x}"))
.collect::<Vec<_>>();
let next_candidate_offset = post_span.next_aligned_candidate_offset;
let next_candidate_delta_from_span_target =
next_candidate_offset.and_then(|offset| offset.checked_sub(post_span.span_target_offset));
let packed_profile_delta_from_span_target = packed_profile
.packed_profile_offset
.checked_sub(post_span.span_target_offset)?;
let next_candidate_delta_from_packed_profile = next_candidate_offset
.map(|offset| offset as i64 - packed_profile.packed_profile_offset as i64);
let mut bridge_evidence = vec![
format!("profile family {}", trailer.profile_family),
format!("selector high {}", trailer.selector_high_hex),
format!("descriptor high {}", trailer.descriptor_high_hex),
format!(
"packed profile sits +0x{packed_profile_delta_from_span_target:x} from span target"
),
];
if let Some(delta) = next_candidate_delta_from_span_target {
bridge_evidence.push(format!("next candidate sits +0x{delta:x} from span target"));
}
if let Some(delta) = next_candidate_delta_from_packed_profile {
bridge_evidence.push(format!(
"next candidate is {delta:+#x} relative to packed profile"
));
}
let bridge_family = match (
trailer.selector_high_u16,
trailer.descriptor_high_u16,
next_candidate_high_u16_words.as_slice(),
) {
(0x7110, 0x7801 | 0x7401, [0x6200, 0x0000, 0xfff7, 0x5515, ..]) => {
bridge_evidence.push(format!(
"selector/descriptor pair 0x7110 -> 0x{:04x}",
trailer.descriptor_high_u16
));
bridge_evidence.push(
"next candidate begins with high-16 lanes 0x6200/0x0000/0xfff7/0x5515"
.to_string(),
);
"rt3-105-save-post-span-bridge-v1"
}
(0x54cd, 0x5901, [0x1500, 0x0100, 0x4100, 0x0200, ..]) => {
bridge_evidence.push("selector/descriptor pair 0x54cd -> 0x5901".to_string());
bridge_evidence.push(
"next candidate begins with high-16 lanes 0x1500/0x0100/0x4100/0x0200"
.to_string(),
);
"rt3-105-alt-save-post-span-bridge-v1"
}
(0x0001, 0x0186, [0x0186, 0x0006, 0x0006, 0x0001, ..]) => {
bridge_evidence.push("selector/descriptor pair 0x0001 -> 0x0186".to_string());
bridge_evidence.push(
"next candidate remains in the local cycle neighborhood with 0x0186/0x0006/0x0006/0x0001"
.to_string(),
);
"rt3-105-scenario-post-span-bridge-v1"
}
_ => "unknown",
}
.to_string();
Some(SmpRt3105PostSpanBridgeProbe {
profile_family: trailer.profile_family.clone(),
bridge_family,
bridge_evidence,
span_target_offset: post_span.span_target_offset,
next_candidate_offset,
next_candidate_delta_from_span_target,
packed_profile_offset: packed_profile.packed_profile_offset,
packed_profile_delta_from_span_target,
next_candidate_delta_from_packed_profile,
selector_high_u16: trailer.selector_high_u16,
selector_high_hex: trailer.selector_high_hex.clone(),
descriptor_high_u16: trailer.descriptor_high_u16,
descriptor_high_hex: trailer.descriptor_high_hex.clone(),
next_candidate_high_u16_words,
next_candidate_high_hex_words,
})
}
fn parse_rt3_105_save_bridge_payload_probe(
bytes: &[u8],
bridge_probe: Option<&SmpRt3105PostSpanBridgeProbe>,
) -> Option<SmpRt3105SaveBridgePayloadProbe> {
let bridge = bridge_probe?;
if bridge.bridge_family != "rt3-105-save-post-span-bridge-v1" {
return None;
}
let primary_block_offset = bridge.next_candidate_offset?;
let primary_block_word_count = 8usize;
let primary_words = read_u32_window(bytes, primary_block_offset, primary_block_word_count);
if primary_words.len() < primary_block_word_count {
return None;
}
let secondary_block_delta_from_primary = 0x1808usize;
let secondary_block_offset = primary_block_offset + secondary_block_delta_from_primary;
let secondary_block_end_offset = bridge.packed_profile_offset;
let secondary_block_len = secondary_block_end_offset.checked_sub(secondary_block_offset)?;
let secondary_preview_word_count = 32usize;
let secondary_words =
read_u32_window(bytes, secondary_block_offset, secondary_preview_word_count);
if secondary_words.len() < secondary_preview_word_count {
return None;
}
let primary_signature_matches = primary_words
== [
0x62000000, 0x00000000, 0xfff70000, 0x55150000, 0x55550000, 0x00000000, 0xfff70000,
0x54550000,
];
let secondary_prefix_matches = secondary_words.starts_with(&[
0x00050000, 0x00050005, 0xfff70000, 0x54540000, 0x545400f9, 0x00f900f9, 0x00f94008,
0x00001555,
]);
let mut evidence = vec![
"bridge family rt3-105-save-post-span-bridge-v1".to_string(),
format!("primary block offset 0x{primary_block_offset:08x}"),
format!("secondary block offset 0x{secondary_block_offset:08x}"),
format!("secondary block delta from primary 0x{secondary_block_delta_from_primary:x}"),
format!("secondary block end offset 0x{secondary_block_end_offset:08x}"),
format!("secondary block span 0x{secondary_block_len:x} bytes"),
];
if primary_signature_matches {
evidence.push(
"primary 8-word bridge block matches the observed 0x6200/0xfff7/0x5515/0x5555 spine"
.to_string(),
);
}
if secondary_prefix_matches {
evidence.push(
"secondary preview matches the observed 0x0005/0xfff7/0x5454 dense block prefix"
.to_string(),
);
}
Some(SmpRt3105SaveBridgePayloadProbe {
profile_family: bridge.profile_family.clone(),
bridge_family: bridge.bridge_family.clone(),
primary_block_offset,
primary_block_len: primary_block_word_count * 4,
primary_block_len_hex: format!("0x{:02x}", primary_block_word_count * 4),
primary_hex_words: primary_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
primary_words,
secondary_block_offset,
secondary_block_delta_from_primary,
secondary_block_delta_from_primary_hex: format!("0x{secondary_block_delta_from_primary:x}"),
secondary_block_end_offset,
secondary_block_len,
secondary_block_len_hex: format!("0x{secondary_block_len:x}"),
secondary_preview_word_count,
secondary_hex_words: secondary_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect(),
secondary_words,
evidence,
})
}
fn parse_rt3_105_save_name_table_probe(
bytes: &[u8],
file_extension_hint: Option<&str>,
container_profile: Option<&SmpContainerProfile>,
bridge_payload_probe: Option<&SmpRt3105SaveBridgePayloadProbe>,
) -> Option<SmpRt3105SaveNameTableProbe> {
let (
profile_family,
source_kind,
header_offset,
entries_offset,
block_end_offset,
mut evidence,
) = if let Some(payload) = bridge_payload_probe {
(
payload.profile_family.clone(),
"save-bridge-secondary-block".to_string(),
payload.secondary_block_offset + 0x354,
payload.secondary_block_offset + 0x3b5,
payload.secondary_block_end_offset,
vec![
"common-save bridge payload branch".to_string(),
format!(
"secondary block span 0x{:x}..0x{:x}",
payload.secondary_block_offset, payload.secondary_block_end_offset
),
],
)
} else {
let profile_family = container_profile
.map(|profile| profile.profile_family.clone())
.unwrap_or_else(|| "unknown".to_string());
let extension = file_extension_hint.unwrap_or("");
let source_kind = match extension {
"gmp" => "map-fixed-catalog-range",
"gms" => "save-fixed-catalog-range",
"gmx" => "sandbox-fixed-catalog-range",
_ => "fixed-catalog-range",
}
.to_string();
(
profile_family,
source_kind,
0x6a70,
0x6ad1,
0x73c0,
vec![
"fixed catalog range branch".to_string(),
"using observed shared 1.05 candidate-availability table offsets".to_string(),
],
)
};
let entry_stride = 0x22usize;
if block_end_offset > bytes.len() {
return None;
}
if !matches_candidate_availability_table_header(bytes, header_offset) {
return None;
}
let observed_entry_capacity = read_u32_at(bytes, header_offset + 0x1c)? as usize;
let observed_entry_count = read_u32_at(bytes, header_offset + 0x20)? as usize;
let entries_len = observed_entry_count.checked_mul(entry_stride)?;
let entries_end_offset = entries_offset.checked_add(entries_len)?;
if observed_entry_count == 0 || observed_entry_capacity < observed_entry_count {
return None;
}
if entries_end_offset > block_end_offset || entries_end_offset > bytes.len() {
return None;
}
let mut entries = Vec::with_capacity(observed_entry_count);
for index in 0..observed_entry_count {
let offset = entries_offset + index * entry_stride;
let chunk = &bytes[offset..offset + entry_stride];
let nul_index = chunk
.iter()
.position(|byte| *byte == 0)
.unwrap_or(entry_stride);
let text = std::str::from_utf8(&chunk[..nul_index]).ok()?.to_string();
let trailer_word = read_u32_at(bytes, offset + entry_stride - 4)?;
entries.push(SmpRt3105SaveNameTableEntry {
index,
offset,
text,
availability_dword: trailer_word,
availability_dword_hex: format!("0x{trailer_word:08x}"),
trailer_word,
trailer_word_hex: format!("0x{trailer_word:08x}"),
});
}
let zero_trailer_entry_names = entries
.iter()
.filter(|entry| entry.trailer_word == 0)
.map(|entry| entry.text.clone())
.collect::<Vec<_>>();
let zero_trailer_entry_count = zero_trailer_entry_names.len();
let nonzero_trailer_entry_count = entries.len().saturating_sub(zero_trailer_entry_count);
let mut distinct_trailer_words = entries
.iter()
.map(|entry| entry.trailer_word)
.collect::<Vec<_>>();
distinct_trailer_words.sort_unstable();
distinct_trailer_words.dedup();
let distinct_trailer_hex_words = distinct_trailer_words
.iter()
.map(|word| format!("0x{word:08x}"))
.collect::<Vec<_>>();
let trailing_footer_hex = hex_encode(&bytes[entries_end_offset..block_end_offset]);
let footer = &bytes[entries_end_offset..block_end_offset];
if footer.len() != 9 {
return None;
}
let footer_progress_word_0 = u32::from_le_bytes([footer[0], footer[1], footer[2], footer[3]]);
let footer_progress_word_1 = u32::from_le_bytes([footer[4], footer[5], footer[6], footer[7]]);
let footer_trailing_byte = footer[8];
let mut footer_grounded_alignments = Vec::new();
for value in [footer_progress_word_0, footer_progress_word_1] {
if let Some(alignment) = classify_name_table_footer_progress_alignment(value) {
footer_grounded_alignments.push(alignment.to_string());
}
}
evidence.extend([
format!("header offset 0x{header_offset:08x}"),
format!("entries offset 0x{entries_offset:08x}"),
format!("entry stride 0x{entry_stride:x}"),
format!("observed entry capacity {}", observed_entry_capacity),
format!("observed entry count {}", observed_entry_count),
format!("zero-trailer entries {}", zero_trailer_entry_count),
format!(
"trailing footer {} bytes after last entry",
block_end_offset - entries_end_offset
),
]);
let semantic_alignment = vec![
"Matches the grounded scenario-side named candidate-availability table shape under 0x00437743.".to_string(),
"Entry layout matches 0x00434ea0/0x00434f20: name slot at +0x00..+0x1d and availability dword at +0x1e.".to_string(),
"The shared map/save range suggests this catalog is bundled in source map content and later mirrored into scenario state [state+0x66b2].".to_string(),
];
Some(SmpRt3105SaveNameTableProbe {
profile_family,
source_kind,
semantic_family: "scenario-named-candidate-availability-table".to_string(),
semantic_alignment,
header_offset,
header_word_0: read_u32_at(bytes, header_offset)?,
header_word_0_hex: format!("0x{:08x}", read_u32_at(bytes, header_offset)?),
header_word_1: read_u32_at(bytes, header_offset + 4)?,
header_word_1_hex: format!("0x{:08x}", read_u32_at(bytes, header_offset + 4)?),
header_word_2: read_u32_at(bytes, header_offset + 8)?,
header_word_2_hex: format!("0x{:08x}", read_u32_at(bytes, header_offset + 8)?),
entry_stride,
entry_stride_hex: format!("0x{entry_stride:x}"),
header_prefix_word_count: 11,
observed_entry_capacity,
observed_entry_count,
zero_trailer_entry_count,
nonzero_trailer_entry_count,
distinct_trailer_words,
distinct_trailer_hex_words,
zero_trailer_entry_names,
entries_offset,
entries_end_offset,
trailing_footer_hex,
footer_progress_word_0,
footer_progress_word_0_hex: format!("0x{footer_progress_word_0:08x}"),
footer_progress_word_1,
footer_progress_word_1_hex: format!("0x{footer_progress_word_1:08x}"),
footer_trailing_byte,
footer_trailing_byte_hex: format!("0x{footer_trailing_byte:02x}"),
footer_grounded_alignments,
entries,
evidence,
})
}
fn matches_candidate_availability_table_header(bytes: &[u8], header_offset: usize) -> bool {
matches!(
(
read_u32_at(bytes, header_offset + 0x08),
read_u32_at(bytes, header_offset + 0x0c),
read_u32_at(bytes, header_offset + 0x10),
read_u32_at(bytes, header_offset + 0x14),
read_u32_at(bytes, header_offset + 0x18),
read_u32_at(bytes, header_offset + 0x1c),
read_u32_at(bytes, header_offset + 0x20),
read_u32_at(bytes, header_offset + 0x24),
read_u32_at(bytes, header_offset + 0x28),
),
(
Some(0x0000332e),
Some(0x00000001),
Some(0x00000022),
Some(0x00000002),
Some(0x00000002),
Some(_),
Some(_),
Some(0x00000000),
Some(0x00000001),
)
)
}
fn classify_name_table_footer_progress_alignment(value: u32) -> Option<&'static str> {
match value {
0x32dc => Some(
"Footer progress word 0x000032dc matches the grounded late rehydrate progress id 0x32dc.",
),
0x3714 => Some(
"Footer progress word 0x00003714 matches the grounded late rehydrate progress id 0x3714.",
),
_ => None,
}
}
fn parse_rt3_105_packed_profile_block(
bytes: &[u8],
packed_profile_offset: usize,
packed_profile_len: usize,
) -> Option<SmpRt3105PackedProfileBlock> {
let block_end = packed_profile_offset.checked_add(packed_profile_len)?;
if block_end > bytes.len() || packed_profile_len != 0x108 {
return None;
}
let leading_word_0 = read_u32_at(bytes, packed_profile_offset)?;
let trailing_zero_word_count_after_leading_word = (1..4)
.take_while(|index| {
read_u32_at(bytes, packed_profile_offset + (index * 4)).is_some_and(|word| word == 0)
})
.count();
let header_flag_word_3 = read_u32_at(bytes, packed_profile_offset + 0x0c)?;
let map_path_offset = 0x10usize;
let display_name_offset = 0x43usize;
let stable_nonzero_word_offsets = [0x00usize, 0x0c, 0x78, 0x7c, 0x80, 0x84];
let stable_nonzero_words = stable_nonzero_word_offsets
.iter()
.filter_map(|relative_offset| {
let value = read_u32_at(bytes, packed_profile_offset + relative_offset)?;
if value == 0 {
return None;
}
Some(SmpPackedProfileWordLane {
relative_offset: *relative_offset,
relative_offset_hex: format!("0x{relative_offset:02x}"),
value,
value_hex: format!("0x{value:08x}"),
})
})
.collect::<Vec<_>>();
Some(SmpRt3105PackedProfileBlock {
relative_len: packed_profile_len,
relative_len_hex: format!("0x{packed_profile_len:03x}"),
leading_word_0,
leading_word_0_hex: format!("0x{leading_word_0:08x}"),
trailing_zero_word_count_after_leading_word,
header_flag_word_3,
header_flag_word_3_hex: format!("0x{header_flag_word_3:08x}"),
map_path_offset,
map_path: read_c_string_in_range(bytes, packed_profile_offset + map_path_offset, block_end),
display_name_offset,
display_name: read_c_string_in_range(
bytes,
packed_profile_offset + display_name_offset,
block_end,
),
profile_byte_0x77: bytes[packed_profile_offset + 0x77],
profile_byte_0x77_hex: format!("0x{:02x}", bytes[packed_profile_offset + 0x77]),
profile_byte_0x82: bytes[packed_profile_offset + 0x82],
profile_byte_0x82_hex: format!("0x{:02x}", bytes[packed_profile_offset + 0x82]),
profile_byte_0x97: bytes[packed_profile_offset + 0x97],
profile_byte_0x97_hex: format!("0x{:02x}", bytes[packed_profile_offset + 0x97]),
profile_byte_0xc5: bytes[packed_profile_offset + 0xc5],
profile_byte_0xc5_hex: format!("0x{:02x}", bytes[packed_profile_offset + 0xc5]),
stable_nonzero_words,
})
}
fn collect_runtime_post_span_header_candidates(
bytes: &[u8],
start: usize,
search_len: usize,
) -> Vec<SmpRuntimePostSpanHeaderCandidate> {
let end = bytes.len().min(start + search_len);
let mut offset = start & !0x3;
let mut candidates = Vec::new();
while offset + 16 <= end && candidates.len() < 8 {
if let Some(candidate) = build_runtime_post_span_header_candidate(bytes, offset) {
let mut cluster_end = offset + 4;
while cluster_end + 16 <= end
&& build_runtime_post_span_header_candidate(bytes, cluster_end).is_some()
{
cluster_end += 4;
}
candidates.push(candidate);
offset = cluster_end;
} else {
offset += 4;
}
}
candidates
}
fn build_runtime_post_span_header_candidate(
bytes: &[u8],
offset: usize,
) -> Option<SmpRuntimePostSpanHeaderCandidate> {
let words = read_u32_window(bytes, offset, 4);
if words.len() < 4 {
return None;
}
let dense_words = words
.iter()
.copied()
.filter(|word| (word & 0xffff) == 0 && (word >> 16) != 0)
.collect::<Vec<_>>();
if dense_words.len() < 3 {
return None;
}
let high_u16_words = words
.iter()
.map(|word| (word >> 16) as u16)
.collect::<Vec<_>>();
let mut grounded_alignments = Vec::new();
for high in &high_u16_words {
if let Some(alignment) = classify_runtime_post_span_high16_grounded_alignment(*high) {
if !grounded_alignments
.iter()
.any(|existing| existing == alignment)
{
grounded_alignments.push(alignment.to_string());
}
}
}
Some(SmpRuntimePostSpanHeaderCandidate {
offset,
hex_words: words.iter().map(|word| format!("0x{word:08x}")).collect(),
dense_word_count: dense_words.len(),
high_hex_words: high_u16_words
.iter()
.map(|word| format!("0x{word:04x}"))
.collect(),
high_u16_words,
grounded_alignments,
words,
})
}
fn classify_runtime_post_span_high16_grounded_alignment(high_u16: u16) -> Option<&'static str> {
match high_u16 {
0x32dc => Some(
"High-16 value 0x32dc matches the grounded late rehydrate progress id posted during world_entry_transition_and_runtime_bringup.",
),
0x3714 => Some(
"High-16 value 0x3714 matches the grounded late rehydrate progress id posted during world_entry_transition_and_runtime_bringup.",
),
0x3715 => Some(
"High-16 value 0x3715 matches the grounded late rehydrate progress id posted during world_entry_transition_and_runtime_bringup.",
),
_ => None,
}
}
fn find_grounded_progress_high16_hits(
bytes: &[u8],
start: usize,
search_len: usize,
) -> Vec<String> {
let end = bytes.len().min(start + search_len);
let mut hits = Vec::new();
let mut offset = start & !0x3;
while offset + 4 <= end {
if let Some(word) = read_u32_at(bytes, offset) {
let high = (word >> 16) as u16;
if matches!(high, 0x32dc | 0x3714 | 0x3715) {
hits.push(format!("0x{high:04x}@0x{offset:08x}"));
}
}
offset += 4;
}
hits
}
fn parse_grounded_progress_hit_offset(hits: &[String], high_u16: u16) -> Option<usize> {
let needle = format!("0x{high_u16:04x}@0x");
let hit = hits.iter().find(|hit| hit.starts_with(&needle))?;
let offset_hex = hit.split("@0x").nth(1)?;
usize::from_str_radix(offset_hex, 16).ok()
}
fn collect_ascii_previews_in_range(
bytes: &[u8],
start: usize,
end: usize,
min_len: usize,
) -> Vec<SmpAsciiPreview> {
let mut previews = Vec::new();
let mut run_start = None;
let end = end.min(bytes.len());
for index in start..end {
let byte = bytes[index];
if is_ascii_preview_byte(byte) {
run_start.get_or_insert(index);
continue;
}
if let Some(current_start) = run_start.take() {
if index - current_start >= min_len {
previews.push(build_ascii_preview(bytes, current_start, index));
}
}
}
if let Some(current_start) = run_start {
if end - current_start >= min_len {
previews.push(build_ascii_preview(bytes, current_start, end));
}
}
previews
}
fn find_c_string_with_suffix_in_range(
bytes: &[u8],
start: usize,
end: usize,
suffix: &str,
) -> Option<usize> {
let end = end.min(bytes.len());
let suffix = suffix.as_bytes();
let mut offset = start.min(end);
while offset < end {
if !is_ascii_preview_byte(bytes[offset]) {
offset += 1;
continue;
}
let run_start = offset;
while offset < end && is_ascii_preview_byte(bytes[offset]) {
offset += 1;
}
let run = &bytes[run_start..offset];
if run.ends_with(suffix) {
return Some(run_start);
}
}
None
}
fn read_c_string_in_range(bytes: &[u8], start: usize, end: usize) -> Option<String> {
if start >= end || start >= bytes.len() {
return None;
}
let end = end.min(bytes.len());
let mut cursor = start;
while cursor < end && bytes[cursor] != 0 {
cursor += 1;
}
if cursor == start {
return None;
}
std::str::from_utf8(&bytes[start..cursor])
.ok()
.map(ToString::to_string)
}
fn find_u16_le_offsets(bytes: &[u8], needle: u16) -> Vec<usize> {
let pattern = needle.to_le_bytes();
bytes
.windows(pattern.len())
.enumerate()
.filter_map(|(offset, window)| (window == pattern).then_some(offset))
.collect()
}
fn find_next_nonzero_offset(bytes: &[u8], start: usize) -> Option<usize> {
bytes
.iter()
.enumerate()
.skip(start)
.find_map(|(offset, byte)| (*byte != 0).then_some(offset))
}
fn find_zero_run(bytes: &[u8], start: usize, min_len: usize) -> Option<usize> {
let mut run_start = None;
let mut run_len = 0usize;
for (offset, byte) in bytes.iter().enumerate().skip(start) {
if *byte == 0 {
run_start.get_or_insert(offset);
run_len += 1;
if run_len >= min_len {
return run_start;
}
} else {
run_start = None;
run_len = 0;
}
}
None
}
fn find_first_ascii_run(bytes: &[u8]) -> Option<SmpAsciiPreview> {
let mut start = None;
for (index, byte) in bytes.iter().copied().enumerate() {
if is_ascii_preview_byte(byte) {
start.get_or_insert(index);
continue;
}
if let Some(run_start) = start.take() {
if index - run_start >= MIN_ASCII_RUN_LEN {
return Some(build_ascii_preview(bytes, run_start, index));
}
}
}
start.and_then(|run_start| {
if bytes.len() - run_start >= MIN_ASCII_RUN_LEN {
Some(build_ascii_preview(bytes, run_start, bytes.len()))
} else {
None
}
})
}
fn build_ascii_preview(bytes: &[u8], start: usize, end: usize) -> SmpAsciiPreview {
let byte_len = end - start;
let preview_bytes = &bytes[start..end];
let preview = String::from_utf8_lossy(
&preview_bytes[..preview_bytes.len().min(ASCII_PREVIEW_CHAR_LIMIT)],
)
.into_owned();
SmpAsciiPreview {
offset: start,
byte_len,
truncated: byte_len > ASCII_PREVIEW_CHAR_LIMIT,
preview,
}
}
fn is_ascii_preview_byte(byte: u8) -> bool {
matches!(byte, b' ' | b'\t' | b'\n' | b'\r' | 0x21..=0x7e)
}
fn read_u32_window(bytes: &[u8], offset: usize, count: usize) -> Vec<u32> {
let mut words = Vec::new();
let end = bytes.len().min(offset + count * 4);
for chunk in bytes[offset..end].chunks_exact(4) {
words.push(u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]));
}
words
}
fn read_u32_at(bytes: &[u8], offset: usize) -> Option<u32> {
let chunk = bytes.get(offset..offset + 4)?;
Some(u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
}
fn hex_encode(bytes: &[u8]) -> String {
bytes.iter().map(|byte| format!("{byte:02x}")).collect()
}
fn sha256_hex(bytes: &[u8]) -> String {
let digest = Sha256::digest(bytes);
format!("{digest:x}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reports_grounded_tag_hits_and_offsets() {
let bytes = [
0x34, 0x12, 0x00, 0x00, 0xe0, 0x2e, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x80,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x71, 0x07, 0x00, 0x00, 0x71, 0x07, 0x00, 0x00,
0x71, 0x07, 0x00, 0x00, 0xaa, 0xbb, 0x00, 0x00, 0xcc, 0xdd, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, b'H', b'e', b'l',
b'l', b'o', b' ', b'R', b'R', b'T', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xde, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xee, 0x2c, 0x11, 0x51, 0x2d, 0x22,
0x71, 0x94, 0x33, 0x72, 0x94,
];
let report = inspect_smp_bytes(&bytes);
assert!(report.contains_grounded_runtime_tags);
assert_eq!(report.known_tag_hits.len(), 4);
assert_eq!(report.preamble.word_count, 16);
assert_eq!(report.preamble.words[0].value_le, 0x00001234);
let shared_header = report
.shared_header
.as_ref()
.expect("shared header should parse");
assert!(shared_header.matches_grounded_common_signature);
let header_variant = report
.header_variant_probe
.as_ref()
.expect("header variant probe should exist");
assert_eq!(header_variant.variant_family, "unknown");
assert!(!header_variant.is_known_family);
assert_eq!(shared_header.primary_family_tag, 0x00002ee0);
assert_eq!(
shared_header.payload_window_words_8_to_9,
vec![0x0000bbaa, 0x0000ddcc]
);
assert!(shared_header.reserved_words_10_to_14_all_zero);
assert_eq!(shared_header.final_flag_word, 0);
let ascii_run = report
.first_ascii_run
.as_ref()
.expect("ascii run should exist");
assert_eq!(ascii_run.offset, 67);
assert_eq!(ascii_run.byte_len, 9);
assert_eq!(ascii_run.preview, "Hello RRT");
let early_probe = report
.early_content_probe
.as_ref()
.expect("early content probe should exist");
assert_eq!(early_probe.first_post_text_nonzero_offset, 88);
assert_eq!(early_probe.zero_pad_after_text_len, 12);
assert_eq!(early_probe.first_post_text_block_len, 4);
assert_eq!(early_probe.first_post_text_block_hex, "11223344");
assert_eq!(early_probe.trailing_zero_pad_after_first_block_len, 16);
assert_eq!(early_probe.secondary_nonzero_offset, Some(108));
assert_eq!(early_probe.secondary_aligned_word_window_offset, Some(108));
assert_eq!(
&early_probe.secondary_aligned_word_window_words[..2],
&[0x78563412, 0xf0debc9a]
);
assert!(
early_probe
.secondary_preview_hex
.starts_with("123456789abcdef0")
);
let secondary_variant = report
.secondary_variant_probe
.as_ref()
.expect("secondary variant probe should exist");
assert_eq!(secondary_variant.variant_family, "unknown");
let container_profile = report
.container_profile
.as_ref()
.expect("container profile should exist");
assert_eq!(container_profile.profile_family, "unknown");
assert!(!container_profile.is_known_profile);
assert!(report.save_bootstrap_block.is_none());
assert!(report.save_anchor_run_block.is_none());
assert!(report.runtime_anchor_cycle_block.is_none());
assert!(report.runtime_trailer_block.is_none());
assert!(report.runtime_post_span_probe.is_none());
assert!(report.classic_rehydrate_profile_probe.is_none());
assert_eq!(report.known_tag_hits[0].tag_id, 0x2cee);
assert_eq!(report.known_tag_hits[0].hit_count, 1);
assert_eq!(report.known_tag_hits[0].sample_offsets, vec![120]);
assert_eq!(report.known_tag_hits[1].tag_id, 0x2d51);
assert_eq!(report.known_tag_hits[1].sample_offsets, vec![123]);
assert_eq!(report.known_tag_hits[2].tag_id, 0x9471);
assert_eq!(report.known_tag_hits[2].sample_offsets, vec![126]);
assert_eq!(report.known_tag_hits[3].tag_id, 0x9472);
assert_eq!(report.known_tag_hits[3].sample_offsets, vec![129]);
}
#[test]
fn warns_when_no_grounded_tags_are_present() {
let report = inspect_smp_bytes(&[0xaa, 0xbb, 0xcc]);
assert!(!report.contains_grounded_runtime_tags);
assert!(report.known_tag_hits.is_empty());
assert_eq!(report.preamble.word_count, 0);
assert!(report.shared_header.is_none());
assert!(report.header_variant_probe.is_none());
assert!(report.first_ascii_run.is_none());
assert!(report.early_content_probe.is_none());
assert!(report.secondary_variant_probe.is_none());
assert!(report.container_profile.is_none());
assert!(report.save_bootstrap_block.is_none());
assert!(report.save_anchor_run_block.is_none());
assert!(report.runtime_anchor_cycle_block.is_none());
assert!(report.runtime_trailer_block.is_none());
assert!(report.runtime_post_span_probe.is_none());
assert!(report.classic_rehydrate_profile_probe.is_none());
assert!(
report
.warnings
.iter()
.any(|warning| warning.contains("No grounded runtime bundle tags were found"))
);
}
#[test]
fn parses_save_anchor_cycle_and_trailer() {
let cycle_words: [u32; 9] = [
0x00000000, 0x0186a000, 0x00000000, 0x86a00000, 0x00000001, 0xa0000000, 0x00000186,
0x00000000, 0x000186a0,
];
let trailer_words: [u32; 3] = [0x00020000, 0x00030000, 0x2ee10000];
let mut bytes = vec![0u8; 0x1c + (cycle_words.len() * 2 + 2 + trailer_words.len()) * 4];
let mut cursor = 0x1c;
for _ in 0..2 {
for word in cycle_words {
bytes[cursor..cursor + 4].copy_from_slice(&word.to_le_bytes());
cursor += 4;
}
}
for word in &cycle_words[..2] {
bytes[cursor..cursor + 4].copy_from_slice(&(*word).to_le_bytes());
cursor += 4;
}
for word in trailer_words {
bytes[cursor..cursor + 4].copy_from_slice(&word.to_le_bytes());
cursor += 4;
}
let container_profile = SmpContainerProfile {
profile_family: "rt3-classic-save-container-v1".to_string(),
profile_evidence: vec!["test".to_string()],
is_known_profile: true,
};
let bootstrap = SmpSaveBootstrapBlock {
profile_family: "rt3-classic-save-container-v1".to_string(),
aligned_window_offset: 0,
leading_word: 0,
leading_word_hex: "0x00000000".to_string(),
anchor_word: 0,
anchor_word_hex: "0x00000000".to_string(),
descriptor_word_2: 0,
descriptor_word_2_hex: "0x00000000".to_string(),
descriptor_word_3: 0,
descriptor_word_3_hex: "0x00000000".to_string(),
descriptor_word_4: 0,
descriptor_word_4_hex: "0x00000000".to_string(),
descriptor_word_5: 0,
descriptor_word_5_hex: "0x00000000".to_string(),
descriptor_word_6: 0,
descriptor_word_6_hex: "0x00000000".to_string(),
descriptor_word_7: 0,
descriptor_word_7_hex: "0x00000000".to_string(),
};
let parsed =
parse_save_anchor_run_block(&bytes, Some(&container_profile), Some(&bootstrap))
.expect("cycle block should parse");
assert_eq!(parsed.cycle_start_offset, 0x1c);
assert_eq!(parsed.cycle_words, cycle_words);
assert_eq!(parsed.full_cycle_count, 2);
assert_eq!(parsed.partial_cycle_word_count, 2);
assert_eq!(
parsed.trailer_offset,
0x1c + (cycle_words.len() * 2 + 2) * 4
);
assert_eq!(parsed.trailer_words, trailer_words);
}
#[test]
fn classifies_runtime_trailer_family() {
let runtime_anchor_cycle_block = SmpRuntimeAnchorCycleBlock {
profile_family: "rt3-classic-sandbox-container-v1".to_string(),
cycle_start_offset: 0x33c,
cycle_words: vec![0; 9],
cycle_hex_words: vec!["0x00000000".to_string(); 9],
full_cycle_count: 3,
partial_cycle_word_count: 2,
trailer_offset: 0x3b0,
trailer_words: vec![
0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00000000, 0x00000000, 0x2ee10000,
0x32c80000, 0x0dcd0000, 0x01010107, 0x26010000, 0x01010107, 0x00010000, 0x0334c68c,
0x03000000, 0x01000000,
],
trailer_hex_words: Vec::new(),
};
let container_profile = SmpContainerProfile {
profile_family: "rt3-classic-sandbox-container-v1".to_string(),
profile_evidence: vec!["test".to_string()],
is_known_profile: true,
};
let trailer = parse_runtime_trailer_block(
Some(&container_profile),
Some(&runtime_anchor_cycle_block),
)
.expect("runtime trailer should parse");
assert_eq!(trailer.trailer_family, "rt3-classic-sandbox-trailer-v1");
assert_eq!(trailer.prefix_words_0_to_5[0], 0x00010000);
assert_eq!(trailer.tag_word_6, 0x2ee10000);
assert_eq!(trailer.tag_chunk_id_u16, 0x2ee1);
assert_eq!(trailer.selector_word_8, 0x0dcd0000);
assert_eq!(trailer.selector_high_u16, 0x0dcd);
assert_eq!(trailer.mode_word_15, 0x01000000);
}
#[test]
fn probes_runtime_post_span_region() {
let mut bytes = vec![0u8; 0x200];
bytes[0x90..0x94].copy_from_slice(&0x32dc0000u32.to_le_bytes());
bytes[0x94..0x98].copy_from_slice(&0x37140000u32.to_le_bytes());
bytes[0x98..0x9c].copy_from_slice(&0x03000000u32.to_le_bytes());
bytes[0xa0..0xa4].copy_from_slice(&0x37150000u32.to_le_bytes());
bytes[0xa4..0xa8].copy_from_slice(&0x00010000u32.to_le_bytes());
bytes[0xa8..0xac].copy_from_slice(&0x00410000u32.to_le_bytes());
let trailer = SmpRuntimeTrailerBlock {
profile_family: "rt3-classic-save-container-v1".to_string(),
trailer_family: "test".to_string(),
trailer_evidence: Vec::new(),
trailer_offset: 0x40,
prefix_words_0_to_5: Vec::new(),
prefix_hex_words_0_to_5: Vec::new(),
tag_word_6: 0x2ee10000,
tag_word_6_hex: "0x2ee10000".to_string(),
tag_chunk_id_u16: 0x2ee1,
tag_chunk_id_hex: "0x2ee1".to_string(),
tag_chunk_id_grounded_alignment: None,
length_word_7: 0x00200000,
length_word_7_hex: "0x00200000".to_string(),
length_high_u16: 0x0020,
length_high_hex: "0x0020".to_string(),
selector_word_8: 0,
selector_word_8_hex: "0x00000000".to_string(),
selector_high_u16: 0,
selector_high_hex: "0x0000".to_string(),
layout_word_9: 0,
layout_word_9_hex: "0x00000000".to_string(),
descriptor_word_10: 0,
descriptor_word_10_hex: "0x00000000".to_string(),
descriptor_high_u16: 0,
descriptor_high_hex: "0x0000".to_string(),
descriptor_word_11: 0,
descriptor_word_11_hex: "0x00000000".to_string(),
counter_word_12: 0,
counter_word_12_hex: "0x00000000".to_string(),
offset_word_13: 0,
offset_word_13_hex: "0x00000000".to_string(),
span_word_14: 0,
span_word_14_hex: "0x00000000".to_string(),
mode_word_15: 0,
mode_word_15_hex: "0x00000000".to_string(),
words: Vec::new(),
hex_words: Vec::new(),
};
let probe = parse_runtime_post_span_probe(&bytes, Some(&trailer))
.expect("post-span probe should parse");
assert_eq!(probe.span_target_offset, 0x60);
assert_eq!(probe.next_nonzero_offset, Some(0x92));
assert_eq!(probe.next_aligned_candidate_offset, Some(0x8c));
assert_eq!(probe.header_candidates.len(), 1);
assert_eq!(probe.header_candidates[0].dense_word_count, 3);
assert_eq!(probe.header_candidates[0].grounded_alignments.len(), 2);
assert_eq!(probe.grounded_progress_hits[0], "0x32dc@0x00000090");
}
#[test]
fn parses_classic_rehydrate_profile_probe() {
let mut bytes = vec![0u8; 0x220];
bytes[0x90..0x94].copy_from_slice(&0x32dc0000u32.to_le_bytes());
bytes[0x94..0x98].copy_from_slice(&0x37140000u32.to_le_bytes());
bytes[0x1a0..0x1a4].copy_from_slice(&0x37150000u32.to_le_bytes());
bytes[0xab..0xb7].copy_from_slice(b"test-map.gmp");
bytes[0xde..0xe6].copy_from_slice(b"Test Map");
let post_span = SmpRuntimePostSpanProbe {
profile_family: "rt3-classic-save-container-v1".to_string(),
span_target_offset: 0,
next_nonzero_offset: Some(0x92),
next_aligned_candidate_offset: Some(0x8c),
next_aligned_candidate_words: vec![0, 0x32dc0000, 0x37140000, 0x03000000],
next_aligned_candidate_hex_words: vec![],
header_candidates: vec![],
grounded_progress_hits: vec![
"0x32dc@0x00000090".to_string(),
"0x3714@0x00000094".to_string(),
"0x3715@0x000001a0".to_string(),
],
};
let probe = parse_classic_rehydrate_profile_probe(&bytes, Some(&post_span))
.expect("classic rehydrate probe should parse");
assert_eq!(probe.packed_profile_offset, 0x98);
assert_eq!(probe.packed_profile_len, 0x108);
assert_eq!(probe.ascii_runs[0].preview, "test-map.gmp");
assert_eq!(probe.packed_profile_block.leading_word_0, 0x00000000);
assert_eq!(
probe.packed_profile_block.map_path.as_deref(),
Some("test-map.gmp")
);
assert_eq!(
probe.packed_profile_block.display_name.as_deref(),
Some("Test Map")
);
assert_eq!(probe.packed_profile_block.profile_byte_0x77, 0x00);
assert_eq!(probe.packed_profile_block.profile_byte_0x82, 0x00);
assert_eq!(probe.packed_profile_block.profile_byte_0x97, 0x00);
assert_eq!(probe.packed_profile_block.profile_byte_0xc5, 0x00);
}
#[test]
fn parses_rt3_105_packed_profile_probe() {
let mut bytes = vec![0u8; 0x9000];
let block = 0x73c0usize;
bytes[block..block + 4].copy_from_slice(&0x00000003u32.to_le_bytes());
bytes[block + 0x0c..block + 0x10].copy_from_slice(&0x01000000u32.to_le_bytes());
bytes[block + 0x10..block + 0x1d].copy_from_slice(b"test-105.gmp\0");
bytes[block + 0x43..block + 0x4c].copy_from_slice(b"Test 105\0");
bytes[block + 0x77] = 0x07;
bytes[block + 0x82] = 0x4d;
bytes[block + 0x84..block + 0x88].copy_from_slice(&0x65010000u32.to_le_bytes());
let header_variant_probe = SmpHeaderVariantProbe {
variant_family: "rt3-105-common-header-v1".to_string(),
variant_evidence: vec![],
is_known_family: true,
};
let probe = parse_rt3_105_packed_profile_probe(
&bytes,
Some("gms"),
Some(&header_variant_probe),
None,
)
.expect("1.05 packed profile probe should parse");
assert_eq!(probe.profile_family, "rt3-105-save-analog-block-inferred");
assert_eq!(probe.packed_profile_offset, 0x73c0);
assert_eq!(probe.packed_profile_len, 0x108);
assert_eq!(
probe.packed_profile_block.map_path.as_deref(),
Some("test-105.gmp")
);
assert_eq!(
probe.packed_profile_block.display_name.as_deref(),
Some("Test 105")
);
assert_eq!(probe.packed_profile_block.profile_byte_0x77, 0x07);
assert_eq!(probe.packed_profile_block.profile_byte_0x82, 0x4d);
assert_eq!(probe.packed_profile_block.profile_byte_0x97, 0x00);
assert_eq!(probe.packed_profile_block.profile_byte_0xc5, 0x00);
}
#[test]
fn classifies_rt3_105_post_span_bridge_variants() {
let base_trailer = SmpRuntimeTrailerBlock {
profile_family: "rt3-105-save-container-v1".to_string(),
trailer_family: "rt3-105-save-trailer-v1".to_string(),
trailer_evidence: vec![],
trailer_offset: 944,
prefix_words_0_to_5: vec![],
prefix_hex_words_0_to_5: vec![],
tag_word_6: 0,
tag_word_6_hex: String::new(),
tag_chunk_id_u16: 0x2ee1,
tag_chunk_id_hex: "0x2ee1".to_string(),
tag_chunk_id_grounded_alignment: None,
length_word_7: 0x32c8_0000,
length_word_7_hex: "0x32c80000".to_string(),
length_high_u16: 0x32c8,
length_high_hex: "0x32c8".to_string(),
selector_word_8: 0x7110_0000,
selector_word_8_hex: "0x71100000".to_string(),
selector_high_u16: 0x7110,
selector_high_hex: "0x7110".to_string(),
layout_word_9: 0,
layout_word_9_hex: String::new(),
descriptor_word_10: 0x7801_0000,
descriptor_word_10_hex: "0x78010000".to_string(),
descriptor_high_u16: 0x7801,
descriptor_high_hex: "0x7801".to_string(),
descriptor_word_11: 0,
descriptor_word_11_hex: String::new(),
counter_word_12: 0,
counter_word_12_hex: String::new(),
offset_word_13: 0,
offset_word_13_hex: String::new(),
span_word_14: 0,
span_word_14_hex: String::new(),
mode_word_15: 0,
mode_word_15_hex: String::new(),
words: vec![],
hex_words: vec![],
};
let base_post_span = SmpRuntimePostSpanProbe {
profile_family: "rt3-105-save-container-v1".to_string(),
span_target_offset: 13944,
next_nonzero_offset: Some(14795),
next_aligned_candidate_offset: Some(20244),
next_aligned_candidate_words: vec![],
next_aligned_candidate_hex_words: vec![],
header_candidates: vec![SmpRuntimePostSpanHeaderCandidate {
offset: 20244,
words: vec![],
hex_words: vec![],
dense_word_count: 3,
high_u16_words: vec![0x6200, 0x0000, 0xfff7, 0x5515],
high_hex_words: vec![],
grounded_alignments: vec![],
}],
grounded_progress_hits: vec![],
};
let base_profile = SmpRt3105PackedProfileProbe {
profile_family: "rt3-105-save-container-v1".to_string(),
packed_profile_offset: 29632,
packed_profile_len: 0x108,
packed_profile_len_hex: "0x108".to_string(),
packed_profile_block: SmpRt3105PackedProfileBlock {
relative_len: 0x108,
relative_len_hex: "0x108".to_string(),
leading_word_0: 3,
leading_word_0_hex: "0x00000003".to_string(),
trailing_zero_word_count_after_leading_word: 2,
header_flag_word_3: 0x0100_0000,
header_flag_word_3_hex: "0x01000000".to_string(),
map_path_offset: 0x10,
map_path: Some("Alternate USA.gmp".to_string()),
display_name_offset: 0x43,
display_name: Some("Alternate USA".to_string()),
profile_byte_0x77: 0x07,
profile_byte_0x77_hex: "0x07".to_string(),
profile_byte_0x82: 0x4d,
profile_byte_0x82_hex: "0x4d".to_string(),
profile_byte_0x97: 0,
profile_byte_0x97_hex: "0x00".to_string(),
profile_byte_0xc5: 0,
profile_byte_0xc5_hex: "0x00".to_string(),
stable_nonzero_words: vec![],
},
ascii_runs: vec![],
};
let base_bridge = parse_rt3_105_post_span_bridge_probe(
Some(&base_trailer),
Some(&base_post_span),
Some(&base_profile),
)
.expect("base bridge should parse");
assert_eq!(
base_bridge.bridge_family,
"rt3-105-save-post-span-bridge-v1"
);
assert_eq!(base_bridge.packed_profile_delta_from_span_target, 15688);
assert_eq!(
base_bridge.next_candidate_delta_from_packed_profile,
Some(-9388)
);
let base_variant_trailer = SmpRuntimeTrailerBlock {
descriptor_word_10: 0x7401_0000,
descriptor_word_10_hex: "0x74010000".to_string(),
descriptor_high_u16: 0x7401,
descriptor_high_hex: "0x7401".to_string(),
..base_trailer.clone()
};
let base_variant_bridge = parse_rt3_105_post_span_bridge_probe(
Some(&base_variant_trailer),
Some(&base_post_span),
Some(&base_profile),
)
.expect("base bridge variant should parse");
assert_eq!(
base_variant_bridge.bridge_family,
"rt3-105-save-post-span-bridge-v1"
);
let alt_trailer = SmpRuntimeTrailerBlock {
profile_family: "rt3-105-alt-save-container-v1".to_string(),
selector_word_8: 0x54cd_0000,
selector_word_8_hex: "0x54cd0000".to_string(),
selector_high_u16: 0x54cd,
selector_high_hex: "0x54cd".to_string(),
descriptor_word_10: 0x5901_0000,
descriptor_word_10_hex: "0x59010000".to_string(),
descriptor_high_u16: 0x5901,
descriptor_high_hex: "0x5901".to_string(),
..base_trailer.clone()
};
let alt_post_span = SmpRuntimePostSpanProbe {
profile_family: "rt3-105-alt-save-container-v1".to_string(),
next_aligned_candidate_offset: Some(29892),
header_candidates: vec![SmpRuntimePostSpanHeaderCandidate {
offset: 29892,
words: vec![],
hex_words: vec![],
dense_word_count: 3,
high_u16_words: vec![0x1500, 0x0100, 0x4100, 0x0200],
high_hex_words: vec![],
grounded_alignments: vec![],
}],
..base_post_span.clone()
};
let alt_profile = SmpRt3105PackedProfileProbe {
profile_family: "rt3-105-alt-save-container-v1".to_string(),
packed_profile_block: SmpRt3105PackedProfileBlock {
map_path: Some("Spanish Mainline.gmp".to_string()),
display_name: Some("Spanish Mainline".to_string()),
profile_byte_0x82: 0xa3,
profile_byte_0x82_hex: "0xa3".to_string(),
..base_profile.packed_profile_block.clone()
},
..base_profile.clone()
};
let alt_bridge = parse_rt3_105_post_span_bridge_probe(
Some(&alt_trailer),
Some(&alt_post_span),
Some(&alt_profile),
)
.expect("alt bridge should parse");
assert_eq!(
alt_bridge.bridge_family,
"rt3-105-alt-save-post-span-bridge-v1"
);
assert_eq!(
alt_bridge.next_candidate_delta_from_packed_profile,
Some(260)
);
let scenario_trailer = SmpRuntimeTrailerBlock {
profile_family: "rt3-105-scenario-save-container-v1".to_string(),
trailer_family: "unknown".to_string(),
trailer_offset: 864,
length_word_7: 0,
length_word_7_hex: "0x00000000".to_string(),
length_high_u16: 0,
length_high_hex: "0x0000".to_string(),
selector_word_8: 0x0001_86a0,
selector_word_8_hex: "0x000186a0".to_string(),
selector_high_u16: 0x0001,
selector_high_hex: "0x0001".to_string(),
descriptor_word_10: 0x0186_a000,
descriptor_word_10_hex: "0x0186a000".to_string(),
descriptor_high_u16: 0x0186,
descriptor_high_hex: "0x0186".to_string(),
..base_trailer.clone()
};
let scenario_post_span = SmpRuntimePostSpanProbe {
profile_family: "rt3-105-scenario-save-container-v1".to_string(),
span_target_offset: 864,
next_aligned_candidate_offset: Some(940),
header_candidates: vec![SmpRuntimePostSpanHeaderCandidate {
offset: 940,
words: vec![],
hex_words: vec![],
dense_word_count: 3,
high_u16_words: vec![0x0186, 0x0006, 0x0006, 0x0001],
high_hex_words: vec![],
grounded_alignments: vec![],
}],
..base_post_span.clone()
};
let scenario_profile = SmpRt3105PackedProfileProbe {
profile_family: "rt3-105-scenario-save-container-v1".to_string(),
packed_profile_block: SmpRt3105PackedProfileBlock {
map_path: Some("Southern Pacific.gmp".to_string()),
display_name: Some("Southern Pacific".to_string()),
profile_byte_0x82: 0x90,
profile_byte_0x82_hex: "0x90".to_string(),
..base_profile.packed_profile_block.clone()
},
..base_profile.clone()
};
let scenario_bridge = parse_rt3_105_post_span_bridge_probe(
Some(&scenario_trailer),
Some(&scenario_post_span),
Some(&scenario_profile),
)
.expect("scenario bridge should parse");
assert_eq!(
scenario_bridge.bridge_family,
"rt3-105-scenario-post-span-bridge-v1"
);
assert_eq!(
scenario_bridge.next_candidate_delta_from_packed_profile,
Some(-28692)
);
}
#[test]
fn parses_rt3_105_save_bridge_payload_probe() {
let mut bytes = vec![0u8; 0x7000];
let primary = 0x4f14usize;
let secondary = 0x671cusize;
let primary_words: [u32; 8] = [
0x62000000, 0x00000000, 0xfff70000, 0x55150000, 0x55550000, 0x00000000, 0xfff70000,
0x54550000,
];
for (index, word) in primary_words.iter().enumerate() {
bytes[primary + index * 4..primary + (index + 1) * 4]
.copy_from_slice(&(*word).to_le_bytes());
}
let secondary_words: [u32; 8] = [
0x00050000, 0x00050005, 0xfff70000, 0x54540000, 0x545400f9, 0x00f900f9, 0x00f94008,
0x00001555,
];
for (index, word) in secondary_words.iter().enumerate() {
bytes[secondary + index * 4..secondary + (index + 1) * 4]
.copy_from_slice(&(*word).to_le_bytes());
}
let bridge_probe = SmpRt3105PostSpanBridgeProbe {
profile_family: "rt3-105-save-container-v1".to_string(),
bridge_family: "rt3-105-save-post-span-bridge-v1".to_string(),
bridge_evidence: vec![],
span_target_offset: 0x3678,
next_candidate_offset: Some(primary),
next_candidate_delta_from_span_target: Some(primary - 0x3678),
packed_profile_offset: 0x73c0,
packed_profile_delta_from_span_target: 0x3d48,
next_candidate_delta_from_packed_profile: Some(primary as i64 - 0x73c0),
selector_high_u16: 0x7110,
selector_high_hex: "0x7110".to_string(),
descriptor_high_u16: 0x7801,
descriptor_high_hex: "0x7801".to_string(),
next_candidate_high_u16_words: vec![0x6200, 0x0000, 0xfff7, 0x5515],
next_candidate_high_hex_words: vec![],
};
let probe = parse_rt3_105_save_bridge_payload_probe(&bytes, Some(&bridge_probe))
.expect("save bridge payload probe should parse");
assert_eq!(probe.primary_block_offset, primary);
assert_eq!(probe.primary_block_len, 0x20);
assert_eq!(probe.secondary_block_offset, secondary);
assert_eq!(probe.secondary_block_delta_from_primary, 0x1808);
assert_eq!(probe.secondary_block_end_offset, 0x73c0);
assert_eq!(probe.secondary_block_len, 0xca4);
assert_eq!(probe.primary_words[..4], primary_words[..4]);
assert_eq!(probe.secondary_words[..8], secondary_words[..8]);
}
#[test]
fn parses_rt3_105_save_name_table_probe() {
let mut bytes = vec![0u8; 0x7400];
let secondary = 0x671cusize;
let header = secondary + 0x354;
let entries = secondary + 0x3b5;
let stride = 0x22usize;
let names = ["AluminumMill", "Nuclear Power Plant", "Bakery"];
bytes[header..header + 4].copy_from_slice(&0x10000000u32.to_le_bytes());
bytes[header + 4..header + 8].copy_from_slice(&0x00009000u32.to_le_bytes());
bytes[header + 8..header + 12].copy_from_slice(&0x0000332eu32.to_le_bytes());
bytes[header + 0x1c..header + 0x20].copy_from_slice(&4u32.to_le_bytes());
bytes[header + 0x20..header + 0x24].copy_from_slice(&(names.len() as u32).to_le_bytes());
bytes[header + 12..header + 16].copy_from_slice(&1u32.to_le_bytes());
bytes[header + 16..header + 20].copy_from_slice(&0x22u32.to_le_bytes());
bytes[header + 20..header + 24].copy_from_slice(&2u32.to_le_bytes());
bytes[header + 24..header + 28].copy_from_slice(&2u32.to_le_bytes());
bytes[header + 0x28..header + 0x2c].copy_from_slice(&1u32.to_le_bytes());
for (index, name) in names.iter().enumerate() {
let off = entries + index * stride;
let raw = &mut bytes[off..off + stride];
raw[..name.len()].copy_from_slice(name.as_bytes());
let trailer = if *name == "Nuclear Power Plant" {
0u32
} else {
1u32
};
raw[stride - 4..stride].copy_from_slice(&trailer.to_le_bytes());
}
let footer = entries + names.len() * stride;
bytes[footer..footer + 4].copy_from_slice(&0x32dcu32.to_le_bytes());
bytes[footer + 4..footer + 8].copy_from_slice(&0x3714u32.to_le_bytes());
bytes[footer + 8] = 0x00;
let payload = SmpRt3105SaveBridgePayloadProbe {
profile_family: "rt3-105-save-container-v1".to_string(),
bridge_family: "rt3-105-save-post-span-bridge-v1".to_string(),
primary_block_offset: 0x4f14,
primary_block_len: 0x20,
primary_block_len_hex: "0x20".to_string(),
primary_words: vec![],
primary_hex_words: vec![],
secondary_block_offset: secondary,
secondary_block_delta_from_primary: 0x1808,
secondary_block_delta_from_primary_hex: "0x1808".to_string(),
secondary_block_end_offset: footer + 9,
secondary_block_len: footer + 9 - secondary,
secondary_block_len_hex: format!("0x{:x}", footer + 9 - secondary),
secondary_preview_word_count: 32,
secondary_words: vec![],
secondary_hex_words: vec![],
evidence: vec![],
};
let probe = parse_rt3_105_save_name_table_probe(
&bytes,
Some("gms"),
Some(&SmpContainerProfile {
profile_family: "rt3-105-save-container-v1".to_string(),
profile_evidence: vec![],
is_known_profile: true,
}),
Some(&payload),
)
.expect("save name table probe should parse");
assert_eq!(probe.source_kind, "save-bridge-secondary-block");
assert_eq!(
probe.semantic_family,
"scenario-named-candidate-availability-table"
);
assert_eq!(probe.header_offset, header);
assert_eq!(probe.entry_stride, stride);
assert_eq!(probe.observed_entry_capacity, 4);
assert_eq!(probe.observed_entry_count, names.len());
assert_eq!(probe.entries[0].text, "AluminumMill");
assert_eq!(probe.entries[0].availability_dword, 1);
assert_eq!(probe.entries[2].text, "Bakery");
assert_eq!(probe.zero_trailer_entry_count, 1);
assert_eq!(
probe.zero_trailer_entry_names,
vec!["Nuclear Power Plant".to_string()]
);
assert_eq!(probe.trailing_footer_hex, "dc3200001437000000");
assert_eq!(probe.footer_progress_word_0, 0x32dc);
assert_eq!(probe.footer_progress_word_1, 0x3714);
assert_eq!(probe.footer_trailing_byte, 0x00);
}
#[test]
fn parses_rt3_105_map_name_table_probe_from_fixed_offsets() {
let mut bytes = vec![0u8; 0x7400];
let header = 0x6a70usize;
let entries = 0x6ad1usize;
let stride = 0x22usize;
let observed_entry_count = 67usize;
bytes[header..header + 4].copy_from_slice(&0x00000000u32.to_le_bytes());
bytes[header + 4..header + 8].copy_from_slice(&0x00000000u32.to_le_bytes());
bytes[header + 8..header + 12].copy_from_slice(&0x0000332eu32.to_le_bytes());
bytes[header + 12..header + 16].copy_from_slice(&1u32.to_le_bytes());
bytes[header + 16..header + 20].copy_from_slice(&0x22u32.to_le_bytes());
bytes[header + 20..header + 24].copy_from_slice(&2u32.to_le_bytes());
bytes[header + 24..header + 28].copy_from_slice(&2u32.to_le_bytes());
bytes[header + 0x1c..header + 0x20].copy_from_slice(&0x44u32.to_le_bytes());
bytes[header + 0x20..header + 0x24]
.copy_from_slice(&(observed_entry_count as u32).to_le_bytes());
bytes[header + 0x28..header + 0x2c].copy_from_slice(&1u32.to_le_bytes());
for index in 0..observed_entry_count {
let name = match index {
0 => "AutoPlant".to_string(),
1 => "Nuclear Power Plant".to_string(),
66 => "Warehouse11".to_string(),
_ => format!("Entry{index:02}"),
};
let off = entries + index * stride;
let raw = &mut bytes[off..off + stride];
raw[..name.len()].copy_from_slice(name.as_bytes());
let trailer = if name == "Nuclear Power Plant" {
0u32
} else {
1u32
};
raw[stride - 4..stride].copy_from_slice(&trailer.to_le_bytes());
}
let footer = entries + observed_entry_count * stride;
bytes[footer..footer + 4].copy_from_slice(&0x32dcu32.to_le_bytes());
bytes[footer + 4..footer + 8].copy_from_slice(&0x3714u32.to_le_bytes());
bytes[footer + 8] = 0x00;
let probe = parse_rt3_105_save_name_table_probe(
&bytes,
Some("gmp"),
Some(&SmpContainerProfile {
profile_family: "rt3-105-map-container-v1".to_string(),
profile_evidence: vec![],
is_known_profile: true,
}),
None,
)
.expect("map name table probe should parse");
assert_eq!(probe.profile_family, "rt3-105-map-container-v1");
assert_eq!(probe.source_kind, "map-fixed-catalog-range");
assert_eq!(probe.header_offset, header);
assert_eq!(probe.entries_offset, entries);
assert_eq!(probe.observed_entry_count, observed_entry_count);
assert_eq!(probe.entries[0].text, "AutoPlant");
assert_eq!(probe.entries[66].text, "Warehouse11");
assert_eq!(
probe.zero_trailer_entry_names,
vec!["Nuclear Power Plant".to_string()]
);
assert_eq!(probe.footer_progress_word_0, 0x32dc);
assert_eq!(probe.footer_progress_word_1, 0x3714);
}
#[test]
fn classifies_rt3_105_alt_save_container_profile() {
let shared_header = SmpSharedHeader {
byte_len: 64,
root_kind_word: 0x000025e5,
root_kind_word_hex: "0x000025e5".to_string(),
primary_family_tag: 0x00002ee0,
primary_family_tag_hex: "0x00002ee0".to_string(),
shared_signature_words_1_to_7: vec![
0x00002ee0, 0x0001c001, 0x00018000, 0x00010000, 0x00000754, 0x00000754, 0x00000754,
],
shared_signature_hex_words_1_to_7: vec![
"0x00002ee0".to_string(),
"0x0001c001".to_string(),
"0x00018000".to_string(),
"0x00010000".to_string(),
"0x00000754".to_string(),
"0x00000754".to_string(),
"0x00000754".to_string(),
],
matches_grounded_common_signature: false,
payload_window_words_8_to_9: vec![0x007a5978, 0x007a9022],
payload_window_hex_words_8_to_9: vec![
"0x007a5978".to_string(),
"0x007a9022".to_string(),
],
reserved_words_10_to_14: vec![0; 5],
reserved_words_10_to_14_all_zero: true,
final_flag_word: 0,
final_flag_word_hex: "0x00000000".to_string(),
};
let early_content_probe = SmpEarlyContentProbe {
first_post_text_nonzero_offset: 722,
zero_pad_after_text_len: 431,
first_post_text_block_len: 35,
first_post_text_block_hex:
"0101010000010000000000000100000000000000010000000000000000010100000001".to_string(),
trailing_zero_pad_after_first_block_len: 45,
secondary_nonzero_offset: Some(802),
secondary_aligned_word_window_offset: Some(800),
secondary_aligned_word_window_words: vec![
0x00010000, 0x49f00100, 0x00000002, 0xa0000000, 0x00000186, 0x00000000, 0x000186a0,
0x00000000,
],
secondary_aligned_word_window_hex_words: vec![
"0x00010000".to_string(),
"0x49f00100".to_string(),
"0x00000002".to_string(),
"0xa0000000".to_string(),
"0x00000186".to_string(),
"0x00000000".to_string(),
"0x000186a0".to_string(),
"0x00000000".to_string(),
],
secondary_preview_hex:
"01000001f04902000000000000a08601000000000000a08601000000000000a0".to_string(),
};
let header_variant = classify_header_variant_probe(&shared_header);
let secondary_variant =
classify_secondary_variant_probe(&early_content_probe).expect("secondary probe");
let container_profile = classify_container_profile(
Some("gms"),
Some(&header_variant),
Some(&secondary_variant),
)
.expect("container profile");
assert_eq!(header_variant.variant_family, "rt3-105-alt-save-header-v1");
assert_eq!(
secondary_variant.variant_family,
"rt3-105-gms-alt-family-v1"
);
assert_eq!(
container_profile.profile_family,
"rt3-105-alt-save-container-v1"
);
assert!(container_profile.is_known_profile);
}
#[test]
fn classifies_rt3_105_map_container_profiles_from_header_families() {
let scenario_profile = classify_container_profile(
Some("gmp"),
Some(&SmpHeaderVariantProbe {
variant_family: "rt3-105-scenario-save-header-v1".to_string(),
variant_evidence: vec![],
is_known_family: true,
}),
Some(&SmpSecondaryVariantProbe {
aligned_window_offset: 0,
words: vec![1, 0, 0, 0],
hex_words: vec![],
variant_family: "unknown".to_string(),
variant_evidence: vec![],
}),
)
.expect("scenario map profile");
let alt_profile = classify_container_profile(
Some("gmp"),
Some(&SmpHeaderVariantProbe {
variant_family: "rt3-105-alt-save-header-v1".to_string(),
variant_evidence: vec![],
is_known_family: true,
}),
Some(&SmpSecondaryVariantProbe {
aligned_window_offset: 0,
words: vec![0x49f00100, 2, 0xa0000000, 0x186],
hex_words: vec![],
variant_family: "unknown".to_string(),
variant_evidence: vec![],
}),
)
.expect("alt map profile");
assert_eq!(
scenario_profile.profile_family,
"rt3-105-scenario-map-container-v1"
);
assert!(scenario_profile.is_known_profile);
assert_eq!(alt_profile.profile_family, "rt3-105-alt-map-container-v1");
assert!(alt_profile.is_known_profile);
}
}