Recover locomotive policy packed event descriptors
This commit is contained in:
parent
1e36e6c929
commit
c04c19ac82
9 changed files with 679 additions and 18 deletions
|
|
@ -4485,6 +4485,102 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaves_recovered_locomotive_availability_rows_blocked_unmapped_world_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: 31,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![31],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 31,
|
||||
payload_offset: Some(0x7202),
|
||||
payload_len: Some(96),
|
||||
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(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index: 0,
|
||||
row_index: 0,
|
||||
descriptor_id: 250,
|
||||
descriptor_label: Some("Unknown Loco Available".to_string()),
|
||||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("locomotive_availability_scalar".to_string()),
|
||||
opcode: 3,
|
||||
raw_scalar_value: 42,
|
||||
value_byte_0x09: 0,
|
||||
value_dword_0x0d: 0,
|
||||
value_byte_0x11: 0,
|
||||
value_byte_0x12: 0,
|
||||
value_word_0x14: 0,
|
||||
value_word_0x16: 0,
|
||||
row_shape: "scalar_assignment".to_string(),
|
||||
semantic_family: Some("scalar_assignment".to_string()),
|
||||
semantic_preview: Some("Set Unknown Loco Available to 42".to_string()),
|
||||
locomotive_name: None,
|
||||
notes: vec![],
|
||||
}],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec![
|
||||
"decoded from grounded real 0x4e9a row framing".to_string(),
|
||||
"recovered locomotive availability descriptor family remains parity-only"
|
||||
.to_string(),
|
||||
],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let import = project_save_slice_to_runtime_state_import(
|
||||
&save_slice,
|
||||
"packed-events-recovered-locomotive-availability-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].import_outcome.as_deref()),
|
||||
Some("blocked_unmapped_world_descriptor")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_real_company_cash_descriptor_into_executable_runtime_record() {
|
||||
let base_state = RuntimeState {
|
||||
|
|
@ -5919,6 +6015,117 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_recovered_locomotive_policy_descriptors_into_executable_runtime_record() {
|
||||
let base_state = state();
|
||||
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: 29,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![29],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 1,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 29,
|
||||
payload_offset: Some(0x7200),
|
||||
payload_len: Some(160),
|
||||
decode_status: "executable".to_string(),
|
||||
payload_family: "real_packed_v1".to_string(),
|
||||
trigger_kind: Some(7),
|
||||
active: None,
|
||||
marks_collection_dirty: None,
|
||||
one_shot: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: packed_text_bands(),
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![3, 0, 0, 0],
|
||||
grouped_effect_rows: vec![
|
||||
real_world_flag_row(454, "All Steam Locos Avail.", true),
|
||||
real_world_flag_row(455, "All Diesel Locos Avail.", false),
|
||||
real_world_flag_row(456, "All Electric Locos Avail.", true),
|
||||
],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![
|
||||
RuntimeEffect::SetWorldFlag {
|
||||
key: "world.all_steam_locos_available".to_string(),
|
||||
value: true,
|
||||
},
|
||||
RuntimeEffect::SetWorldFlag {
|
||||
key: "world.all_diesel_locos_available".to_string(),
|
||||
value: false,
|
||||
},
|
||||
RuntimeEffect::SetWorldFlag {
|
||||
key: "world.all_electric_locos_available".to_string(),
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
executable_import_ready: true,
|
||||
notes: vec![
|
||||
"recovered locomotive policy descriptor band now imports as keyed world flags"
|
||||
.to_string(),
|
||||
],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let mut import = project_save_slice_overlay_to_runtime_state_import(
|
||||
&base_state,
|
||||
&save_slice,
|
||||
"real-locomotive-policy-overlay",
|
||||
None,
|
||||
)
|
||||
.expect("overlay import should project");
|
||||
|
||||
crate::execute_step_command(
|
||||
&mut import.state,
|
||||
&crate::StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect("trigger service should execute");
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.world_flags
|
||||
.get("world.all_steam_locos_available"),
|
||||
Some(&true)
|
||||
);
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.world_flags
|
||||
.get("world.all_diesel_locos_available"),
|
||||
Some(&false)
|
||||
);
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.world_flags
|
||||
.get("world.all_electric_locos_available"),
|
||||
Some(&true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_real_world_flag_condition_into_executable_runtime_record() {
|
||||
let mut base_state = state();
|
||||
|
|
|
|||
|
|
@ -2785,10 +2785,129 @@ fn real_grouped_effect_descriptor_metadata(
|
|||
.iter()
|
||||
.copied()
|
||||
.find(|metadata| metadata.descriptor_id == descriptor_id)
|
||||
.or_else(|| recovered_cargo_production_descriptor_metadata(descriptor_id))
|
||||
.or_else(|| recovered_locomotive_availability_descriptor_metadata(descriptor_id))
|
||||
.or_else(|| recovered_locomotive_cost_descriptor_metadata(descriptor_id))
|
||||
.or_else(|| recovered_territory_access_cost_descriptor_metadata(descriptor_id))
|
||||
.or_else(|| recovered_locomotive_policy_descriptor_metadata(descriptor_id))
|
||||
.or_else(|| special_condition_world_scalar_descriptor_metadata(descriptor_id))
|
||||
.or_else(|| special_condition_world_toggle_descriptor_metadata(descriptor_id))
|
||||
}
|
||||
|
||||
fn recovered_cargo_production_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
(230..=240)
|
||||
.contains(&descriptor_id)
|
||||
.then_some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Unknown Cargo Production",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "unknown_cargo_production_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn recovered_locomotive_availability_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
(241..=351)
|
||||
.contains(&descriptor_id)
|
||||
.then_some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Unknown Loco Available",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "locomotive_availability_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
.or_else(|| {
|
||||
(457..=474)
|
||||
.contains(&descriptor_id)
|
||||
.then_some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Unknown Loco Available",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "locomotive_availability_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn recovered_locomotive_cost_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
(352..=451)
|
||||
.contains(&descriptor_id)
|
||||
.then_some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Unknown Loco Cost",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "locomotive_cost_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
.or_else(|| {
|
||||
(475..=500)
|
||||
.contains(&descriptor_id)
|
||||
.then_some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Unknown Loco Cost",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "locomotive_cost_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn recovered_territory_access_cost_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
(descriptor_id == 453).then_some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Territory access cost",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "territory_access_cost_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn recovered_locomotive_policy_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
match descriptor_id {
|
||||
454 => Some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "All Steam Locos Avail.",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "world_flag_toggle",
|
||||
runtime_key: Some("world.all_steam_locos_available"),
|
||||
executable_in_runtime: true,
|
||||
}),
|
||||
455 => Some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "All Diesel Locos Avail.",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "world_flag_toggle",
|
||||
runtime_key: Some("world.all_diesel_locos_available"),
|
||||
executable_in_runtime: true,
|
||||
}),
|
||||
456 => Some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "All Electric Locos Avail.",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "world_flag_toggle",
|
||||
runtime_key: Some("world.all_electric_locos_available"),
|
||||
executable_in_runtime: true,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn special_condition_world_scalar_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
|
|
@ -9087,6 +9206,106 @@ mod tests {
|
|||
assert!(metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_recovered_locomotive_availability_descriptor_metadata() {
|
||||
let metadata =
|
||||
real_grouped_effect_descriptor_metadata(250).expect("descriptor metadata should exist");
|
||||
|
||||
assert_eq!(metadata.label, "Unknown Loco Available");
|
||||
assert_eq!(metadata.target_mask_bits, 0x08);
|
||||
assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
|
||||
assert_eq!(metadata.runtime_key, None);
|
||||
assert!(!metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_recovered_locomotive_policy_descriptor_metadata() {
|
||||
let metadata =
|
||||
real_grouped_effect_descriptor_metadata(454).expect("descriptor metadata should exist");
|
||||
|
||||
assert_eq!(metadata.label, "All Steam Locos Avail.");
|
||||
assert_eq!(metadata.parameter_family, "world_flag_toggle");
|
||||
assert_eq!(
|
||||
metadata.runtime_key,
|
||||
Some("world.all_steam_locos_available")
|
||||
);
|
||||
assert!(metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_recovered_locomotive_policy_descriptor_from_checked_in_metadata() {
|
||||
let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
descriptor_id: 454,
|
||||
opcode: 0,
|
||||
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,
|
||||
});
|
||||
let group0_rows = vec![grouped_row];
|
||||
let record_body = build_real_event_record(
|
||||
[b"Locos", b"", b"", b"", b"", b""],
|
||||
Some(RealCompactControlSpec {
|
||||
mode_byte_0x7ef: 6,
|
||||
primary_selector_0x7f0: 0,
|
||||
grouped_mode_0x7f4: 2,
|
||||
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: [1, 0, 0, 0],
|
||||
summary_toggle_0x800: 1,
|
||||
grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
|
||||
}),
|
||||
&[],
|
||||
[&group0_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");
|
||||
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0]
|
||||
.descriptor_label
|
||||
.as_deref(),
|
||||
Some("All Steam Locos Avail.")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].grouped_effect_rows[0]
|
||||
.parameter_family
|
||||
.as_deref(),
|
||||
Some("world_flag_toggle")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.records[0].decoded_actions,
|
||||
vec![RuntimeEffect::SetWorldFlag {
|
||||
key: "world.all_steam_locos_available".to_string(),
|
||||
value: true,
|
||||
}]
|
||||
);
|
||||
assert!(summary.records[0].executable_import_ready);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_checked_in_deactivate_player_descriptor_metadata() {
|
||||
let metadata =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue