Extend cargo selector and save-world analysis surfaces

This commit is contained in:
Jan Petykiewicz 2026-04-17 13:01:26 -07:00
commit 6b8f849731
11 changed files with 639 additions and 26 deletions

View file

@ -18,16 +18,17 @@ use rrt_model::{
};
use rrt_runtime::{
CAMPAIGN_SCENARIO_COUNT, CampaignExeInspectionReport, CargoEconomySourceReport,
CargoSkinInspectionReport, CargoTypeInspectionReport, OBSERVED_CAMPAIGN_SCENARIO_NAMES,
OVERLAY_IMPORT_DOCUMENT_FORMAT_VERSION, Pk4ExtractionReport, Pk4InspectionReport,
RuntimeOverlayImportDocument, RuntimeOverlayImportDocumentSource, RuntimeSaveSliceDocument,
RuntimeSaveSliceDocumentSource, RuntimeSnapshotDocument, RuntimeSnapshotSource, RuntimeSummary,
SAVE_SLICE_DOCUMENT_FORMAT_VERSION, SNAPSHOT_FORMAT_VERSION, SmpClassicPackedProfileBlock,
SmpInspectionReport, SmpLoadedSaveSlice, SmpRt3105PackedProfileBlock, SmpSaveLoadSummary,
WinInspectionReport, execute_step_command, extract_pk4_entry_file, inspect_campaign_exe_file,
inspect_cargo_economy_sources_with_bindings, inspect_cargo_skin_pk4, inspect_cargo_types_dir,
inspect_pk4_file, inspect_smp_file, inspect_win_file, load_runtime_snapshot_document,
load_runtime_state_import, load_save_slice_file, project_save_slice_to_runtime_state_import,
CargoSelectorReport, CargoSkinInspectionReport, CargoTypeInspectionReport,
OBSERVED_CAMPAIGN_SCENARIO_NAMES, OVERLAY_IMPORT_DOCUMENT_FORMAT_VERSION, Pk4ExtractionReport,
Pk4InspectionReport, RuntimeOverlayImportDocument, RuntimeOverlayImportDocumentSource,
RuntimeSaveSliceDocument, RuntimeSaveSliceDocumentSource, RuntimeSnapshotDocument,
RuntimeSnapshotSource, RuntimeSummary, SAVE_SLICE_DOCUMENT_FORMAT_VERSION,
SNAPSHOT_FORMAT_VERSION, SmpClassicPackedProfileBlock, SmpInspectionReport, SmpLoadedSaveSlice,
SmpRt3105PackedProfileBlock, SmpSaveLoadSummary, WinInspectionReport, execute_step_command,
extract_pk4_entry_file, inspect_campaign_exe_file, inspect_cargo_economy_sources_with_bindings,
inspect_cargo_skin_pk4, inspect_cargo_types_dir, inspect_pk4_file, inspect_smp_file,
inspect_win_file, load_runtime_snapshot_document, load_runtime_state_import,
load_save_slice_file, project_save_slice_to_runtime_state_import,
save_runtime_overlay_import_document, save_runtime_save_slice_document,
save_runtime_snapshot_document, validate_runtime_snapshot_document,
};
@ -151,6 +152,14 @@ enum Command {
cargo_types_dir: PathBuf,
cargo_skin_pk4_path: PathBuf,
},
RuntimeInspectCargoProductionSelector {
cargo_types_dir: PathBuf,
cargo_skin_pk4_path: PathBuf,
},
RuntimeInspectCargoPriceSelector {
cargo_types_dir: PathBuf,
cargo_skin_pk4_path: PathBuf,
},
RuntimeInspectWin {
win_path: PathBuf,
},
@ -303,6 +312,13 @@ struct RuntimeCargoEconomyInspectionOutput {
inspection: CargoEconomySourceReport,
}
#[derive(Debug, Serialize)]
struct RuntimeCargoSelectorInspectionOutput {
cargo_types_dir: String,
cargo_skin_pk4_path: String,
selector: CargoSelectorReport,
}
#[derive(Debug, Serialize)]
struct RuntimeWinInspectionOutput {
path: String,
@ -860,6 +876,18 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
} => {
run_runtime_inspect_cargo_economy_sources(&cargo_types_dir, &cargo_skin_pk4_path)?;
}
Command::RuntimeInspectCargoProductionSelector {
cargo_types_dir,
cargo_skin_pk4_path,
} => {
run_runtime_inspect_cargo_production_selector(&cargo_types_dir, &cargo_skin_pk4_path)?;
}
Command::RuntimeInspectCargoPriceSelector {
cargo_types_dir,
cargo_skin_pk4_path,
} => {
run_runtime_inspect_cargo_price_selector(&cargo_types_dir, &cargo_skin_pk4_path)?;
}
Command::RuntimeInspectWin { win_path } => {
run_runtime_inspect_win(&win_path)?;
}
@ -1060,6 +1088,22 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
cargo_skin_pk4_path: PathBuf::from(cargo_skin_pk4_path),
})
}
[command, subcommand, cargo_types_dir, cargo_skin_pk4_path]
if command == "runtime" && subcommand == "inspect-cargo-production-selector" =>
{
Ok(Command::RuntimeInspectCargoProductionSelector {
cargo_types_dir: PathBuf::from(cargo_types_dir),
cargo_skin_pk4_path: PathBuf::from(cargo_skin_pk4_path),
})
}
[command, subcommand, cargo_types_dir, cargo_skin_pk4_path]
if command == "runtime" && subcommand == "inspect-cargo-price-selector" =>
{
Ok(Command::RuntimeInspectCargoPriceSelector {
cargo_types_dir: PathBuf::from(cargo_types_dir),
cargo_skin_pk4_path: PathBuf::from(cargo_skin_pk4_path),
})
}
[command, subcommand, path] if command == "runtime" && subcommand == "inspect-win" => {
Ok(Command::RuntimeInspectWin {
win_path: PathBuf::from(path),
@ -1195,7 +1239,7 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
})
}
_ => Err(
"usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json> | runtime validate-fixture <fixture.json> | runtime summarize-fixture <fixture.json> | runtime export-fixture-state <fixture.json> <snapshot.json> | runtime diff-state <left.json> <right.json> | runtime summarize-state <snapshot.json> | runtime import-state <input.json> <snapshot.json> | runtime inspect-smp <file.smp> | runtime summarize-save-load <file.smp> | runtime load-save-slice <file.smp> | runtime import-save-state <file.smp> <snapshot.json> | runtime export-save-slice <file.smp> <save-slice.json> | runtime export-overlay-import <snapshot.json> <save-slice.json> <overlay-import.json> | runtime inspect-pk4 <file.pk4> | runtime inspect-cargo-types <CargoTypes-dir> | runtime inspect-cargo-skins <Cargo106.PK4> | runtime inspect-cargo-economy-sources <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-win <file.win> | runtime extract-pk4-entry <file.pk4> <entry-name> <output-path> | runtime inspect-campaign-exe <RT3.exe> | runtime compare-classic-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-105-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-candidate-table <file1> <file2> [fileN...] | runtime compare-recipe-book-lines <file1> <file2> [fileN...] | runtime compare-setup-payload-core <file1> <file2> [fileN...] | runtime compare-setup-launch-payload <file1> <file2> [fileN...] | runtime compare-post-special-conditions-scalars <file1> <file2> [fileN...] | runtime scan-candidate-table-headers <root-dir> | runtime scan-special-conditions <root-dir> | runtime scan-aligned-runtime-rule-band <root-dir> | runtime scan-post-special-conditions-scalars <root-dir> | runtime scan-post-special-conditions-tail <root-dir> | runtime scan-recipe-book-lines <root-dir> | runtime export-profile-block <save.gms> <profile.json>]"
"usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json> | runtime validate-fixture <fixture.json> | runtime summarize-fixture <fixture.json> | runtime export-fixture-state <fixture.json> <snapshot.json> | runtime diff-state <left.json> <right.json> | runtime summarize-state <snapshot.json> | runtime import-state <input.json> <snapshot.json> | runtime inspect-smp <file.smp> | runtime summarize-save-load <file.smp> | runtime load-save-slice <file.smp> | runtime import-save-state <file.smp> <snapshot.json> | runtime export-save-slice <file.smp> <save-slice.json> | runtime export-overlay-import <snapshot.json> <save-slice.json> <overlay-import.json> | runtime inspect-pk4 <file.pk4> | runtime inspect-cargo-types <CargoTypes-dir> | runtime inspect-cargo-skins <Cargo106.PK4> | runtime inspect-cargo-economy-sources <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-production-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-price-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-win <file.win> | runtime extract-pk4-entry <file.pk4> <entry-name> <output-path> | runtime inspect-campaign-exe <RT3.exe> | runtime compare-classic-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-105-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-candidate-table <file1> <file2> [fileN...] | runtime compare-recipe-book-lines <file1> <file2> [fileN...] | runtime compare-setup-payload-core <file1> <file2> [fileN...] | runtime compare-setup-launch-payload <file1> <file2> [fileN...] | runtime compare-post-special-conditions-scalars <file1> <file2> [fileN...] | runtime scan-candidate-table-headers <root-dir> | runtime scan-special-conditions <root-dir> | runtime scan-aligned-runtime-rule-band <root-dir> | runtime scan-post-special-conditions-scalars <root-dir> | runtime scan-post-special-conditions-tail <root-dir> | runtime scan-recipe-book-lines <root-dir> | runtime export-profile-block <save.gms> <profile.json>]"
.into(),
),
}
@ -1598,6 +1642,49 @@ fn run_runtime_inspect_cargo_economy_sources(
Ok(())
}
fn run_runtime_inspect_cargo_production_selector(
cargo_types_dir: &Path,
cargo_skin_pk4_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let cargo_bindings_path =
Path::new("artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json");
let inspection = inspect_cargo_economy_sources_with_bindings(
cargo_types_dir,
cargo_skin_pk4_path,
Some(cargo_bindings_path),
)?;
let selector = inspection
.production_selector
.ok_or("named cargo production selector is not available in the checked-in bindings")?;
let report = RuntimeCargoSelectorInspectionOutput {
cargo_types_dir: cargo_types_dir.display().to_string(),
cargo_skin_pk4_path: cargo_skin_pk4_path.display().to_string(),
selector,
};
println!("{}", serde_json::to_string_pretty(&report)?);
Ok(())
}
fn run_runtime_inspect_cargo_price_selector(
cargo_types_dir: &Path,
cargo_skin_pk4_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let cargo_bindings_path =
Path::new("artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json");
let inspection = inspect_cargo_economy_sources_with_bindings(
cargo_types_dir,
cargo_skin_pk4_path,
Some(cargo_bindings_path),
)?;
let report = RuntimeCargoSelectorInspectionOutput {
cargo_types_dir: cargo_types_dir.display().to_string(),
cargo_skin_pk4_path: cargo_skin_pk4_path.display().to_string(),
selector: inspection.price_selector,
};
println!("{}", serde_json::to_string_pretty(&report)?);
Ok(())
}
fn run_runtime_inspect_win(win_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let report = RuntimeWinInspectionOutput {
path: win_path.display().to_string(),
@ -4665,9 +4752,14 @@ mod tests {
);
let stock_prices_shell_save_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../fixtures/runtime/packed-event-stock-prices-shell-save-slice-fixture.json");
let game_won_shell_save_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../fixtures/runtime/packed-event-game-won-shell-save-slice-fixture.json");
let merger_premium_shell_save_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
"../../fixtures/runtime/packed-event-merger-premium-shell-save-slice-fixture.json",
);
let set_human_control_shell_save_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
"../../fixtures/runtime/packed-event-set-human-control-shell-save-slice-fixture.json",
);
let investor_confidence_condition_save_fixture = PathBuf::from(env!(
"CARGO_MANIFEST_DIR"
))
@ -4771,8 +4863,12 @@ mod tests {
.expect("save-slice-backed credit-rating descriptor fixture should summarize");
run_runtime_summarize_fixture(&stock_prices_shell_save_fixture)
.expect("save-slice-backed shell-owned stock-prices fixture should summarize");
run_runtime_summarize_fixture(&game_won_shell_save_fixture)
.expect("save-slice-backed shell-owned game-won fixture should summarize");
run_runtime_summarize_fixture(&merger_premium_shell_save_fixture)
.expect("save-slice-backed shell-owned merger-premium fixture should summarize");
run_runtime_summarize_fixture(&set_human_control_shell_save_fixture)
.expect("save-slice-backed shell-owned set-human-control fixture should summarize");
run_runtime_summarize_fixture(&investor_confidence_condition_save_fixture)
.expect("save-slice-backed investor-confidence condition fixture should summarize");
run_runtime_summarize_fixture(&management_attitude_condition_save_fixture)

View file

@ -69,6 +69,8 @@ pub struct CargoEconomySourceReport {
pub cargo_skin_only_visible_names: Vec<String>,
pub live_registry_count: usize,
pub live_registry_entries: Vec<CargoLiveRegistryEntry>,
pub price_selector_candidate_excess_count: usize,
pub price_selector_candidate_only_visible_names: Vec<String>,
pub production_selector: Option<CargoSelectorReport>,
pub price_selector: CargoSelectorReport,
pub notes: Vec<String>,
@ -271,7 +273,25 @@ fn build_cargo_economy_source_report(
build_live_registry_entries(&cargo_types.entries, &cargo_skins.entries);
let production_selector =
cargo_bindings.map(|bindings| build_production_selector(bindings, &live_registry_entries));
let price_selector_candidate_only_visible_names = production_selector
.as_ref()
.map(|selector| {
let selector_names = selector
.entries
.iter()
.map(|entry| entry.visible_name.as_str())
.collect::<BTreeSet<_>>();
live_registry_entries
.iter()
.filter(|entry| !selector_names.contains(entry.visible_name.as_str()))
.map(|entry| entry.visible_name.clone())
.collect::<Vec<_>>()
})
.unwrap_or_default();
let price_selector = build_price_selector(&live_registry_entries);
let price_selector_candidate_excess_count = live_registry_entries
.len()
.saturating_sub(NAMED_CARGO_PRICE_DESCRIPTOR_ROW_COUNT);
let mut notes = Vec::new();
notes.push(format!(
@ -313,6 +333,8 @@ fn build_cargo_economy_source_report(
cargo_skin_only_visible_names,
live_registry_count: live_registry_entries.len(),
live_registry_entries,
price_selector_candidate_excess_count,
price_selector_candidate_only_visible_names,
production_selector,
price_selector,
notes,
@ -661,6 +683,12 @@ mod tests {
);
assert!(!report.price_selector.exact_resolution);
assert_eq!(report.price_selector.candidate_registry_count, 3);
assert_eq!(report.price_selector_candidate_excess_count, 0);
assert!(
report
.price_selector_candidate_only_visible_names
.is_empty()
);
assert!(report.production_selector.is_none());
}
@ -733,5 +761,11 @@ mod tests {
]
);
assert_eq!(selector.entries[1].visible_name, "Coal");
assert!(
report
.price_selector_candidate_only_visible_names
.is_empty()
);
assert_eq!(report.price_selector_candidate_excess_count, 0);
}
}

View file

@ -95,6 +95,11 @@ const RT3_SAVE_WORLD_BLOCK_NEXT_CHUNK_TAG: u32 = 0x000032c9;
const RT3_SAVE_WORLD_BLOCK_LEN: usize = 0x4f2c;
const RT3_SAVE_WORLD_BLOCK_SELECTED_COMPANY_ID_RELATIVE_OFFSET: usize = 0x1d;
const RT3_SAVE_WORLD_BLOCK_SELECTED_CHAIRMAN_PROFILE_ID_RELATIVE_OFFSET: usize = 0x21;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET: usize = 0x83;
const RT3_SAVE_WORLD_BLOCK_CAMPAIGN_OVERRIDE_FLAG_RELATIVE_OFFSET: usize = 0xc1;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_RELATIVE_OFFSET: usize = 0x0bbf;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_COUNT: usize = 16;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_STRIDE: usize = 9;
const EVENT_RUNTIME_COLLECTION_METADATA_TAG: u16 = 0x4e99;
const EVENT_RUNTIME_COLLECTION_RECORDS_TAG: u16 = 0x4e9a;
const EVENT_RUNTIME_COLLECTION_CLOSE_TAG: u16 = 0x4e9b;
@ -1464,6 +1469,13 @@ pub struct SmpSaveWorldSelectionContextProbe {
pub selected_chairman_profile_id_offset: usize,
pub selected_chairman_profile_id: u32,
pub selected_chairman_profile_id_hex: String,
pub chairman_slot_selector_offset: usize,
pub chairman_slot_selectors: Vec<u8>,
pub campaign_override_flag_offset: usize,
pub campaign_override_flag: u8,
pub campaign_override_flag_hex: String,
pub chairman_role_gate_offset: usize,
pub chairman_role_gate_bytes: Vec<u8>,
pub evidence: Vec<String>,
}
@ -2551,6 +2563,13 @@ pub fn load_save_slice_from_report(
"Raw save fixed world block exposes selected_chairman_profile_id={} at file offset 0x{:x}.",
probe.selected_chairman_profile_id, probe.selected_chairman_profile_id_offset
));
notes.push(format!(
"Raw save fixed world block also exposes {} chairman slot selector bytes at file offset 0x{:x} and campaign_override_flag={} at file offset 0x{:x}.",
probe.chairman_slot_selectors.len(),
probe.chairman_slot_selector_offset,
probe.campaign_override_flag,
probe.campaign_override_flag_offset
));
notes.push(
"Raw save inspection still does not reconstruct full company_roster or chairman_profile_table payloads; the grounded package-save path only proves selection ids and header-level collection state for those families."
.to_string(),
@ -6933,8 +6952,31 @@ fn parse_save_world_selection_context_probe(
payload_offset + RT3_SAVE_WORLD_BLOCK_SELECTED_COMPANY_ID_RELATIVE_OFFSET;
let selected_chairman_profile_id_offset =
payload_offset + RT3_SAVE_WORLD_BLOCK_SELECTED_CHAIRMAN_PROFILE_ID_RELATIVE_OFFSET;
let chairman_slot_selector_offset =
payload_offset + RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET;
let campaign_override_flag_offset =
payload_offset + RT3_SAVE_WORLD_BLOCK_CAMPAIGN_OVERRIDE_FLAG_RELATIVE_OFFSET;
let chairman_role_gate_offset =
payload_offset + RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_RELATIVE_OFFSET;
let selected_company_id = read_u32_at(bytes, selected_company_id_offset)?;
let selected_chairman_profile_id = read_u32_at(bytes, selected_chairman_profile_id_offset)?;
let chairman_slot_selectors = bytes
.get(
chairman_slot_selector_offset
..chairman_slot_selector_offset + RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_COUNT,
)?
.to_vec();
let campaign_override_flag = *bytes.get(campaign_override_flag_offset)?;
let chairman_role_gate_bytes = (0..RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_COUNT)
.map(|slot_index| {
bytes
.get(
chairman_role_gate_offset
+ slot_index * RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_STRIDE,
)
.copied()
})
.collect::<Option<Vec<_>>>()?;
return Some(SmpSaveWorldSelectionContextProbe {
profile_family: profile.profile_family.clone(),
source_kind: "save-direct-world-block".to_string(),
@ -6949,6 +6991,13 @@ fn parse_save_world_selection_context_probe(
selected_chairman_profile_id_offset,
selected_chairman_profile_id,
selected_chairman_profile_id_hex: format!("0x{selected_chairman_profile_id:08x}"),
chairman_slot_selector_offset,
chairman_slot_selectors,
campaign_override_flag_offset,
campaign_override_flag,
campaign_override_flag_hex: format!("0x{campaign_override_flag:02x}"),
chairman_role_gate_offset,
chairman_role_gate_bytes,
evidence: vec![
format!(
"chunk tag 0x32c8 at 0x{chunk_tag_offset:x} matches the fixed [world+0x04] save block"
@ -6964,6 +7013,19 @@ fn parse_save_world_selection_context_probe(
"selected chairman profile id comes from payload +0x{:x} ([world+0x25])",
RT3_SAVE_WORLD_BLOCK_SELECTED_CHAIRMAN_PROFILE_ID_RELATIVE_OFFSET
),
format!(
"16 chairman slot selector bytes come from payload +0x{:x} ([world+0x87])",
RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET
),
format!(
"campaign override flag comes from payload +0x{:x} ([world+0xc5])",
RT3_SAVE_WORLD_BLOCK_CAMPAIGN_OVERRIDE_FLAG_RELATIVE_OFFSET
),
format!(
"chairman role-gate bytes come from payload +0x{:x} + slot*0x{:x} ([world+0x0bc3+slot*9])",
RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_RELATIVE_OFFSET,
RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_STRIDE
),
],
});
}
@ -13248,6 +13310,17 @@ mod tests {
+ RT3_SAVE_WORLD_BLOCK_SELECTED_CHAIRMAN_PROFILE_ID_RELATIVE_OFFSET
+ 4]
.copy_from_slice(&9u32.to_le_bytes());
bytes[payload_offset + RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET
..payload_offset
+ RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET
+ RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_COUNT]
.copy_from_slice(&[3, 1, 4, 1, 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
bytes[payload_offset + RT3_SAVE_WORLD_BLOCK_CAMPAIGN_OVERRIDE_FLAG_RELATIVE_OFFSET] = 1;
for (slot_index, role_gate) in [2u8, 1, 0, 2].into_iter().enumerate() {
bytes[payload_offset
+ RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_RELATIVE_OFFSET
+ slot_index * RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_STRIDE] = role_gate;
}
let next_chunk_offset = payload_offset + RT3_SAVE_WORLD_BLOCK_LEN;
bytes[next_chunk_offset..next_chunk_offset + 4]
.copy_from_slice(&RT3_SAVE_WORLD_BLOCK_NEXT_CHUNK_TAG.to_le_bytes());
@ -13267,6 +13340,9 @@ mod tests {
assert_eq!(probe.payload_offset, payload_offset);
assert_eq!(probe.selected_company_id, 7);
assert_eq!(probe.selected_chairman_profile_id, 9);
assert_eq!(probe.chairman_slot_selectors[..6], [3, 1, 4, 1, 5, 9]);
assert_eq!(probe.campaign_override_flag, 1);
assert_eq!(probe.chairman_role_gate_bytes[..4], [2, 1, 0, 2]);
}
#[test]
@ -13310,6 +13386,13 @@ mod tests {
selected_chairman_profile_id_offset: 0x3f3,
selected_chairman_profile_id: 9,
selected_chairman_profile_id_hex: "0x00000009".to_string(),
chairman_slot_selector_offset: 0x455,
chairman_slot_selectors: vec![3, 1, 4, 1, 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
campaign_override_flag_offset: 0x493,
campaign_override_flag: 1,
campaign_override_flag_hex: "0x01".to_string(),
chairman_role_gate_offset: 0xf91,
chairman_role_gate_bytes: vec![2, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
evidence: vec![],
});
@ -13339,6 +13422,12 @@ mod tests {
.iter()
.any(|note| note.contains("selected_chairman_profile_id=9"))
);
assert!(
slice
.notes
.iter()
.any(|note| note.contains("campaign_override_flag=1"))
);
}
#[test]