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