Inspect non-direct map event runtime rows
This commit is contained in:
parent
a3c4d4e032
commit
93c0c5cb38
2 changed files with 320 additions and 34 deletions
|
|
@ -4781,6 +4781,7 @@ fn build_periodic_company_service_trace_report(
|
|||
"the trigger-kind field itself is now bounded as an ordinary loaded per-event lane rather than a startup-only special class: restore-side loader 0x00433130 repopulates live event collection 0x0062be18 from packed chunk family 0x4e21/0x4e22, and the event-detail editor strip 0x004d90ba..0x004d91ed writes [event+0x7ef] across the full 0x00..0x0a range through controls 0x4e98..0x4ea2, including kind 8 at 0x004d91b3".to_string(),
|
||||
"that keeps 0x00444d92 -> 0x00432f40(kind 8) on the ordinary loaded runtime-effect pipeline too: world bring-up is servicing pre-existing rows from 0x0062be18 rather than a one-off startup-only record class synthesized outside the collection".to_string(),
|
||||
"the event-detail editor family now ties that trigger-kind field to the ordinary runtime-effect builders too: selected-event control family 0x004db02a / 0x004db1b8..0x004db309 mirrors current [event+0x7ef] back into controls 0x4e98..0x4ea2 under root control 0x4e84, while editor-side builder 0x004db9e5..0x004db9f1 allocates a runtime-effect row from compact payload into 0x0062be18 through 0x00432ea0 before rebinding the selected event id".to_string(),
|
||||
"bundle-side inspection now grounds the ordinary startup collection further too: War Effort.gmp exposes a non-direct 0x4e99/0x4e9a/0x4e9b runtime-event collection at 0x74740c/0x7543f4/0x7554cf with 24 live rows, and those rows now segment cleanly as compact 0x526f-delimited bodies with repeated 0x4eb8 grouped-effect markers plus optional 0x4eb9 terminators rather than disappearing behind a missing bundle probe".to_string(),
|
||||
],
|
||||
blockers: vec![
|
||||
"current atlas evidence now grounds one tuple-backed owner path too: loader tuple field [+0x0c] reaches [site+0x276] through 0x0046f073 / 0x004707ff -> 0x0040ef10, but the classified 0x004707ff caller belongs to multiplayer transport selector-0x13 rather than ordinary save-load restore, so a non-transport persisted source family is still needed for shellless acquisition".to_string(),
|
||||
|
|
@ -4788,7 +4789,7 @@ fn build_periodic_company_service_trace_report(
|
|||
"the paired collection-side triplet serializer 0x00413440 is ruled down too, so the missing ordinary restored-row owner seam likely sits outside the currently bounded direct allocator/finalize/store families and the tagged 0x36b1/0x36b2/0x36b3 load-save strip".to_string(),
|
||||
"the load-side stream owner 0x00413280 is ruled down to cached-source/candidate replay through vtable slot +0x40 and 0x0040ce60, so the missing ordinary restored-row owner seam still sits beyond the current stream-load bridge too".to_string(),
|
||||
"the checked ordinary restore ordering is ruled down too: 0x00413280 stream load, 0x00481210 dynamic side-buffer refresh, and 0x004133b0 local-runtime replay all sit on the bring-up strip without re-entering 0x004134d0 / 0x0040f6d0 / 0x0040ef10 for already-restored rows".to_string(),
|
||||
"the grouped opcode dispatcher 0x00431b20 is still not a tagged restore owner, but the remaining uncertainty is narrower now than 'is kind 8 synthetic' or 'does kind 8 live on a separate editor/build class': restore-side 0x00433130 reloads ordinary live event rows into 0x0062be18, the event-detail editor exposes [event+0x7ef] across 0x00..0x0a including kind 8, and the same editor family reaches ordinary runtime-effect allocator 0x00432ea0, so the open question is which loaded kind-8 rows can actually reach the placed-structure mutation opcodes under 0x00431b20".to_string(),
|
||||
"the grouped opcode dispatcher 0x00431b20 is still not a tagged restore owner, but the remaining uncertainty is narrower now than 'is kind 8 synthetic' or 'does kind 8 live on a separate editor/build class': restore-side 0x00433130 reloads ordinary live event rows into 0x0062be18, the event-detail editor exposes [event+0x7ef] across 0x00..0x0a including kind 8, the same editor family reaches ordinary runtime-effect allocator 0x00432ea0, and War Effort.gmp now proves that the bundle-side collection carries 24 compact non-direct 0x526f/0x4eb8(/0x4eb9) row bodies, so the open question is the field mapping inside that compact row family and which loaded kind-8 rows can actually reach the placed-structure mutation opcodes under 0x00431b20".to_string(),
|
||||
],
|
||||
},
|
||||
SmpServiceConsumerHypothesis {
|
||||
|
|
@ -9265,12 +9266,44 @@ fn parse_event_runtime_collection_summary(
|
|||
container_profile: Option<&SmpContainerProfile>,
|
||||
save_load_summary: Option<&SmpSaveLoadSummary>,
|
||||
) -> Option<SmpLoadedEventRuntimeCollectionSummary> {
|
||||
let metadata_offsets = find_u16_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_METADATA_TAG);
|
||||
let record_offsets = find_u16_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_RECORDS_TAG);
|
||||
let close_offsets = find_u16_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_CLOSE_TAG);
|
||||
parse_event_runtime_collection_summary_with_tag_width(
|
||||
bytes,
|
||||
container_profile,
|
||||
save_load_summary,
|
||||
2,
|
||||
)
|
||||
.or_else(|| {
|
||||
parse_event_runtime_collection_summary_with_tag_width(
|
||||
bytes,
|
||||
container_profile,
|
||||
save_load_summary,
|
||||
4,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_event_runtime_collection_summary_with_tag_width(
|
||||
bytes: &[u8],
|
||||
container_profile: Option<&SmpContainerProfile>,
|
||||
save_load_summary: Option<&SmpSaveLoadSummary>,
|
||||
tag_width: usize,
|
||||
) -> Option<SmpLoadedEventRuntimeCollectionSummary> {
|
||||
let (metadata_offsets, record_offsets, close_offsets) = match tag_width {
|
||||
2 => (
|
||||
find_u16_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_METADATA_TAG),
|
||||
find_u16_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_RECORDS_TAG),
|
||||
find_u16_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_CLOSE_TAG),
|
||||
),
|
||||
4 => (
|
||||
find_u32_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_METADATA_TAG as u32),
|
||||
find_u32_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_RECORDS_TAG as u32),
|
||||
find_u32_le_offsets(bytes, EVENT_RUNTIME_COLLECTION_CLOSE_TAG as u32),
|
||||
),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
for metadata_tag_offset in metadata_offsets {
|
||||
let packed_state_version = read_u32_at(bytes, metadata_tag_offset + 2)?;
|
||||
let packed_state_version = read_u32_at(bytes, metadata_tag_offset + tag_width)?;
|
||||
if packed_state_version != EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -9278,12 +9311,13 @@ fn parse_event_runtime_collection_summary(
|
|||
let records_tag_offset = record_offsets
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|offset| *offset > metadata_tag_offset + 6)?;
|
||||
.find(|offset| *offset > metadata_tag_offset + tag_width + 4)?;
|
||||
let close_tag_offset = close_offsets
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|offset| *offset > records_tag_offset)?;
|
||||
let metadata_payload = bytes.get(metadata_tag_offset + 6..records_tag_offset)?;
|
||||
let metadata_payload =
|
||||
bytes.get(metadata_tag_offset + tag_width + 4..records_tag_offset)?;
|
||||
if metadata_payload.len() < INDEXED_COLLECTION_SERIALIZED_HEADER_LEN {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -9295,34 +9329,62 @@ fn parse_event_runtime_collection_summary(
|
|||
let direct_record_stride = usize::try_from(header_words[1]).ok()?;
|
||||
let live_id_bound = header_words[4];
|
||||
let live_record_count = usize::try_from(header_words[5]).ok()?;
|
||||
if direct_collection_flag == 0 || direct_record_stride == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let bitset_len = ((usize::try_from(live_id_bound).ok()?).saturating_add(15)) / 8;
|
||||
let payload_bytes = direct_record_stride.checked_mul(live_record_count)?;
|
||||
if metadata_payload.len() < INDEXED_COLLECTION_SERIALIZED_HEADER_LEN + bitset_len {
|
||||
continue;
|
||||
}
|
||||
if metadata_payload.len() < bitset_len + payload_bytes {
|
||||
continue;
|
||||
}
|
||||
let records_payload = bytes.get(records_tag_offset + tag_width..close_tag_offset)?;
|
||||
let (source_kind, live_entry_ids) = if direct_collection_flag == 0 && tag_width == 4 {
|
||||
(
|
||||
"packed-event-runtime-collection-nondirect".to_string(),
|
||||
(1..=u32::try_from(live_record_count).ok()?).collect::<Vec<_>>(),
|
||||
)
|
||||
} else {
|
||||
if direct_collection_flag == 0 || direct_record_stride == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let bitset_offset = metadata_payload.len() - bitset_len - payload_bytes;
|
||||
if bitset_offset < INDEXED_COLLECTION_SERIALIZED_HEADER_LEN {
|
||||
continue;
|
||||
}
|
||||
let bitset = metadata_payload.get(bitset_offset..bitset_offset + bitset_len)?;
|
||||
let live_entry_ids = decode_live_entry_ids_from_tombstone_bitset(bitset, live_id_bound)?;
|
||||
if live_entry_ids.len() != live_record_count {
|
||||
continue;
|
||||
}
|
||||
let records_payload = bytes.get(records_tag_offset + 2..close_tag_offset)?;
|
||||
let records = parse_event_runtime_record_summaries(
|
||||
records_payload,
|
||||
records_tag_offset + 2,
|
||||
&live_entry_ids,
|
||||
);
|
||||
let bitset_len = ((usize::try_from(live_id_bound).ok()?).saturating_add(15)) / 8;
|
||||
let payload_bytes = direct_record_stride.checked_mul(live_record_count)?;
|
||||
if metadata_payload.len() < INDEXED_COLLECTION_SERIALIZED_HEADER_LEN + bitset_len {
|
||||
continue;
|
||||
}
|
||||
if metadata_payload.len() < bitset_len + payload_bytes {
|
||||
continue;
|
||||
}
|
||||
|
||||
let bitset_offset = metadata_payload.len() - bitset_len - payload_bytes;
|
||||
if bitset_offset < INDEXED_COLLECTION_SERIALIZED_HEADER_LEN {
|
||||
continue;
|
||||
}
|
||||
let bitset = metadata_payload.get(bitset_offset..bitset_offset + bitset_len)?;
|
||||
let live_entry_ids =
|
||||
decode_live_entry_ids_from_tombstone_bitset(bitset, live_id_bound)?;
|
||||
if live_entry_ids.len() != live_record_count {
|
||||
continue;
|
||||
}
|
||||
(
|
||||
"packed-event-runtime-collection".to_string(),
|
||||
live_entry_ids,
|
||||
)
|
||||
};
|
||||
let records = if source_kind == "packed-event-runtime-collection-nondirect" {
|
||||
try_parse_nondirect_event_runtime_record_summaries(
|
||||
records_payload,
|
||||
records_tag_offset + tag_width,
|
||||
&live_entry_ids,
|
||||
)
|
||||
.unwrap_or_else(|| {
|
||||
parse_event_runtime_record_summaries(
|
||||
records_payload,
|
||||
records_tag_offset + tag_width,
|
||||
&live_entry_ids,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
parse_event_runtime_record_summaries(
|
||||
records_payload,
|
||||
records_tag_offset + tag_width,
|
||||
&live_entry_ids,
|
||||
)
|
||||
};
|
||||
let decoded_record_count = records
|
||||
.iter()
|
||||
.filter(|record| record.decode_status != "unsupported_framing")
|
||||
|
|
@ -9333,7 +9395,7 @@ fn parse_event_runtime_collection_summary(
|
|||
.count();
|
||||
|
||||
return Some(SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
source_kind,
|
||||
mechanism_family: save_load_summary
|
||||
.map(|summary| summary.mechanism_family.clone())
|
||||
.unwrap_or_else(|| "unknown".to_string()),
|
||||
|
|
@ -9359,6 +9421,93 @@ fn parse_event_runtime_collection_summary(
|
|||
None
|
||||
}
|
||||
|
||||
fn try_parse_nondirect_event_runtime_record_summaries(
|
||||
records_payload: &[u8],
|
||||
records_payload_offset: usize,
|
||||
live_entry_ids: &[u32],
|
||||
) -> Option<Vec<SmpLoadedPackedEventRecordSummary>> {
|
||||
let marker_offsets = find_u16_le_offsets(records_payload, PACKED_EVENT_REAL_CONDITION_MARKER);
|
||||
if marker_offsets.len() != live_entry_ids.len() || marker_offsets.first().copied() != Some(0) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut record_offsets = marker_offsets;
|
||||
record_offsets.push(records_payload.len());
|
||||
let mut records = Vec::with_capacity(live_entry_ids.len());
|
||||
|
||||
for (record_index, live_entry_id) in live_entry_ids.iter().copied().enumerate() {
|
||||
let start = *record_offsets.get(record_index)?;
|
||||
let end = *record_offsets.get(record_index + 1)?;
|
||||
let record_body = records_payload.get(start..end)?;
|
||||
let grouped_marker_relative_offset =
|
||||
find_u16_le_offsets(record_body, PACKED_EVENT_REAL_GROUPED_EFFECT_MARKER)
|
||||
.into_iter()
|
||||
.next();
|
||||
let end_marker_relative_offset =
|
||||
find_u16_le_offsets(record_body, 0x4eb9).into_iter().next();
|
||||
let head_signature_words = read_u16_window(record_body, 0, 18);
|
||||
let post_group_signature_words = grouped_marker_relative_offset
|
||||
.map(|offset| offset + 2)
|
||||
.map(|offset| read_u16_window(record_body, offset, 12))
|
||||
.unwrap_or_default();
|
||||
let ascii_preview_before_grouped_marker = grouped_marker_relative_offset
|
||||
.and_then(|offset| record_body.get(..offset).map(ascii_preview));
|
||||
|
||||
let mut notes = vec![
|
||||
"decoded from non-direct 0x4e99/0x4e9a/0x4e9b map-bundle row segmentation using 0x526f-delimited slices".to_string(),
|
||||
format!(
|
||||
"head signature u16 words = {}",
|
||||
format_u16_word_signature(&head_signature_words)
|
||||
),
|
||||
];
|
||||
if let Some(offset) = grouped_marker_relative_offset {
|
||||
notes.push(format!(
|
||||
"grouped-effect marker 0x4eb8 at relative offset +0x{offset:x}"
|
||||
));
|
||||
if !post_group_signature_words.is_empty() {
|
||||
notes.push(format!(
|
||||
"post-group signature u16 words = {}",
|
||||
format_u16_word_signature(&post_group_signature_words)
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(offset) = end_marker_relative_offset {
|
||||
notes.push(format!(
|
||||
"row terminator marker 0x4eb9 at relative offset +0x{offset:x}"
|
||||
));
|
||||
}
|
||||
if let Some(preview) = ascii_preview_before_grouped_marker {
|
||||
notes.push(format!("ascii preview before grouped marker = {preview}"));
|
||||
}
|
||||
|
||||
records.push(SmpLoadedPackedEventRecordSummary {
|
||||
record_index,
|
||||
live_entry_id,
|
||||
payload_offset: Some(records_payload_offset + start),
|
||||
payload_len: Some(end.saturating_sub(start)),
|
||||
decode_status: "compact_nondirect_parity_only".to_string(),
|
||||
payload_family: "real_packed_nondirect_compact_v1".to_string(),
|
||||
trigger_kind: None,
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: None,
|
||||
compact_control: None,
|
||||
text_bands: Vec::new(),
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: Vec::new(),
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![0, 0, 0, 0],
|
||||
grouped_effect_rows: Vec::new(),
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: Vec::new(),
|
||||
executable_import_ready: false,
|
||||
notes,
|
||||
});
|
||||
}
|
||||
|
||||
Some(records)
|
||||
}
|
||||
|
||||
fn decode_live_entry_ids_from_tombstone_bitset(
|
||||
bitset: &[u8],
|
||||
live_id_bound: u32,
|
||||
|
|
@ -12777,6 +12926,12 @@ fn classify_secondary_variant_probe(
|
|||
evidence.push("third/fourth words 0x21000001 and 0xa0000100".to_string());
|
||||
"rt3-105-gms-scenario-family-v1".to_string()
|
||||
}
|
||||
[0x00140000, 0x93e00100, 0x00000004, 0xa0000000, ..] => {
|
||||
evidence.push("leading word 0x00140000".to_string());
|
||||
evidence.push("anchor word 0x93e00100".to_string());
|
||||
evidence.push("third/fourth words 0x00000004 and 0xa0000000".to_string());
|
||||
"rt3-map-secondary-family-v1".to_string()
|
||||
}
|
||||
[0x00010000, 0x49f00100, 0x00000002, 0xa0000000, ..] => {
|
||||
evidence.push("leading word 0x00010000".to_string());
|
||||
evidence.push("anchor word 0x49f00100".to_string());
|
||||
|
|
@ -12912,6 +13067,15 @@ fn classify_container_profile(
|
|||
],
|
||||
true,
|
||||
),
|
||||
("gmp", "rt3-map-header-family", "rt3-map-secondary-family-v1") => (
|
||||
"rt3-map-container-family".to_string(),
|
||||
vec![
|
||||
"extension .gmp".to_string(),
|
||||
"map header family".to_string(),
|
||||
"observed map secondary window family".to_string(),
|
||||
],
|
||||
true,
|
||||
),
|
||||
(_, header_family, secondary_family) => (
|
||||
"unknown".to_string(),
|
||||
vec![
|
||||
|
|
@ -19980,6 +20144,23 @@ fn read_u32_window(bytes: &[u8], offset: usize, count: usize) -> Vec<u32> {
|
|||
words
|
||||
}
|
||||
|
||||
fn read_u16_window(bytes: &[u8], offset: usize, count: usize) -> Vec<u16> {
|
||||
let mut words = Vec::new();
|
||||
let end = bytes.len().min(offset + count * 2);
|
||||
for chunk in bytes[offset..end].chunks_exact(2) {
|
||||
words.push(u16::from_le_bytes([chunk[0], chunk[1]]));
|
||||
}
|
||||
words
|
||||
}
|
||||
|
||||
fn format_u16_word_signature(words: &[u16]) -> String {
|
||||
words
|
||||
.iter()
|
||||
.map(|word| format!("0x{word:04x}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
fn read_u8_at(bytes: &[u8], offset: usize) -> Option<u8> {
|
||||
bytes.get(offset).copied()
|
||||
}
|
||||
|
|
@ -21421,6 +21602,79 @@ mod tests {
|
|||
assert_eq!(summary.records[0].decode_status, "unsupported_framing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_event_runtime_collection_summary_from_u32_tag_chunks() {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_METADATA_TAG as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
|
||||
|
||||
let header_words = [1u32, 4, 0, 0, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
for word in header_words {
|
||||
bytes.extend_from_slice(&word.to_le_bytes());
|
||||
}
|
||||
|
||||
bytes.extend_from_slice(&[0x14, 0x00]);
|
||||
bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
|
||||
bytes.extend_from_slice(&[0x11, 0x22, 0x33, 0x44]);
|
||||
bytes.extend_from_slice(&[0x55, 0x66, 0x77, 0x88]);
|
||||
bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_RECORDS_TAG as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
|
||||
bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_CLOSE_TAG as u32).to_le_bytes());
|
||||
|
||||
let report = inspect_smp_bytes(&bytes);
|
||||
let summary = report
|
||||
.event_runtime_collection_summary
|
||||
.as_ref()
|
||||
.expect("event runtime collection summary should parse from u32 tags");
|
||||
|
||||
assert_eq!(summary.packed_state_version, 0x3e9);
|
||||
assert_eq!(summary.live_id_bound, 5);
|
||||
assert_eq!(summary.live_record_count, 3);
|
||||
assert_eq!(summary.live_entry_ids, vec![1, 3, 5]);
|
||||
assert_eq!(summary.records_tag_offset, 98);
|
||||
assert_eq!(summary.decoded_record_count, 0);
|
||||
assert_eq!(summary.records.len(), 3);
|
||||
assert_eq!(summary.records[0].decode_status, "unsupported_framing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_nondirect_event_runtime_collection_summary_from_u32_tag_chunks() {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_METADATA_TAG as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
|
||||
|
||||
let header_words = [
|
||||
0u32, 6, 10, 20, 30, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1, 23, 0, 0, 0,
|
||||
];
|
||||
for word in header_words {
|
||||
bytes.extend_from_slice(&word.to_le_bytes());
|
||||
}
|
||||
|
||||
bytes.extend_from_slice(&[0u8; 18]);
|
||||
bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_RECORDS_TAG as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
|
||||
bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_CLOSE_TAG as u32).to_le_bytes());
|
||||
|
||||
let report = inspect_smp_bytes(&bytes);
|
||||
let summary = report
|
||||
.event_runtime_collection_summary
|
||||
.as_ref()
|
||||
.expect("non-direct event runtime collection summary should parse");
|
||||
|
||||
assert_eq!(
|
||||
summary.source_kind,
|
||||
"packed-event-runtime-collection-nondirect"
|
||||
);
|
||||
assert_eq!(summary.packed_state_version, 0x3e9);
|
||||
assert_eq!(summary.live_id_bound, 30);
|
||||
assert_eq!(summary.live_record_count, 3);
|
||||
assert_eq!(summary.live_entry_ids, vec![1, 2, 3]);
|
||||
assert_eq!(summary.records_tag_offset, 102);
|
||||
assert_eq!(summary.decoded_record_count, 0);
|
||||
assert_eq!(summary.records.len(), 3);
|
||||
assert_eq!(summary.records[0].decode_status, "unsupported_framing");
|
||||
}
|
||||
|
||||
fn encode_len_prefixed_string(text: &str) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(1 + text.len());
|
||||
bytes.push(text.len() as u8);
|
||||
|
|
@ -27751,6 +28005,29 @@ mod tests {
|
|||
assert!(scenario_profile.is_known_profile);
|
||||
assert_eq!(alt_profile.profile_family, "rt3-105-alt-map-container-v1");
|
||||
assert!(alt_profile.is_known_profile);
|
||||
|
||||
let generic_map_profile = classify_container_profile(
|
||||
Some("gmp"),
|
||||
Some(&SmpHeaderVariantProbe {
|
||||
variant_family: "rt3-map-header-family".to_string(),
|
||||
variant_evidence: vec![],
|
||||
is_known_family: true,
|
||||
}),
|
||||
Some(&SmpSecondaryVariantProbe {
|
||||
aligned_window_offset: 0,
|
||||
words: vec![0x00140000, 0x93e00100, 0x00000004, 0xa0000000],
|
||||
hex_words: vec![],
|
||||
variant_family: "rt3-map-secondary-family-v1".to_string(),
|
||||
variant_evidence: vec![],
|
||||
}),
|
||||
)
|
||||
.expect("generic map profile");
|
||||
|
||||
assert_eq!(
|
||||
generic_map_profile.profile_family,
|
||||
"rt3-map-container-family"
|
||||
);
|
||||
assert!(generic_map_profile.is_known_profile);
|
||||
}
|
||||
|
||||
fn empty_analysis_report() -> SmpSaveCompanyChairmanAnalysisReport {
|
||||
|
|
|
|||
|
|
@ -241,6 +241,15 @@ Working rule:
|
|||
into `0x0062be18` through `0x00432ea0`; so the remaining startup compact-effect question is no
|
||||
longer whether kind `8` lives on a separate editor/build class either, but which loaded
|
||||
kind-`8` rows actually carry the mutation-capable compact payloads
|
||||
- bundle-side inspection now grounds the startup collection itself:
|
||||
`War Effort.gmp` exposes a non-direct `0x4e99/0x4e9a/0x4e9b` runtime-event collection at
|
||||
`0x74740c / 0x7543f4 / 0x7554cf` with `24` live rows, and those rows now segment cleanly as
|
||||
compact `0x526f`-delimited bodies with repeated `0x4eb8` grouped-effect markers plus optional
|
||||
`0x4eb9` terminators
|
||||
- that moves the startup compact-effect blocker again:
|
||||
the remaining question is no longer collection existence, but field mapping inside that
|
||||
compact non-direct row family and whether its observed signatures correspond to loaded
|
||||
`kind 8` rows that can reach the placed-structure mutation opcodes under `0x00431b20`
|
||||
- the `[site+0x27a]` companion lane is grounded now too:
|
||||
it is a live signed scalar accumulator rather than a second owner-identity seam, with zero-init
|
||||
at `0x0042125d` and `0x0040f793`, accumulation at `0x0040dfec` and `0x00426ad8`, direct set on
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue