Recover real packed event descriptor semantics
This commit is contained in:
parent
f918d0c4f7
commit
eb6c4833af
9 changed files with 719 additions and 120 deletions
|
|
@ -113,6 +113,74 @@ const SHARED_SIGNATURE_WORDS_1_TO_7: [u32; 7] = [
|
|||
0x00002ee0, 0x00040001, 0x00028000, 0x00010000, 0x00000771, 0x00000771, 0x00000771,
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: u32,
|
||||
label: &'static str,
|
||||
target_mask_bits: u8,
|
||||
parameter_family: &'static str,
|
||||
executable_in_runtime: bool,
|
||||
}
|
||||
|
||||
const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetadata; 8] = [
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 1,
|
||||
label: "Player Cash",
|
||||
target_mask_bits: 0x02,
|
||||
parameter_family: "player_finance_scalar",
|
||||
executable_in_runtime: false,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 2,
|
||||
label: "Company Cash",
|
||||
target_mask_bits: 0x01,
|
||||
parameter_family: "company_finance_scalar",
|
||||
executable_in_runtime: true,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 3,
|
||||
label: "Territory - Allow All",
|
||||
target_mask_bits: 0x05,
|
||||
parameter_family: "territory_access_toggle",
|
||||
executable_in_runtime: false,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 8,
|
||||
label: "Economic Status",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "whole_game_state_enum",
|
||||
executable_in_runtime: false,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 9,
|
||||
label: "Confiscate All",
|
||||
target_mask_bits: 0x01,
|
||||
parameter_family: "company_confiscation_variant",
|
||||
executable_in_runtime: false,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 13,
|
||||
label: "Deactivate Company",
|
||||
target_mask_bits: 0x01,
|
||||
parameter_family: "company_lifecycle_toggle",
|
||||
executable_in_runtime: false,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 15,
|
||||
label: "Retire Train",
|
||||
target_mask_bits: 0x0d,
|
||||
parameter_family: "company_or_territory_asset_toggle",
|
||||
executable_in_runtime: false,
|
||||
},
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id: 16,
|
||||
label: "Company Track Pieces Buildable",
|
||||
target_mask_bits: 0x01,
|
||||
parameter_family: "company_build_limit_scalar",
|
||||
executable_in_runtime: false,
|
||||
},
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct KnownSpecialConditionDefinition {
|
||||
slot_index: u8,
|
||||
|
|
@ -1295,6 +1363,12 @@ pub struct SmpLoadedPackedEventGroupedEffectRowSummary {
|
|||
pub group_index: usize,
|
||||
pub row_index: usize,
|
||||
pub descriptor_id: u32,
|
||||
#[serde(default)]
|
||||
pub descriptor_label: Option<String>,
|
||||
#[serde(default)]
|
||||
pub target_mask_bits: Option<u8>,
|
||||
#[serde(default)]
|
||||
pub parameter_family: Option<String>,
|
||||
pub opcode: u8,
|
||||
pub raw_scalar_value: i32,
|
||||
pub value_byte_0x09: u8,
|
||||
|
|
@ -1305,6 +1379,10 @@ pub struct SmpLoadedPackedEventGroupedEffectRowSummary {
|
|||
pub value_word_0x16: u16,
|
||||
pub row_shape: String,
|
||||
#[serde(default)]
|
||||
pub semantic_family: Option<String>,
|
||||
#[serde(default)]
|
||||
pub semantic_preview: Option<String>,
|
||||
#[serde(default)]
|
||||
pub locomotive_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub notes: Vec<String>,
|
||||
|
|
@ -1852,8 +1930,7 @@ fn parse_real_event_runtime_record_summary(
|
|||
let row_bytes =
|
||||
record_body.get(cursor..cursor + PACKED_EVENT_REAL_GROUPED_EFFECT_ROW_LEN)?;
|
||||
cursor += PACKED_EVENT_REAL_GROUPED_EFFECT_ROW_LEN;
|
||||
let locomotive_name =
|
||||
parse_optional_u16_len_prefixed_string(record_body, &mut cursor)?;
|
||||
let locomotive_name = parse_optional_u16_len_prefixed_string(record_body, &mut cursor)?;
|
||||
grouped_effect_rows.push(parse_real_grouped_effect_row_summary(
|
||||
row_bytes,
|
||||
group_index,
|
||||
|
|
@ -1863,6 +1940,14 @@ fn parse_real_event_runtime_record_summary(
|
|||
}
|
||||
}
|
||||
|
||||
let decoded_actions = compact_control
|
||||
.as_ref()
|
||||
.map(|control| decode_real_grouped_effect_actions(&grouped_effect_rows, control))
|
||||
.unwrap_or_default();
|
||||
let executable_import_ready = !decoded_actions.is_empty()
|
||||
&& decoded_actions
|
||||
.iter()
|
||||
.all(runtime_effect_supported_for_save_import);
|
||||
let consumed_len = cursor;
|
||||
Some((
|
||||
SmpLoadedPackedEventRecordSummary {
|
||||
|
|
@ -1884,9 +1969,12 @@ fn parse_real_event_runtime_record_summary(
|
|||
standalone_condition_rows,
|
||||
grouped_effect_row_counts,
|
||||
grouped_effect_rows,
|
||||
decoded_actions: Vec::new(),
|
||||
executable_import_ready: false,
|
||||
notes: vec!["decoded from grounded real 0x4e9a row framing".to_string()],
|
||||
decoded_actions,
|
||||
executable_import_ready,
|
||||
notes: vec![
|
||||
"decoded from grounded real 0x4e9a row framing".to_string(),
|
||||
"grouped descriptor labels and target masks come from the checked-in effect table recovered around 0x006103a0".to_string(),
|
||||
],
|
||||
},
|
||||
consumed_len,
|
||||
))
|
||||
|
|
@ -1931,8 +2019,7 @@ fn parse_optional_real_compact_control_summary(
|
|||
let summary_toggle_0x800 = read_u8_at(bytes, local)?;
|
||||
local += 1;
|
||||
|
||||
let mut grouped_territory_selectors_0x80f =
|
||||
Vec::with_capacity(PACKED_EVENT_REAL_GROUP_COUNT);
|
||||
let mut grouped_territory_selectors_0x80f = Vec::with_capacity(PACKED_EVENT_REAL_GROUP_COUNT);
|
||||
for _ in 0..PACKED_EVENT_REAL_GROUP_COUNT {
|
||||
grouped_territory_selectors_0x80f.push(read_i32_at(bytes, local)?);
|
||||
local += 4;
|
||||
|
|
@ -1978,7 +2065,9 @@ fn parse_real_condition_row_summary(
|
|||
row_index,
|
||||
raw_condition_id,
|
||||
subtype,
|
||||
flag_bytes: row_bytes.get(5..PACKED_EVENT_REAL_CONDITION_ROW_LEN)?.to_vec(),
|
||||
flag_bytes: row_bytes
|
||||
.get(5..PACKED_EVENT_REAL_CONDITION_ROW_LEN)?
|
||||
.to_vec(),
|
||||
candidate_name,
|
||||
notes,
|
||||
})
|
||||
|
|
@ -2008,16 +2097,32 @@ fn parse_real_grouped_effect_row_summary(
|
|||
value_word_0x16,
|
||||
)
|
||||
.to_string();
|
||||
let descriptor_metadata = real_grouped_effect_descriptor_metadata(descriptor_id);
|
||||
let semantic_family = classify_real_grouped_effect_semantic_family(
|
||||
opcode,
|
||||
raw_scalar_value,
|
||||
value_byte_0x11,
|
||||
value_byte_0x12,
|
||||
value_word_0x14,
|
||||
value_word_0x16,
|
||||
)
|
||||
.to_string();
|
||||
|
||||
let mut notes = Vec::new();
|
||||
if locomotive_name.is_some() {
|
||||
notes.push("grouped effect row carries locomotive-name side string".to_string());
|
||||
}
|
||||
if descriptor_metadata.is_none() {
|
||||
notes.push("descriptor id not yet recovered in the checked-in effect table".to_string());
|
||||
}
|
||||
|
||||
Some(SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index,
|
||||
row_index,
|
||||
descriptor_id,
|
||||
descriptor_label: descriptor_metadata.map(|metadata| metadata.label.to_string()),
|
||||
target_mask_bits: descriptor_metadata.map(|metadata| metadata.target_mask_bits),
|
||||
parameter_family: descriptor_metadata.map(|metadata| metadata.parameter_family.to_string()),
|
||||
opcode,
|
||||
raw_scalar_value,
|
||||
value_byte_0x09,
|
||||
|
|
@ -2027,11 +2132,51 @@ fn parse_real_grouped_effect_row_summary(
|
|||
value_word_0x14,
|
||||
value_word_0x16,
|
||||
row_shape,
|
||||
semantic_family: Some(semantic_family.clone()),
|
||||
semantic_preview: Some(build_real_grouped_effect_semantic_preview(
|
||||
descriptor_metadata.map(|metadata| metadata.label),
|
||||
&semantic_family,
|
||||
raw_scalar_value,
|
||||
value_byte_0x11,
|
||||
value_byte_0x12,
|
||||
value_word_0x14,
|
||||
value_word_0x16,
|
||||
)),
|
||||
locomotive_name,
|
||||
notes,
|
||||
})
|
||||
}
|
||||
|
||||
fn real_grouped_effect_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|metadata| metadata.descriptor_id == descriptor_id)
|
||||
}
|
||||
|
||||
fn classify_real_grouped_effect_semantic_family(
|
||||
opcode: u8,
|
||||
raw_scalar_value: i32,
|
||||
value_byte_0x11: u8,
|
||||
value_byte_0x12: u8,
|
||||
value_word_0x14: u16,
|
||||
value_word_0x16: u16,
|
||||
) -> &'static str {
|
||||
if opcode == 8 {
|
||||
return "multivalue_scalar";
|
||||
}
|
||||
if value_byte_0x11 != 0 || value_byte_0x12 != 0 || value_word_0x14 != 0 || value_word_0x16 != 0
|
||||
{
|
||||
return "timed_duration";
|
||||
}
|
||||
if raw_scalar_value == 0 || raw_scalar_value == 1 {
|
||||
return "bool_toggle";
|
||||
}
|
||||
"scalar_assignment"
|
||||
}
|
||||
|
||||
fn classify_real_grouped_effect_row_shape(
|
||||
opcode: u8,
|
||||
raw_scalar_value: i32,
|
||||
|
|
@ -2050,7 +2195,77 @@ fn classify_real_grouped_effect_row_shape(
|
|||
if raw_scalar_value == 0 || raw_scalar_value == 1 {
|
||||
return "bool_toggle";
|
||||
}
|
||||
"raw_other"
|
||||
"scalar_assignment"
|
||||
}
|
||||
|
||||
fn build_real_grouped_effect_semantic_preview(
|
||||
descriptor_label: Option<&str>,
|
||||
semantic_family: &str,
|
||||
raw_scalar_value: i32,
|
||||
value_byte_0x11: u8,
|
||||
value_byte_0x12: u8,
|
||||
value_word_0x14: u16,
|
||||
value_word_0x16: u16,
|
||||
) -> String {
|
||||
let label = descriptor_label.unwrap_or("descriptor");
|
||||
match semantic_family {
|
||||
"bool_toggle" => {
|
||||
let state = if raw_scalar_value == 0 {
|
||||
"FALSE"
|
||||
} else {
|
||||
"TRUE"
|
||||
};
|
||||
format!("Set {label} to {state}")
|
||||
}
|
||||
"timed_duration" => format!(
|
||||
"Set {label} to {raw_scalar_value} for {value_word_0x14} years {value_word_0x16} months"
|
||||
),
|
||||
"multivalue_scalar" => format!(
|
||||
"Set {label} to {raw_scalar_value} with aux [{value_byte_0x11}, {value_byte_0x12}, {value_word_0x14}, {value_word_0x16}]"
|
||||
),
|
||||
_ => format!("Set {label} to {raw_scalar_value}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_real_grouped_effect_actions(
|
||||
grouped_effect_rows: &[SmpLoadedPackedEventGroupedEffectRowSummary],
|
||||
compact_control: &SmpLoadedPackedEventCompactControlSummary,
|
||||
) -> Vec<RuntimeEffect> {
|
||||
grouped_effect_rows
|
||||
.iter()
|
||||
.filter_map(|row| decode_real_grouped_effect_action(row, compact_control))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn decode_real_grouped_effect_action(
|
||||
row: &SmpLoadedPackedEventGroupedEffectRowSummary,
|
||||
compact_control: &SmpLoadedPackedEventCompactControlSummary,
|
||||
) -> Option<RuntimeEffect> {
|
||||
let descriptor_metadata = real_grouped_effect_descriptor_metadata(row.descriptor_id)?;
|
||||
let target_scope_ordinal = compact_control
|
||||
.grouped_target_scope_ordinals_0x7fb
|
||||
.get(row.group_index)
|
||||
.copied()?;
|
||||
let target = match target_scope_ordinal {
|
||||
0 => RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
1 => RuntimeCompanyTarget::SelectedCompany,
|
||||
2 => RuntimeCompanyTarget::HumanCompanies,
|
||||
3 => RuntimeCompanyTarget::AiCompanies,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if descriptor_metadata.executable_in_runtime
|
||||
&& descriptor_metadata.descriptor_id == 2
|
||||
&& row.opcode == 8
|
||||
&& row.row_shape == "multivalue_scalar"
|
||||
{
|
||||
return Some(RuntimeEffect::SetCompanyCash {
|
||||
target,
|
||||
value: i64::from(row.raw_scalar_value),
|
||||
});
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_synthetic_packed_event_action(bytes: &[u8], cursor: &mut usize) -> Option<RuntimeEffect> {
|
||||
|
|
@ -2183,7 +2398,10 @@ fn parse_len_prefixed_string(bytes: &[u8], cursor: &mut usize) -> Option<String>
|
|||
Some(String::from_utf8_lossy(text_bytes).into_owned())
|
||||
}
|
||||
|
||||
fn parse_optional_u16_len_prefixed_string(bytes: &[u8], cursor: &mut usize) -> Option<Option<String>> {
|
||||
fn parse_optional_u16_len_prefixed_string(
|
||||
bytes: &[u8],
|
||||
cursor: &mut usize,
|
||||
) -> Option<Option<String>> {
|
||||
let len = usize::from(read_u16_at(bytes, *cursor)?);
|
||||
*cursor += 2;
|
||||
if len == 0 {
|
||||
|
|
@ -2202,7 +2420,8 @@ fn runtime_effect_supported_for_save_import(effect: &RuntimeEffect) -> bool {
|
|||
| RuntimeEffect::ActivateEventRecord { .. }
|
||||
| RuntimeEffect::DeactivateEventRecord { .. }
|
||||
| RuntimeEffect::RemoveEventRecord { .. } => true,
|
||||
RuntimeEffect::AdjustCompanyCash { target, .. }
|
||||
RuntimeEffect::SetCompanyCash { target, .. }
|
||||
| RuntimeEffect::AdjustCompanyCash { target, .. }
|
||||
| RuntimeEffect::AdjustCompanyDebt { target, .. } => {
|
||||
matches!(target, RuntimeCompanyTarget::AllActive)
|
||||
}
|
||||
|
|
@ -2228,14 +2447,14 @@ fn build_unsupported_event_runtime_record_summaries(
|
|||
payload_offset: None,
|
||||
payload_len: None,
|
||||
decode_status: "unsupported_framing".to_string(),
|
||||
payload_family: "unsupported_framing".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,
|
||||
payload_family: "unsupported_framing".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(),
|
||||
grouped_effect_row_counts: vec![0, 0, 0, 0],
|
||||
grouped_effect_rows: Vec::new(),
|
||||
|
|
@ -7356,7 +7575,10 @@ mod tests {
|
|||
assert_eq!(summary.records[0].text_bands[0].preview, "Alpha");
|
||||
assert_eq!(summary.records[0].standalone_condition_row_count, 0);
|
||||
assert_eq!(summary.records[0].standalone_condition_rows.len(), 0);
|
||||
assert_eq!(summary.records[0].grouped_effect_row_counts, vec![0, 0, 0, 0]);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_row_counts,
|
||||
vec![0, 0, 0, 0]
|
||||
);
|
||||
assert_eq!(summary.records[0].grouped_effect_rows.len(), 0);
|
||||
}
|
||||
|
||||
|
|
@ -7422,7 +7644,10 @@ mod tests {
|
|||
.grouped_target_scope_ordinals_0x7fb,
|
||||
vec![1, 4, 7, 8]
|
||||
);
|
||||
assert_eq!(summary.records[0].standalone_condition_rows[0].raw_condition_id, -1);
|
||||
assert_eq!(
|
||||
summary.records[0].standalone_condition_rows[0].raw_condition_id,
|
||||
-1
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].standalone_condition_rows[0]
|
||||
.candidate_name
|
||||
|
|
@ -7431,16 +7656,173 @@ mod tests {
|
|||
);
|
||||
assert_eq!(summary.records[0].grouped_effect_rows.len(), 1);
|
||||
assert_eq!(summary.records[0].grouped_effect_rows[0].opcode, 8);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0]
|
||||
.descriptor_label
|
||||
.as_deref(),
|
||||
Some("Company Cash")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0].target_mask_bits,
|
||||
Some(0x01)
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0].row_shape,
|
||||
"multivalue_scalar"
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0]
|
||||
.semantic_family
|
||||
.as_deref(),
|
||||
Some("multivalue_scalar")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0]
|
||||
.semantic_preview
|
||||
.as_deref(),
|
||||
Some("Set Company Cash to 7 with aux [2, 3, 24, 36]")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0]
|
||||
.locomotive_name
|
||||
.as_deref(),
|
||||
Some("Mikado")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].decoded_actions,
|
||||
vec![RuntimeEffect::SetCompanyCash {
|
||||
target: RuntimeCompanyTarget::SelectedCompany,
|
||||
value: 7,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifies_real_grouped_row_semantic_families() {
|
||||
let grouped_rows = vec![
|
||||
build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
descriptor_id: 2,
|
||||
opcode: 1,
|
||||
raw_scalar_value: 1,
|
||||
value_byte_0x09: 0,
|
||||
value_dword_0x0d: 0,
|
||||
value_byte_0x11: 0,
|
||||
value_byte_0x12: 0,
|
||||
value_word_0x14: 0,
|
||||
value_word_0x16: 0,
|
||||
locomotive_name: None,
|
||||
}),
|
||||
build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
descriptor_id: 2,
|
||||
opcode: 4,
|
||||
raw_scalar_value: 25,
|
||||
value_byte_0x09: 0,
|
||||
value_dword_0x0d: 0,
|
||||
value_byte_0x11: 0,
|
||||
value_byte_0x12: 0,
|
||||
value_word_0x14: 2,
|
||||
value_word_0x16: 6,
|
||||
locomotive_name: None,
|
||||
}),
|
||||
build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
descriptor_id: 2,
|
||||
opcode: 3,
|
||||
raw_scalar_value: 250,
|
||||
value_byte_0x09: 0,
|
||||
value_dword_0x0d: 0,
|
||||
value_byte_0x11: 0,
|
||||
value_byte_0x12: 0,
|
||||
value_word_0x14: 0,
|
||||
value_word_0x16: 0,
|
||||
locomotive_name: None,
|
||||
}),
|
||||
build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
descriptor_id: 2,
|
||||
opcode: 8,
|
||||
raw_scalar_value: 7,
|
||||
value_byte_0x09: 1,
|
||||
value_dword_0x0d: 12,
|
||||
value_byte_0x11: 2,
|
||||
value_byte_0x12: 3,
|
||||
value_word_0x14: 24,
|
||||
value_word_0x16: 36,
|
||||
locomotive_name: Some("Mikado"),
|
||||
}),
|
||||
];
|
||||
let record_body = build_real_event_record(
|
||||
[b"Semantic", b"", b"", b"", b"", b""],
|
||||
Some(RealCompactControlSpec {
|
||||
mode_byte_0x7ef: 7,
|
||||
primary_selector_0x7f0: 0x63,
|
||||
grouped_mode_0x7f4: 1,
|
||||
one_shot_header_0x7f5: 0,
|
||||
modifier_flag_0x7f9: 0,
|
||||
modifier_flag_0x7fa: 0,
|
||||
grouped_target_scope_ordinals_0x7fb: [1, 1, 1, 1],
|
||||
grouped_scope_checkboxes_0x7ff: [0, 0, 0, 0],
|
||||
summary_toggle_0x800: 0,
|
||||
grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
|
||||
}),
|
||||
&[],
|
||||
[&grouped_rows, &[], &[], &[]],
|
||||
);
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
|
||||
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
|
||||
let header_words = [1u32, 4, 0, 0, 1, 1, 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(&[0x00, 0x00]);
|
||||
bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
|
||||
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
|
||||
bytes.extend_from_slice(&record_body);
|
||||
bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.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");
|
||||
let families = summary.records[0]
|
||||
.grouped_effect_rows
|
||||
.iter()
|
||||
.map(|row| row.semantic_family.as_deref().unwrap_or(""))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
families,
|
||||
vec![
|
||||
"bool_toggle",
|
||||
"timed_duration",
|
||||
"scalar_assignment",
|
||||
"multivalue_scalar",
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0]
|
||||
.semantic_preview
|
||||
.as_deref(),
|
||||
Some("Set Company Cash to TRUE")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[1]
|
||||
.semantic_preview
|
||||
.as_deref(),
|
||||
Some("Set Company Cash to 25 for 2 years 6 months")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[2]
|
||||
.semantic_preview
|
||||
.as_deref(),
|
||||
Some("Set Company Cash to 250")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[3]
|
||||
.semantic_preview
|
||||
.as_deref(),
|
||||
Some("Set Company Cash to 7 with aux [2, 3, 24, 36]")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue