Decode real packed event compact control
This commit is contained in:
parent
45f258cf5d
commit
4ff6d65774
12 changed files with 483 additions and 41 deletions
|
|
@ -11,9 +11,9 @@ The long-term direction is still a DLL we can inject into the original executabl
|
|||
individual functions as we build them out. The active implementation milestone is now a headless
|
||||
runtime rehost layer that can execute deterministic world work, compare normalized state, and grow
|
||||
subsystem breadth without depending on the shell or presentation path. The current packed-event
|
||||
frontier is real `0x4e9a` structural decode on top of the existing save-slice, snapshot, and
|
||||
overlay-import workflows. The PE32 hook remains useful as capture and integration tooling, but it
|
||||
is no longer the main execution milestone.
|
||||
frontier is real `0x4e9a` compact-control decode and descriptor-frontier tightening on top of the
|
||||
existing save-slice, snapshot, and overlay-import workflows. The PE32 hook remains useful as
|
||||
capture and integration tooling, but it is no longer the main execution milestone.
|
||||
|
||||
## Project Docs
|
||||
|
||||
|
|
|
|||
|
|
@ -379,6 +379,7 @@ mod tests {
|
|||
active: Some(true),
|
||||
marks_collection_dirty: Some(false),
|
||||
one_shot: Some(false),
|
||||
compact_control: None,
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ pub struct ExpectedRuntimeSummary {
|
|||
#[serde(default)]
|
||||
pub packed_event_blocked_missing_company_context_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub packed_event_blocked_missing_compact_control_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub packed_event_blocked_unmapped_real_descriptor_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub packed_event_blocked_structural_only_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub event_runtime_record_count: Option<usize>,
|
||||
|
|
@ -373,6 +377,22 @@ impl ExpectedRuntimeSummary {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.packed_event_blocked_missing_compact_control_count {
|
||||
if actual.packed_event_blocked_missing_compact_control_count != count {
|
||||
mismatches.push(format!(
|
||||
"packed_event_blocked_missing_compact_control_count mismatch: expected {count}, got {}",
|
||||
actual.packed_event_blocked_missing_compact_control_count
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.packed_event_blocked_unmapped_real_descriptor_count {
|
||||
if actual.packed_event_blocked_unmapped_real_descriptor_count != count {
|
||||
mismatches.push(format!(
|
||||
"packed_event_blocked_unmapped_real_descriptor_count mismatch: expected {count}, got {}",
|
||||
actual.packed_event_blocked_unmapped_real_descriptor_count
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.packed_event_blocked_structural_only_count {
|
||||
if actual.packed_event_blocked_structural_only_count != count {
|
||||
mismatches.push(format!(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::persistence::{load_runtime_snapshot_document, validate_runtime_snapshot_document};
|
||||
use crate::{
|
||||
CalendarPoint, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
||||
RuntimePackedEventCompactControlSummary,
|
||||
RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary,
|
||||
RuntimePackedEventCollectionSummary, RuntimePackedEventRecordSummary,
|
||||
RuntimePackedEventTextBandSummary, RuntimeSaveProfileState, RuntimeServiceState, RuntimeState,
|
||||
|
|
@ -564,6 +565,10 @@ fn runtime_packed_event_record_summary_from_smp(
|
|||
active: record.active,
|
||||
marks_collection_dirty: record.marks_collection_dirty,
|
||||
one_shot: record.one_shot,
|
||||
compact_control: record
|
||||
.compact_control
|
||||
.as_ref()
|
||||
.map(runtime_packed_event_compact_control_summary_from_smp),
|
||||
text_bands: record
|
||||
.text_bands
|
||||
.iter()
|
||||
|
|
@ -592,6 +597,23 @@ fn runtime_packed_event_record_summary_from_smp(
|
|||
}
|
||||
}
|
||||
|
||||
fn runtime_packed_event_compact_control_summary_from_smp(
|
||||
control: &crate::SmpLoadedPackedEventCompactControlSummary,
|
||||
) -> RuntimePackedEventCompactControlSummary {
|
||||
RuntimePackedEventCompactControlSummary {
|
||||
mode_byte_0x7ef: control.mode_byte_0x7ef,
|
||||
primary_selector_0x7f0: control.primary_selector_0x7f0,
|
||||
grouped_mode_0x7f4: control.grouped_mode_0x7f4,
|
||||
one_shot_header_0x7f5: control.one_shot_header_0x7f5,
|
||||
modifier_flag_0x7f9: control.modifier_flag_0x7f9,
|
||||
modifier_flag_0x7fa: control.modifier_flag_0x7fa,
|
||||
grouped_target_scope_ordinals_0x7fb: control.grouped_target_scope_ordinals_0x7fb.clone(),
|
||||
grouped_scope_checkboxes_0x7ff: control.grouped_scope_checkboxes_0x7ff.clone(),
|
||||
summary_toggle_0x800: control.summary_toggle_0x800,
|
||||
grouped_territory_selectors_0x80f: control.grouped_territory_selectors_0x80f.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn runtime_packed_event_text_band_summary_from_smp(
|
||||
band: &SmpLoadedPackedEventTextBandSummary,
|
||||
) -> RuntimePackedEventTextBandSummary {
|
||||
|
|
@ -803,7 +825,10 @@ fn determine_packed_event_import_outcome(
|
|||
return "blocked_unsupported_decode".to_string();
|
||||
}
|
||||
if record.payload_family == "real_packed_v1" {
|
||||
return "blocked_structural_only".to_string();
|
||||
if record.compact_control.is_none() {
|
||||
return "blocked_missing_compact_control".to_string();
|
||||
}
|
||||
return "blocked_unmapped_real_descriptor".to_string();
|
||||
}
|
||||
if packed_record_requires_missing_company_context(record, known_company_ids) {
|
||||
return "blocked_missing_company_context".to_string();
|
||||
|
|
@ -1208,6 +1233,21 @@ mod tests {
|
|||
}]
|
||||
}
|
||||
|
||||
fn real_compact_control() -> crate::SmpLoadedPackedEventCompactControlSummary {
|
||||
crate::SmpLoadedPackedEventCompactControlSummary {
|
||||
mode_byte_0x7ef: 6,
|
||||
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: vec![0, 1, 2, 3],
|
||||
grouped_scope_checkboxes_0x7ff: vec![1, 0, 1, 0],
|
||||
summary_toggle_0x800: 1,
|
||||
grouped_territory_selectors_0x80f: vec![-1, 10, -1, 22],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loads_dump_document() {
|
||||
let text = serde_json::to_string(&RuntimeStateDumpDocument {
|
||||
|
|
@ -1429,6 +1469,7 @@ mod tests {
|
|||
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(),
|
||||
|
|
@ -1449,6 +1490,7 @@ mod tests {
|
|||
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(),
|
||||
|
|
@ -1469,6 +1511,7 @@ mod tests {
|
|||
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(),
|
||||
|
|
@ -1679,6 +1722,7 @@ mod tests {
|
|||
active: Some(true),
|
||||
marks_collection_dirty: Some(true),
|
||||
one_shot: Some(false),
|
||||
compact_control: None,
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 1,
|
||||
standalone_condition_rows: vec![],
|
||||
|
|
@ -1788,6 +1832,7 @@ mod tests {
|
|||
active: Some(true),
|
||||
marks_collection_dirty: Some(false),
|
||||
one_shot: Some(false),
|
||||
compact_control: None,
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
|
|
@ -1839,7 +1884,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn leaves_real_structural_records_blocked_structural_only() {
|
||||
fn leaves_real_records_without_compact_control_blocked_missing_compact_control() {
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
|
|
@ -1876,6 +1921,7 @@ mod tests {
|
|||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: None,
|
||||
compact_control: None,
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 1,
|
||||
standalone_condition_rows: real_condition_rows(),
|
||||
|
|
@ -1903,7 +1949,7 @@ mod tests {
|
|||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
|
||||
Some("blocked_structural_only")
|
||||
Some("blocked_missing_compact_control")
|
||||
);
|
||||
assert_eq!(
|
||||
import
|
||||
|
|
@ -1923,6 +1969,85 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaves_real_records_with_compact_control_blocked_unmapped_real_descriptor() {
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
source_kind: "packed-event-runtime-collection".to_string(),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
metadata_tag_offset: 0x7100,
|
||||
records_tag_offset: 0x7200,
|
||||
close_tag_offset: 0x7600,
|
||||
packed_state_version: 0x3e9,
|
||||
packed_state_version_hex: "0x000003e9".to_string(),
|
||||
live_id_bound: 7,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![7],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 7,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(133),
|
||||
decode_status: "parity_only".to_string(),
|
||||
payload_family: "real_packed_v1".to_string(),
|
||||
trigger_kind: Some(6),
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: Some(true),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 1,
|
||||
standalone_condition_rows: real_condition_rows(),
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: real_grouped_rows(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec!["decoded from grounded real 0x4e9a row framing".to_string()],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let import = project_save_slice_to_runtime_state_import(
|
||||
&save_slice,
|
||||
"packed-events-real-descriptor-frontier",
|
||||
None,
|
||||
)
|
||||
.expect("save slice should project");
|
||||
|
||||
assert!(import.state.event_runtime_records.is_empty());
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].compact_control.as_ref())
|
||||
.map(|control| control.mode_byte_0x7ef),
|
||||
Some(6)
|
||||
);
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
|
||||
Some("blocked_unmapped_real_descriptor")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_save_slice_events_onto_base_company_context() {
|
||||
let base_state = RuntimeState {
|
||||
|
|
@ -1997,6 +2122,7 @@ mod tests {
|
|||
active: Some(true),
|
||||
marks_collection_dirty: Some(false),
|
||||
one_shot: Some(false),
|
||||
compact_control: None,
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
|
|
@ -2150,6 +2276,7 @@ mod tests {
|
|||
active: Some(true),
|
||||
marks_collection_dirty: Some(false),
|
||||
one_shot: Some(false),
|
||||
compact_control: None,
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ pub use pk4::{
|
|||
pub use runtime::{
|
||||
RuntimeCompany, RuntimeCompanyTarget, RuntimeEffect, RuntimeEventRecord,
|
||||
RuntimeEventRecordTemplate, RuntimePackedEventCollectionSummary,
|
||||
RuntimePackedEventCompactControlSummary,
|
||||
RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary,
|
||||
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimeSaveProfileState,
|
||||
RuntimeServiceState, RuntimeState, RuntimeWorldRestoreState,
|
||||
|
|
@ -47,6 +48,7 @@ pub use smp::{
|
|||
SmpClassicRehydrateProfileProbe, SmpContainerProfile, SmpEarlyContentProbe,
|
||||
SmpHeaderVariantProbe, SmpInspectionReport, SmpKnownTagHit,
|
||||
SmpLoadedCandidateAvailabilityTable, SmpLoadedEventRuntimeCollectionSummary,
|
||||
SmpLoadedPackedEventCompactControlSummary,
|
||||
SmpLoadedPackedEventConditionRowSummary, SmpLoadedPackedEventGroupedEffectRowSummary,
|
||||
SmpLoadedPackedEventRecordSummary, SmpLoadedPackedEventTextBandSummary, SmpLoadedProfile,
|
||||
SmpLoadedSaveSlice, SmpLoadedSpecialConditionsTable, SmpLocomotivePolicyFieldObservation,
|
||||
|
|
|
|||
|
|
@ -125,6 +125,8 @@ pub struct RuntimePackedEventRecordSummary {
|
|||
#[serde(default)]
|
||||
pub one_shot: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub compact_control: Option<RuntimePackedEventCompactControlSummary>,
|
||||
#[serde(default)]
|
||||
pub text_bands: Vec<RuntimePackedEventTextBandSummary>,
|
||||
#[serde(default)]
|
||||
pub standalone_condition_row_count: usize,
|
||||
|
|
@ -144,6 +146,23 @@ pub struct RuntimePackedEventRecordSummary {
|
|||
pub notes: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RuntimePackedEventCompactControlSummary {
|
||||
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,
|
||||
#[serde(default)]
|
||||
pub grouped_target_scope_ordinals_0x7fb: Vec<u8>,
|
||||
#[serde(default)]
|
||||
pub grouped_scope_checkboxes_0x7ff: Vec<u8>,
|
||||
pub summary_toggle_0x800: u8,
|
||||
#[serde(default)]
|
||||
pub grouped_territory_selectors_0x80f: Vec<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RuntimePackedEventTextBandSummary {
|
||||
pub label: String,
|
||||
|
|
@ -467,6 +486,23 @@ impl RuntimeState {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(control) = &record.compact_control {
|
||||
if control.grouped_target_scope_ordinals_0x7fb.len() != 4 {
|
||||
return Err(format!(
|
||||
"packed_event_collection.records[{record_index}].compact_control.grouped_target_scope_ordinals_0x7fb must contain exactly 4 entries"
|
||||
));
|
||||
}
|
||||
if control.grouped_scope_checkboxes_0x7ff.len() != 4 {
|
||||
return Err(format!(
|
||||
"packed_event_collection.records[{record_index}].compact_control.grouped_scope_checkboxes_0x7ff must contain exactly 4 entries"
|
||||
));
|
||||
}
|
||||
if control.grouped_territory_selectors_0x80f.len() != 4 {
|
||||
return Err(format!(
|
||||
"packed_event_collection.records[{record_index}].compact_control.grouped_territory_selectors_0x80f must contain exactly 4 entries"
|
||||
));
|
||||
}
|
||||
}
|
||||
for row in &record.standalone_condition_rows {
|
||||
if row
|
||||
.candidate_name
|
||||
|
|
@ -863,6 +899,7 @@ mod tests {
|
|||
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(),
|
||||
|
|
@ -884,6 +921,7 @@ mod tests {
|
|||
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(),
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
}),
|
||||
&[],
|
||||
[&[], &[], &[], &[]],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ pub struct RuntimeSummary {
|
|||
pub packed_event_parity_only_record_count: usize,
|
||||
pub packed_event_unsupported_record_count: usize,
|
||||
pub packed_event_blocked_missing_company_context_count: usize,
|
||||
pub packed_event_blocked_missing_compact_control_count: usize,
|
||||
pub packed_event_blocked_unmapped_real_descriptor_count: usize,
|
||||
pub packed_event_blocked_structural_only_count: usize,
|
||||
pub event_runtime_record_count: usize,
|
||||
pub candidate_availability_count: usize,
|
||||
|
|
@ -169,6 +171,34 @@ impl RuntimeSummary {
|
|||
.count()
|
||||
})
|
||||
.unwrap_or(0),
|
||||
packed_event_blocked_missing_compact_control_count: state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.map(|summary| {
|
||||
summary
|
||||
.records
|
||||
.iter()
|
||||
.filter(|record| {
|
||||
record.import_outcome.as_deref()
|
||||
== Some("blocked_missing_compact_control")
|
||||
})
|
||||
.count()
|
||||
})
|
||||
.unwrap_or(0),
|
||||
packed_event_blocked_unmapped_real_descriptor_count: state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.map(|summary| {
|
||||
summary
|
||||
.records
|
||||
.iter()
|
||||
.filter(|record| {
|
||||
record.import_outcome.as_deref()
|
||||
== Some("blocked_unmapped_real_descriptor")
|
||||
})
|
||||
.count()
|
||||
})
|
||||
.unwrap_or(0),
|
||||
packed_event_blocked_structural_only_count: state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
|
|
@ -271,6 +301,7 @@ mod tests {
|
|||
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(),
|
||||
|
|
@ -278,7 +309,7 @@ mod tests {
|
|||
grouped_effect_rows: Vec::new(),
|
||||
decoded_actions: Vec::new(),
|
||||
executable_import_ready: false,
|
||||
import_outcome: Some("blocked_structural_only".to_string()),
|
||||
import_outcome: Some("blocked_missing_compact_control".to_string()),
|
||||
notes: Vec::new(),
|
||||
},
|
||||
RuntimePackedEventRecordSummary {
|
||||
|
|
@ -292,6 +323,7 @@ mod tests {
|
|||
active: Some(true),
|
||||
marks_collection_dirty: Some(false),
|
||||
one_shot: Some(false),
|
||||
compact_control: None,
|
||||
text_bands: Vec::new(),
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: Vec::new(),
|
||||
|
|
@ -311,7 +343,9 @@ mod tests {
|
|||
};
|
||||
|
||||
let summary = RuntimeSummary::from_state(&state);
|
||||
assert_eq!(summary.packed_event_blocked_structural_only_count, 1);
|
||||
assert_eq!(summary.packed_event_blocked_missing_compact_control_count, 1);
|
||||
assert_eq!(summary.packed_event_blocked_unmapped_real_descriptor_count, 0);
|
||||
assert_eq!(summary.packed_event_blocked_structural_only_count, 0);
|
||||
assert_eq!(summary.packed_event_blocked_missing_company_context_count, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,12 +75,14 @@ The highest-value next passes are now:
|
|||
|
||||
- preserve the atlas and function map as the source of subsystem boundaries while continuing to
|
||||
avoid shell-first implementation bets
|
||||
- move the packed-event parser from the synthetic harness onto real `0x4e9a` structural decode so
|
||||
real rows stop collapsing to generic unsupported framing
|
||||
- tighten the packed-event frontier from generic real-row structure into decoded real compact
|
||||
control, so parity rows carry mode, selector, one-shot, and grouped target-scope state directly
|
||||
- use overlay imports as the context bridge when selectively executable packed rows still need live
|
||||
company state that save slices do not persist
|
||||
- widen real packed-event executable coverage only after the structural decode frontier and row
|
||||
summaries are stable
|
||||
- widen real packed-event executable coverage only after the compact-control and descriptor frontier
|
||||
is stable, not just after row framing is parsed
|
||||
- keep in mind that the current local `.gms` corpus still exports with no packed event collection,
|
||||
so real descriptor mapping needs to stay plumbing-first until better captures exist
|
||||
- use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution
|
||||
environment
|
||||
- keep `docs/runtime-rehost-plan.md` current as the runtime baseline and next implementation slice
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ Implemented today:
|
|||
normalized state-fragment assertions, and imported packed-event execution
|
||||
|
||||
That means the next implementation work is breadth, not bootstrap. The recommended next slice is
|
||||
real `0x4e9a` packed-event structural decode, not another persistence scaffold pass.
|
||||
real `0x4e9a` compact-control decode and descriptor-frontier tightening, not another persistence
|
||||
scaffold pass.
|
||||
|
||||
## Why This Boundary
|
||||
|
||||
|
|
@ -365,33 +366,41 @@ Checked-in fixture families already include:
|
|||
|
||||
## Next Slice
|
||||
|
||||
The recommended next implementation slice is real `0x4e9a` packed-event structural decode on top
|
||||
of the save-slice, snapshot, and overlay workflows that already exist today.
|
||||
The recommended next implementation slice is real `0x4e9a` compact-control decode on top of the
|
||||
existing real-row structural parse.
|
||||
|
||||
Target behavior:
|
||||
|
||||
- parse real packed-event record bodies structurally instead of dropping them straight to
|
||||
`unsupported_framing`
|
||||
- preserve the first real decode pass as parity-only, exposing text-band and row-family structure
|
||||
without guessing executable semantics
|
||||
- decode the compact control block that sits above the real standalone-condition and grouped-effect
|
||||
row families, carrying through raw grounded lanes such as mode byte `0x7ef`, primary selector
|
||||
`0x7f0`, grouped mode `0x7f4`, one-shot header `0x7f5`, modifier bytes `0x7f9/0x7fa`, grouped
|
||||
target-scope ordinals `0x7fb`, grouped scope checkboxes `0x7ff`, summary toggle `0x800`, and
|
||||
grouped territory selectors `0x80f`
|
||||
- keep real rows parity-only in runtime import, but replace the coarse `blocked_structural_only`
|
||||
frontier with narrower outcomes such as `blocked_missing_compact_control` and
|
||||
`blocked_unmapped_real_descriptor`
|
||||
- keep the existing synthetic harness and overlay-backed executable import path working unchanged
|
||||
- reserve selective executable import from real rows for a later pass once row semantics are tighter
|
||||
- reserve the first real descriptor-to-effect mapping for a later slice once captured evidence is
|
||||
tighter
|
||||
|
||||
Public-model additions for that slice:
|
||||
|
||||
- payload-family labeling that distinguishes synthetic harness records from real packed rows and
|
||||
unsupported framing
|
||||
- structural row summaries for real standalone condition rows and grouped effect rows
|
||||
- runtime-side import outcome labels that distinguish `blocked_structural_only` from
|
||||
`blocked_missing_company_context` and `blocked_unsupported_decode`
|
||||
- compact-control summaries on packed-event records in both the save-side and runtime-side models
|
||||
- runtime summary counts for compact-control-missing and unmapped-real-descriptor blockers
|
||||
- trigger-kind and one-shot derivation only where the compact-control mapping is already grounded
|
||||
|
||||
Fixture work for that slice:
|
||||
|
||||
- one parity-heavy tracked sample that now exposes a real structurally decoded row family
|
||||
- update the parity-heavy tracked sample so the real row includes compact-control state
|
||||
- regression fixtures that keep synthetic executable import and overlay-backed company-context
|
||||
upgrade behavior green
|
||||
- state-fragment assertions that lock the new structural row summaries and
|
||||
`blocked_structural_only` frontier
|
||||
- state-fragment assertions that lock the new compact-control summary and narrower import blockers
|
||||
|
||||
Current local constraint:
|
||||
|
||||
- the local checked-in and on-disk `.gms` corpus currently exports with
|
||||
`packed_event_collection_present = false`, so this slice must not depend on a newly captured real
|
||||
packed-event-bearing save for acceptance
|
||||
|
||||
Do not mix this slice with:
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@
|
|||
"packed_event_imported_runtime_record_count": 0,
|
||||
"packed_event_parity_only_record_count": 1,
|
||||
"packed_event_unsupported_record_count": 1,
|
||||
"packed_event_blocked_structural_only_count": 1,
|
||||
"packed_event_blocked_missing_compact_control_count": 0,
|
||||
"packed_event_blocked_unmapped_real_descriptor_count": 1,
|
||||
"packed_event_blocked_structural_only_count": 0,
|
||||
"event_runtime_record_count": 0,
|
||||
"total_company_cash": 0
|
||||
},
|
||||
|
|
@ -47,7 +49,13 @@
|
|||
{
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"import_outcome": "blocked_structural_only",
|
||||
"trigger_kind": 6,
|
||||
"one_shot": true,
|
||||
"import_outcome": "blocked_unmapped_real_descriptor",
|
||||
"compact_control": {
|
||||
"primary_selector_0x7f0": 99,
|
||||
"grouped_target_scope_ordinals_0x7fb": [0, 1, 2, 3]
|
||||
},
|
||||
"standalone_condition_rows": [
|
||||
{
|
||||
"candidate_name": "AutoPlant"
|
||||
|
|
|
|||
|
|
@ -54,9 +54,23 @@
|
|||
"record_index": 1,
|
||||
"live_entry_id": 5,
|
||||
"payload_offset": 29290,
|
||||
"payload_len": 72,
|
||||
"payload_len": 109,
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 6,
|
||||
"one_shot": true,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 6,
|
||||
"primary_selector_0x7f0": 99,
|
||||
"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]
|
||||
},
|
||||
"text_bands": [
|
||||
{
|
||||
"label": "primary_text_band",
|
||||
|
|
@ -137,7 +151,7 @@
|
|||
"decoded_actions": [],
|
||||
"executable_import_ready": false,
|
||||
"notes": [
|
||||
"decoded from grounded real 0x4e9a row framing"
|
||||
"decoded from grounded real 0x4e9a row framing with compact control"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue