Decode real packed event compact control
This commit is contained in:
parent
45f258cf5d
commit
4ff6d65774
12 changed files with 483 additions and 41 deletions
|
|
@ -95,6 +95,8 @@ const PACKED_EVENT_REAL_CONDITION_MARKER: u16 = 0x526f;
|
|||
const PACKED_EVENT_REAL_GROUPED_EFFECT_MARKER: u16 = 0x4eb8;
|
||||
const PACKED_EVENT_REAL_CONDITION_ROW_LEN: usize = 0x1e;
|
||||
const PACKED_EVENT_REAL_GROUPED_EFFECT_ROW_LEN: usize = 0x28;
|
||||
const PACKED_EVENT_REAL_GROUP_COUNT: usize = 4;
|
||||
const PACKED_EVENT_REAL_COMPACT_CONTROL_LEN: usize = 37;
|
||||
const PACKED_EVENT_TEXT_BAND_LABELS: [&str; 6] = [
|
||||
"primary_text_band",
|
||||
"secondary_text_band_0",
|
||||
|
|
@ -1234,6 +1236,8 @@ pub struct SmpLoadedPackedEventRecordSummary {
|
|||
#[serde(default)]
|
||||
pub one_shot: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub compact_control: Option<SmpLoadedPackedEventCompactControlSummary>,
|
||||
#[serde(default)]
|
||||
pub text_bands: Vec<SmpLoadedPackedEventTextBandSummary>,
|
||||
#[serde(default)]
|
||||
pub standalone_condition_row_count: usize,
|
||||
|
|
@ -1251,6 +1255,20 @@ pub struct SmpLoadedPackedEventRecordSummary {
|
|||
pub notes: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpLoadedPackedEventCompactControlSummary {
|
||||
pub mode_byte_0x7ef: u8,
|
||||
pub primary_selector_0x7f0: u32,
|
||||
pub grouped_mode_0x7f4: u8,
|
||||
pub one_shot_header_0x7f5: u32,
|
||||
pub modifier_flag_0x7f9: u8,
|
||||
pub modifier_flag_0x7fa: u8,
|
||||
pub grouped_target_scope_ordinals_0x7fb: Vec<u8>,
|
||||
pub grouped_scope_checkboxes_0x7ff: Vec<u8>,
|
||||
pub summary_toggle_0x800: u8,
|
||||
pub grouped_territory_selectors_0x80f: Vec<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpLoadedPackedEventTextBandSummary {
|
||||
pub label: String,
|
||||
|
|
@ -1736,6 +1754,7 @@ fn parse_synthetic_event_runtime_record_summary(
|
|||
active: Some(flags & 0x01 != 0),
|
||||
marks_collection_dirty: Some(flags & 0x02 != 0),
|
||||
one_shot: Some(flags & 0x04 != 0),
|
||||
compact_control: None,
|
||||
text_bands,
|
||||
standalone_condition_row_count,
|
||||
standalone_condition_rows: Vec::new(),
|
||||
|
|
@ -1794,6 +1813,8 @@ fn parse_real_event_runtime_record_summary(
|
|||
});
|
||||
}
|
||||
|
||||
let compact_control = parse_optional_real_compact_control_summary(record_body, &mut cursor)?;
|
||||
|
||||
if read_u16_at(record_body, cursor)? != PACKED_EVENT_REAL_CONDITION_MARKER {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -1851,10 +1872,13 @@ fn parse_real_event_runtime_record_summary(
|
|||
payload_len: Some(consumed_len),
|
||||
decode_status: "parity_only".to_string(),
|
||||
payload_family: "real_packed_v1".to_string(),
|
||||
trigger_kind: None,
|
||||
trigger_kind: compact_control.as_ref().map(|control| control.mode_byte_0x7ef),
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: None,
|
||||
one_shot: compact_control
|
||||
.as_ref()
|
||||
.map(|control| control.one_shot_header_0x7f5 != 0),
|
||||
compact_control,
|
||||
text_bands,
|
||||
standalone_condition_row_count,
|
||||
standalone_condition_rows,
|
||||
|
|
@ -1868,6 +1892,74 @@ fn parse_real_event_runtime_record_summary(
|
|||
))
|
||||
}
|
||||
|
||||
fn parse_optional_real_compact_control_summary(
|
||||
record_body: &[u8],
|
||||
cursor: &mut usize,
|
||||
) -> Option<Option<SmpLoadedPackedEventCompactControlSummary>> {
|
||||
if read_u16_at(record_body, *cursor)? == PACKED_EVENT_REAL_CONDITION_MARKER {
|
||||
return Some(None);
|
||||
}
|
||||
|
||||
let end = cursor.checked_add(PACKED_EVENT_REAL_COMPACT_CONTROL_LEN)?;
|
||||
let bytes = record_body.get(*cursor..end)?;
|
||||
let mut local = 0usize;
|
||||
let mode_byte_0x7ef = read_u8_at(bytes, local)?;
|
||||
local += 1;
|
||||
let primary_selector_0x7f0 = read_u32_at(bytes, local)?;
|
||||
local += 4;
|
||||
let grouped_mode_0x7f4 = read_u8_at(bytes, local)?;
|
||||
local += 1;
|
||||
let one_shot_header_0x7f5 = read_u32_at(bytes, local)?;
|
||||
local += 4;
|
||||
let modifier_flag_0x7f9 = read_u8_at(bytes, local)?;
|
||||
local += 1;
|
||||
let modifier_flag_0x7fa = read_u8_at(bytes, local)?;
|
||||
local += 1;
|
||||
|
||||
let mut grouped_target_scope_ordinals_0x7fb = Vec::with_capacity(PACKED_EVENT_REAL_GROUP_COUNT);
|
||||
for _ in 0..PACKED_EVENT_REAL_GROUP_COUNT {
|
||||
grouped_target_scope_ordinals_0x7fb.push(read_u8_at(bytes, local)?);
|
||||
local += 1;
|
||||
}
|
||||
|
||||
let mut grouped_scope_checkboxes_0x7ff = Vec::with_capacity(PACKED_EVENT_REAL_GROUP_COUNT);
|
||||
for _ in 0..PACKED_EVENT_REAL_GROUP_COUNT {
|
||||
grouped_scope_checkboxes_0x7ff.push(read_u8_at(bytes, local)?);
|
||||
local += 1;
|
||||
}
|
||||
|
||||
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);
|
||||
for _ in 0..PACKED_EVENT_REAL_GROUP_COUNT {
|
||||
grouped_territory_selectors_0x80f.push(read_i32_at(bytes, local)?);
|
||||
local += 4;
|
||||
}
|
||||
|
||||
if local != bytes.len() {
|
||||
return None;
|
||||
}
|
||||
if read_u16_at(record_body, end)? != PACKED_EVENT_REAL_CONDITION_MARKER {
|
||||
return None;
|
||||
}
|
||||
|
||||
*cursor = end;
|
||||
Some(Some(SmpLoadedPackedEventCompactControlSummary {
|
||||
mode_byte_0x7ef,
|
||||
primary_selector_0x7f0,
|
||||
grouped_mode_0x7f4,
|
||||
one_shot_header_0x7f5,
|
||||
modifier_flag_0x7f9,
|
||||
modifier_flag_0x7fa,
|
||||
grouped_target_scope_ordinals_0x7fb,
|
||||
grouped_scope_checkboxes_0x7ff,
|
||||
summary_toggle_0x800,
|
||||
grouped_territory_selectors_0x80f,
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_real_condition_row_summary(
|
||||
row_bytes: &[u8],
|
||||
row_index: usize,
|
||||
|
|
@ -2136,13 +2228,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,
|
||||
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(),
|
||||
|
|
@ -5523,6 +5616,11 @@ fn read_u32_at(bytes: &[u8], offset: usize) -> Option<u32> {
|
|||
Some(u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
|
||||
}
|
||||
|
||||
fn read_i32_at(bytes: &[u8], offset: usize) -> Option<i32> {
|
||||
let chunk = bytes.get(offset..offset + 4)?;
|
||||
Some(i32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
|
||||
}
|
||||
|
||||
fn read_i64_at(bytes: &[u8], offset: usize) -> Option<i64> {
|
||||
let chunk = bytes.get(offset..offset + 8)?;
|
||||
Some(i64::from_le_bytes([
|
||||
|
|
@ -7039,8 +7137,40 @@ mod tests {
|
|||
bytes
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct RealCompactControlSpec {
|
||||
mode_byte_0x7ef: u8,
|
||||
primary_selector_0x7f0: u32,
|
||||
grouped_mode_0x7f4: u8,
|
||||
one_shot_header_0x7f5: u32,
|
||||
modifier_flag_0x7f9: u8,
|
||||
modifier_flag_0x7fa: u8,
|
||||
grouped_target_scope_ordinals_0x7fb: [u8; PACKED_EVENT_REAL_GROUP_COUNT],
|
||||
grouped_scope_checkboxes_0x7ff: [u8; PACKED_EVENT_REAL_GROUP_COUNT],
|
||||
summary_toggle_0x800: u8,
|
||||
grouped_territory_selectors_0x80f: [i32; PACKED_EVENT_REAL_GROUP_COUNT],
|
||||
}
|
||||
|
||||
fn build_real_compact_control(spec: RealCompactControlSpec) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(PACKED_EVENT_REAL_COMPACT_CONTROL_LEN);
|
||||
bytes.push(spec.mode_byte_0x7ef);
|
||||
bytes.extend_from_slice(&spec.primary_selector_0x7f0.to_le_bytes());
|
||||
bytes.push(spec.grouped_mode_0x7f4);
|
||||
bytes.extend_from_slice(&spec.one_shot_header_0x7f5.to_le_bytes());
|
||||
bytes.push(spec.modifier_flag_0x7f9);
|
||||
bytes.push(spec.modifier_flag_0x7fa);
|
||||
bytes.extend_from_slice(&spec.grouped_target_scope_ordinals_0x7fb);
|
||||
bytes.extend_from_slice(&spec.grouped_scope_checkboxes_0x7ff);
|
||||
bytes.push(spec.summary_toggle_0x800);
|
||||
for selector in spec.grouped_territory_selectors_0x80f {
|
||||
bytes.extend_from_slice(&selector.to_le_bytes());
|
||||
}
|
||||
bytes
|
||||
}
|
||||
|
||||
fn build_real_event_record(
|
||||
text_bands: [&[u8]; 6],
|
||||
compact_control: Option<RealCompactControlSpec>,
|
||||
condition_rows: &[Vec<u8>],
|
||||
grouped_rows: [&[Vec<u8>]; 4],
|
||||
) -> Vec<u8> {
|
||||
|
|
@ -7049,6 +7179,9 @@ mod tests {
|
|||
bytes.extend_from_slice(&(band.len() as u16).to_le_bytes());
|
||||
bytes.extend_from_slice(band);
|
||||
}
|
||||
if let Some(spec) = compact_control {
|
||||
bytes.extend_from_slice(&build_real_compact_control(spec));
|
||||
}
|
||||
bytes.extend_from_slice(&PACKED_EVENT_REAL_CONDITION_MARKER.to_le_bytes());
|
||||
bytes.extend_from_slice(&(condition_rows.len() as u16).to_le_bytes());
|
||||
for row in condition_rows {
|
||||
|
|
@ -7171,6 +7304,18 @@ mod tests {
|
|||
fn parses_real_style_event_runtime_record_with_zero_rows() {
|
||||
let record_body = build_real_event_record(
|
||||
[b"Alpha", b"", b"", b"", b"", b""],
|
||||
Some(RealCompactControlSpec {
|
||||
mode_byte_0x7ef: 7,
|
||||
primary_selector_0x7f0: 0x63,
|
||||
grouped_mode_0x7f4: 2,
|
||||
one_shot_header_0x7f5: 1,
|
||||
modifier_flag_0x7f9: 1,
|
||||
modifier_flag_0x7fa: 0,
|
||||
grouped_target_scope_ordinals_0x7fb: [0, 1, 2, 3],
|
||||
grouped_scope_checkboxes_0x7ff: [1, 0, 1, 0],
|
||||
summary_toggle_0x800: 1,
|
||||
grouped_territory_selectors_0x80f: [-1, 10, -1, 22],
|
||||
}),
|
||||
&[],
|
||||
[&[], &[], &[], &[]],
|
||||
);
|
||||
|
|
@ -7198,6 +7343,16 @@ mod tests {
|
|||
assert_eq!(summary.imported_runtime_record_count, 0);
|
||||
assert_eq!(summary.records[0].decode_status, "parity_only");
|
||||
assert_eq!(summary.records[0].payload_family, "real_packed_v1");
|
||||
assert_eq!(summary.records[0].trigger_kind, Some(7));
|
||||
assert_eq!(summary.records[0].one_shot, Some(true));
|
||||
assert_eq!(
|
||||
summary.records[0]
|
||||
.compact_control
|
||||
.as_ref()
|
||||
.expect("real compact control should parse")
|
||||
.primary_selector_0x7f0,
|
||||
0x63
|
||||
);
|
||||
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);
|
||||
|
|
@ -7223,6 +7378,18 @@ mod tests {
|
|||
let group0_rows = vec![grouped_row];
|
||||
let record_body = build_real_event_record(
|
||||
[b"Gamma", b"", b"", b"", b"", b""],
|
||||
Some(RealCompactControlSpec {
|
||||
mode_byte_0x7ef: 6,
|
||||
primary_selector_0x7f0: 0x2a,
|
||||
grouped_mode_0x7f4: 1,
|
||||
one_shot_header_0x7f5: 0,
|
||||
modifier_flag_0x7f9: 2,
|
||||
modifier_flag_0x7fa: 3,
|
||||
grouped_target_scope_ordinals_0x7fb: [1, 4, 7, 8],
|
||||
grouped_scope_checkboxes_0x7ff: [0, 1, 0, 1],
|
||||
summary_toggle_0x800: 0,
|
||||
grouped_territory_selectors_0x80f: [11, -1, 33, -1],
|
||||
}),
|
||||
&[condition_row],
|
||||
[&group0_rows, &[], &[], &[]],
|
||||
);
|
||||
|
|
@ -7247,6 +7414,14 @@ mod tests {
|
|||
.expect("event runtime collection summary should parse");
|
||||
|
||||
assert_eq!(summary.records[0].standalone_condition_rows.len(), 1);
|
||||
assert_eq!(
|
||||
summary.records[0]
|
||||
.compact_control
|
||||
.as_ref()
|
||||
.expect("real compact control should parse")
|
||||
.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]
|
||||
|
|
@ -7272,6 +7447,18 @@ mod tests {
|
|||
fn rejects_truncated_real_style_event_runtime_record() {
|
||||
let mut record_body = build_real_event_record(
|
||||
[b"Oops", b"", b"", b"", b"", b""],
|
||||
Some(RealCompactControlSpec {
|
||||
mode_byte_0x7ef: 5,
|
||||
primary_selector_0x7f0: 0,
|
||||
grouped_mode_0x7f4: 0,
|
||||
one_shot_header_0x7f5: 0,
|
||||
modifier_flag_0x7f9: 0,
|
||||
modifier_flag_0x7fa: 0,
|
||||
grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
|
||||
grouped_scope_checkboxes_0x7ff: [0, 0, 0, 0],
|
||||
summary_toggle_0x800: 0,
|
||||
grouped_territory_selectors_0x80f: [0, 0, 0, 0],
|
||||
}),
|
||||
&[],
|
||||
[&[], &[], &[], &[]],
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue