From c04c19ac821a58d706429c4720794ffd63d83c26 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Thu, 16 Apr 2026 09:55:58 -0700 Subject: [PATCH] Recover locomotive policy packed event descriptors --- README.md | 14 +- crates/rrt-runtime/src/import.rs | 207 +++++++++++++++++ crates/rrt-runtime/src/smp.rs | 219 ++++++++++++++++++ docs/README.md | 9 +- docs/runtime-rehost-plan.md | 7 +- ...acked-event-parity-save-slice-fixture.json | 10 +- .../packed-event-parity-save-slice.json | 16 +- ...-locomotive-policy-save-slice-fixture.json | 56 +++++ ...nt-world-locomotive-policy-save-slice.json | 159 +++++++++++++ 9 files changed, 679 insertions(+), 18 deletions(-) create mode 100644 fixtures/runtime/packed-event-world-locomotive-policy-save-slice-fixture.json create mode 100644 fixtures/runtime/packed-event-world-locomotive-policy-save-slice.json diff --git a/README.md b/README.md index a583adf..28398bd 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,15 @@ is broader now too: checked-in world-flag condition ids can lower into `world_fl for boolean equality/inequality forms, so real packed records can gate whole-game effects on existing `world_flags` without fixture-authored placeholder ids. The tracked parity save-slice no longer depends on a raw `unsupported_framing` placeholder either: its remaining residue is now one -structured `real_packed_v1` record that lands in the explicit `blocked_unmapped_real_descriptor` -bucket. Explicit unmapped world-condition and world-descriptor frontier buckets still remain where -current checked-in metadata stops. Shell purchase-flow and selected-profile parity remain out of -scope. Mixed supported/unsupported real rows still stay parity-only. The PE32 hook remains useful -as capture and integration tooling, but it is no longer the main execution milestone. +recovered locomotives-page `real_packed_v1` record that lands in the explicit +`blocked_unmapped_world_descriptor` bucket. The next recovered descriptor band is now partially +executable too: descriptors `454..456` (`All Steam/Diesel/Electric Locos Avail.`) now lower +through checked-in metadata into keyed `world_flags`, while the wider locomotive availability/cost +scalar bands remain recovered-but-parity-only until per-locomotive identity is grounded. Explicit +unmapped world-condition and world-descriptor frontier buckets still remain where current +checked-in metadata stops. Shell purchase-flow and selected-profile parity remain out of scope. +Mixed supported/unsupported real rows still stay parity-only. The PE32 hook remains useful as +capture and integration tooling, but it is no longer the main execution milestone. ## Project Docs diff --git a/crates/rrt-runtime/src/import.rs b/crates/rrt-runtime/src/import.rs index fb36654..8b1efad 100644 --- a/crates/rrt-runtime/src/import.rs +++ b/crates/rrt-runtime/src/import.rs @@ -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(); diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index af91f11..dbf65c9 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -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 { + (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 { + (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 { + (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 { + (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 { + 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 { @@ -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 = diff --git a/docs/README.md b/docs/README.md index e982a0a..ab24937 100644 --- a/docs/README.md +++ b/docs/README.md @@ -120,8 +120,13 @@ The highest-value next passes are now: can lower into `world_flag_equals` gates for boolean equality/inequality forms, so real packed rows can gate whole-game effects on existing `world_flags` - the tracked parity save-slice now keeps its remaining non-imported residue as structured - `real_packed_v1` parity records, with the first captured leftover moved out of - `unsupported_framing` and into the explicit `blocked_unmapped_real_descriptor` frontier + `real_packed_v1` parity records, with the first captured leftover now identified as the + locomotives-page `Unknown Loco Available` band and moved onto the explicit + `blocked_unmapped_world_descriptor` frontier +- the next recovered locomotives-page descriptor batch is partially executable too: + descriptors `454..456` (`All Steam/Diesel/Electric Locos Avail.`) now lower through checked-in + metadata into keyed `world_flags`, while the wider locomotive availability/cost scalar bands + remain recovered-but-parity-only until per-locomotive identity is grounded - 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 diff --git a/docs/runtime-rehost-plan.md b/docs/runtime-rehost-plan.md index a52b231..3dc97ce 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -76,7 +76,12 @@ Implemented today: `world_flags` through `world_flag_equals` without fixture-authored placeholder ids - the tracked parity save-slice no longer preserves an opaque `unsupported_framing` record; its remaining captured residue is now structurally decoded `real_packed_v1` parity state that lands - in existing explicit blocker buckets + in existing explicit blocker buckets, with the first leftover now identified as the + locomotives-page `Unknown Loco Available` band instead of anonymous descriptor residue +- the next recovered locomotives-page descriptor band is now partially executable too: + descriptors `230..240`, `241..351`, `352..451`, `453`, and `457..500` now carry recovered + world-side scalar metadata, while descriptors `454..456` (`All Steam/Diesel/Electric Locos + Avail.`) now execute as keyed `world_flags` That means the next implementation work is breadth, not bootstrap. The recommended next slice is broader real grouped-descriptor and ordinary condition-id coverage beyond the current access, diff --git a/fixtures/runtime/packed-event-parity-save-slice-fixture.json b/fixtures/runtime/packed-event-parity-save-slice-fixture.json index e9d9e61..7cee2b2 100644 --- a/fixtures/runtime/packed-event-parity-save-slice-fixture.json +++ b/fixtures/runtime/packed-event-parity-save-slice-fixture.json @@ -29,7 +29,8 @@ "packed_event_blocked_missing_condition_context_count": 0, "packed_event_blocked_territory_condition_scope_count": 0, "packed_event_blocked_missing_compact_control_count": 0, - "packed_event_blocked_unmapped_real_descriptor_count": 1, + "packed_event_blocked_unmapped_real_descriptor_count": 0, + "packed_event_blocked_unmapped_world_descriptor_count": 1, "packed_event_blocked_structural_only_count": 0, "event_runtime_record_count": 1, "total_company_cash": 0 @@ -47,10 +48,15 @@ { "decode_status": "parity_only", "payload_family": "real_packed_v1", - "import_outcome": "blocked_unmapped_real_descriptor", + "import_outcome": "blocked_unmapped_world_descriptor", "grouped_effect_rows": [ { "descriptor_id": 250, + "descriptor_label": "Unknown Loco Available", + "target_mask_bits": 8, + "parameter_family": "locomotive_availability_scalar", + "semantic_family": "scalar_assignment", + "semantic_preview": "Set Unknown Loco Available to 42", "row_shape": "scalar_assignment" } ] diff --git a/fixtures/runtime/packed-event-parity-save-slice.json b/fixtures/runtime/packed-event-parity-save-slice.json index c22fd1b..d00ddaf 100644 --- a/fixtures/runtime/packed-event-parity-save-slice.json +++ b/fixtures/runtime/packed-event-parity-save-slice.json @@ -7,7 +7,7 @@ "original_save_sha256": "parity-sample-sha256", "notes": [ "tracked as JSON save-slice document rather than raw .smp", - "preserves one structured-but-unmapped row and one semantically decoded-but-parity-only row" + "preserves one recovered-but-unmapped locomotive policy row and one semantically decoded-but-parity-only row" ] }, "save_slice": { @@ -66,9 +66,9 @@ "group_index": 0, "row_index": 0, "descriptor_id": 250, - "descriptor_label": null, - "target_mask_bits": null, - "parameter_family": null, + "descriptor_label": "Unknown Loco Available", + "target_mask_bits": 8, + "parameter_family": "locomotive_availability_scalar", "opcode": 3, "raw_scalar_value": 42, "value_byte_0x09": 0, @@ -78,11 +78,11 @@ "value_word_0x14": 0, "value_word_0x16": 0, "row_shape": "scalar_assignment", - "semantic_family": null, - "semantic_preview": null, + "semantic_family": "scalar_assignment", + "semantic_preview": "Set Unknown Loco Available to 42", "locomotive_name": null, "notes": [ - "real grouped-effect row preserved structurally; descriptor identity not yet recovered" + "recovered locomotive availability descriptor family remains parity-only until per-locomotive identity is grounded" ] } ], @@ -90,7 +90,7 @@ "executable_import_ready": false, "notes": [ "decoded from grounded real 0x4e9a row framing", - "grouped row preserved structurally but still unmapped in the checked-in descriptor table" + "recovered locomotives-page descriptor band is now checked in, but this scalar family still has no executable runtime landing surface" ] }, { diff --git a/fixtures/runtime/packed-event-world-locomotive-policy-save-slice-fixture.json b/fixtures/runtime/packed-event-world-locomotive-policy-save-slice-fixture.json new file mode 100644 index 0000000..2434118 --- /dev/null +++ b/fixtures/runtime/packed-event-world-locomotive-policy-save-slice-fixture.json @@ -0,0 +1,56 @@ +{ + "format_version": 1, + "fixture_id": "packed-event-world-locomotive-policy-save-slice-fixture", + "source": { + "kind": "captured-runtime", + "description": "Fixture proving recovered locomotive policy descriptors import and execute through the ordinary runtime path." + }, + "state_save_slice_path": "packed-event-world-locomotive-policy-save-slice.json", + "commands": [ + { + "kind": "service_trigger_kind", + "trigger_kind": 6 + } + ], + "expected_summary": { + "packed_event_collection_present": true, + "packed_event_record_count": 1, + "packed_event_decoded_record_count": 1, + "packed_event_imported_runtime_record_count": 1, + "event_runtime_record_count": 1, + "packed_event_blocked_unmapped_world_descriptor_count": 0, + "total_event_record_service_count": 1, + "total_trigger_dispatch_count": 1 + }, + "expected_state_fragment": { + "world_flags": { + "world.all_steam_locos_available": true, + "world.all_diesel_locos_available": false, + "world.all_electric_locos_available": true + }, + "packed_event_collection": { + "records": [ + { + "import_outcome": "imported", + "decoded_actions": [ + { + "kind": "set_world_flag", + "key": "world.all_steam_locos_available", + "value": true + }, + { + "kind": "set_world_flag", + "key": "world.all_diesel_locos_available", + "value": false + }, + { + "kind": "set_world_flag", + "key": "world.all_electric_locos_available", + "value": true + } + ] + } + ] + } + } +} diff --git a/fixtures/runtime/packed-event-world-locomotive-policy-save-slice.json b/fixtures/runtime/packed-event-world-locomotive-policy-save-slice.json new file mode 100644 index 0000000..f317fe3 --- /dev/null +++ b/fixtures/runtime/packed-event-world-locomotive-policy-save-slice.json @@ -0,0 +1,159 @@ +{ + "format_version": 1, + "save_slice_id": "packed-event-world-locomotive-policy-save-slice", + "source": { + "description": "Tracked save-slice document proving recovered locomotive policy descriptors import and execute as keyed world flags.", + "original_save_filename": "captured-world-locomotive-policy.gms", + "original_save_sha256": "world-locomotive-policy-sample-sha256", + "notes": [ + "tracked as JSON save-slice document rather than raw .smp", + "recovered locomotives-page policy descriptors 454..456 now import through checked-in keyed world-flag metadata" + ] + }, + "save_slice": { + "file_extension_hint": "gms", + "container_profile_family": "rt3-classic-save-container-v1", + "mechanism_family": "classic-save-rehydrate-v1", + "mechanism_confidence": "grounded", + "trailer_family": null, + "bridge_family": null, + "profile": null, + "candidate_availability_table": null, + "special_conditions_table": null, + "event_runtime_collection": { + "source_kind": "packed-event-runtime-collection", + "mechanism_family": "classic-save-rehydrate-v1", + "mechanism_confidence": "grounded", + "container_profile_family": "rt3-classic-save-container-v1", + "metadata_tag_offset": 32512, + "records_tag_offset": 32768, + "close_tag_offset": 33536, + "packed_state_version": 1001, + "packed_state_version_hex": "0x000003e9", + "live_id_bound": 61, + "live_record_count": 1, + "live_entry_ids": [61], + "decoded_record_count": 1, + "imported_runtime_record_count": 1, + "records": [ + { + "record_index": 0, + "live_entry_id": 61, + "payload_offset": 32832, + "payload_len": 176, + "decode_status": "executable", + "payload_family": "real_packed_v1", + "trigger_kind": 6, + "one_shot": false, + "compact_control": { + "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] + }, + "text_bands": [], + "standalone_condition_row_count": 0, + "standalone_condition_rows": [], + "negative_sentinel_scope": null, + "grouped_effect_row_counts": [3, 0, 0, 0], + "grouped_effect_rows": [ + { + "group_index": 0, + "row_index": 0, + "descriptor_id": 454, + "descriptor_label": "All Steam Locos Avail.", + "target_mask_bits": 8, + "parameter_family": "world_flag_toggle", + "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, + "row_shape": "bool_toggle", + "semantic_family": "bool_toggle", + "semantic_preview": "Set All Steam Locos Avail. to TRUE", + "locomotive_name": null, + "notes": [] + }, + { + "group_index": 0, + "row_index": 1, + "descriptor_id": 455, + "descriptor_label": "All Diesel Locos Avail.", + "target_mask_bits": 8, + "parameter_family": "world_flag_toggle", + "opcode": 0, + "raw_scalar_value": 0, + "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": "bool_toggle", + "semantic_family": "bool_toggle", + "semantic_preview": "Set All Diesel Locos Avail. to FALSE", + "locomotive_name": null, + "notes": [] + }, + { + "group_index": 0, + "row_index": 2, + "descriptor_id": 456, + "descriptor_label": "All Electric Locos Avail.", + "target_mask_bits": 8, + "parameter_family": "world_flag_toggle", + "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, + "row_shape": "bool_toggle", + "semantic_family": "bool_toggle", + "semantic_preview": "Set All Electric Locos Avail. to TRUE", + "locomotive_name": null, + "notes": [] + } + ], + "decoded_conditions": [], + "decoded_actions": [ + { + "kind": "set_world_flag", + "key": "world.all_steam_locos_available", + "value": true + }, + { + "kind": "set_world_flag", + "key": "world.all_diesel_locos_available", + "value": false + }, + { + "kind": "set_world_flag", + "key": "world.all_electric_locos_available", + "value": true + } + ], + "executable_import_ready": true, + "notes": [ + "recovered locomotives-page policy descriptor batch now imports as keyed world flags" + ] + } + ] + }, + "notes": [ + "whole-game locomotive policy effect sample" + ] + } +}