Expose save-side placed-structure triplets
This commit is contained in:
parent
49ee76ef2a
commit
2d6efc7c59
3 changed files with 101 additions and 2 deletions
|
|
@ -134,6 +134,9 @@ enum Command {
|
|||
RuntimeInspectSaveCompanyChairman {
|
||||
smp_path: PathBuf,
|
||||
},
|
||||
RuntimeInspectSavePlacedStructureTriplets {
|
||||
smp_path: PathBuf,
|
||||
},
|
||||
RuntimeCompareRegionFixedRowRuns {
|
||||
left_path: PathBuf,
|
||||
right_path: PathBuf,
|
||||
|
|
@ -904,6 +907,9 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Command::RuntimeInspectSaveCompanyChairman { smp_path } => {
|
||||
run_runtime_inspect_save_company_chairman(&smp_path)?;
|
||||
}
|
||||
Command::RuntimeInspectSavePlacedStructureTriplets { smp_path } => {
|
||||
run_runtime_inspect_save_placed_structure_triplets(&smp_path)?;
|
||||
}
|
||||
Command::RuntimeCompareRegionFixedRowRuns {
|
||||
left_path,
|
||||
right_path,
|
||||
|
|
@ -1131,6 +1137,13 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
|
|||
smp_path: PathBuf::from(path),
|
||||
})
|
||||
}
|
||||
[command, subcommand, path]
|
||||
if command == "runtime" && subcommand == "inspect-save-placed-structure-triplets" =>
|
||||
{
|
||||
Ok(Command::RuntimeInspectSavePlacedStructureTriplets {
|
||||
smp_path: PathBuf::from(path),
|
||||
})
|
||||
}
|
||||
[command, subcommand, left_path, right_path]
|
||||
if command == "runtime" && subcommand == "compare-region-fixed-row-runs" =>
|
||||
{
|
||||
|
|
@ -1387,7 +1400,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 inspect-save-company-chairman <file.smp> | runtime compare-region-fixed-row-runs <left.gms> <right.gms> | runtime inspect-periodic-company-service-trace <file.smp> | runtime inspect-region-service-trace <file.smp> | runtime inspect-infrastructure-asset-trace <file.smp> | runtime inspect-save-region-queued-notice-records <file.smp> | runtime inspect-placed-structure-dynamic-side-buffer <file.smp> | runtime inspect-unclassified-save-collections <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>]"
|
||||
"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 inspect-save-company-chairman <file.smp> | runtime inspect-save-placed-structure-triplets <file.smp> | runtime compare-region-fixed-row-runs <left.gms> <right.gms> | runtime inspect-periodic-company-service-trace <file.smp> | runtime inspect-region-service-trace <file.smp> | runtime inspect-infrastructure-asset-trace <file.smp> | runtime inspect-save-region-queued-notice-records <file.smp> | runtime inspect-placed-structure-dynamic-side-buffer <file.smp> | runtime inspect-unclassified-save-collections <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(),
|
||||
),
|
||||
}
|
||||
|
|
@ -1628,6 +1641,17 @@ fn run_runtime_inspect_save_company_chairman(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn run_runtime_inspect_save_placed_structure_triplets(
|
||||
smp_path: &Path,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let analysis = inspect_save_company_and_chairman_analysis_file(smp_path)?;
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&analysis.placed_structure_record_triplets)?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_runtime_compare_region_fixed_row_runs(
|
||||
left_path: &Path,
|
||||
right_path: &Path,
|
||||
|
|
|
|||
|
|
@ -3611,6 +3611,12 @@ pub struct SmpSavePlacedStructureNonzeroCompanionNamePairSummaryEntry {
|
|||
pub count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpSavePlacedStructurePolicyTrailingWordSummaryEntry {
|
||||
pub policy_trailing_word_hex: String,
|
||||
pub count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SmpPeriodicCompanyServiceTraceReport {
|
||||
pub profile_family: String,
|
||||
|
|
@ -3647,6 +3653,9 @@ pub struct SmpPeriodicCompanyServiceTraceReport {
|
|||
pub peer_site_selector_candidate_saved_companion_byte_summaries:
|
||||
Vec<SmpSavePlacedStructureProfileCompanionByteSummaryEntry>,
|
||||
#[serde(default)]
|
||||
pub peer_site_selector_candidate_saved_policy_trailing_word_summaries:
|
||||
Vec<SmpSavePlacedStructurePolicyTrailingWordSummaryEntry>,
|
||||
#[serde(default)]
|
||||
pub peer_site_selector_candidate_saved_nonzero_companion_name_pair_summaries:
|
||||
Vec<SmpSavePlacedStructureNonzeroCompanionNamePairSummaryEntry>,
|
||||
#[serde(default)]
|
||||
|
|
@ -4428,6 +4437,37 @@ fn summarize_peer_site_selector_candidate_saved_companion_bytes(
|
|||
summaries
|
||||
}
|
||||
|
||||
fn summarize_peer_site_selector_candidate_saved_policy_trailing_words(
|
||||
analysis: &SmpSaveCompanyChairmanAnalysisReport,
|
||||
) -> Vec<SmpSavePlacedStructurePolicyTrailingWordSummaryEntry> {
|
||||
let Some(triplets) = analysis.placed_structure_record_triplets.as_ref() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut grouped = BTreeMap::<String, usize>::new();
|
||||
for entry in &triplets.entries {
|
||||
*grouped
|
||||
.entry(entry.policy_trailing_word_hex.clone())
|
||||
.or_insert(0) += 1;
|
||||
}
|
||||
let mut summaries = grouped
|
||||
.into_iter()
|
||||
.map(|(policy_trailing_word_hex, count)| {
|
||||
SmpSavePlacedStructurePolicyTrailingWordSummaryEntry {
|
||||
policy_trailing_word_hex,
|
||||
count,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
summaries.sort_by(|left, right| {
|
||||
right.count.cmp(&left.count).then_with(|| {
|
||||
left.policy_trailing_word_hex
|
||||
.cmp(&right.policy_trailing_word_hex)
|
||||
})
|
||||
});
|
||||
summaries.truncate(8);
|
||||
summaries
|
||||
}
|
||||
|
||||
fn summarize_peer_site_selector_candidate_saved_nonzero_companion_name_pairs(
|
||||
analysis: &SmpSaveCompanyChairmanAnalysisReport,
|
||||
) -> Vec<SmpSavePlacedStructureNonzeroCompanionNamePairSummaryEntry> {
|
||||
|
|
@ -4510,6 +4550,8 @@ fn build_periodic_company_service_trace_report(
|
|||
summarize_peer_site_selector_candidate_saved_footer_padding(analysis);
|
||||
let peer_site_selector_candidate_saved_companion_byte_summaries =
|
||||
summarize_peer_site_selector_candidate_saved_companion_bytes(analysis);
|
||||
let peer_site_selector_candidate_saved_policy_trailing_word_summaries =
|
||||
summarize_peer_site_selector_candidate_saved_policy_trailing_words(analysis);
|
||||
let peer_site_selector_candidate_saved_nonzero_companion_name_pair_summaries =
|
||||
summarize_peer_site_selector_candidate_saved_nonzero_companion_name_pairs(analysis);
|
||||
let peer_site_restore_input_fields = vec![
|
||||
|
|
@ -4975,7 +5017,7 @@ fn build_periodic_company_service_trace_report(
|
|||
);
|
||||
if !peer_site_selector_candidate_saved_payload_summaries.is_empty() {
|
||||
notes.push(format!(
|
||||
"The periodic-company trace now also carries a compact save-side summary of the tagged 0x5dc1 placed-structure profile payload/status pairs already parsed from the 0x36b1 triplet seam; dominant current pair is {} / {} x{}, dominant adjacent payload delta is {:?}, dominant post-secondary byte is {:?}, and dominant pre-footer padding len is {:?}.",
|
||||
"The periodic-company trace now also carries a compact save-side summary of the tagged 0x5dc1 placed-structure profile payload/status pairs already parsed from the 0x36b1 triplet seam; dominant current pair is {} / {} x{}, dominant adjacent payload delta is {:?}, dominant post-secondary byte is {:?}, dominant fixed-policy trailing word is {:?}, and dominant pre-footer padding len is {:?}.",
|
||||
peer_site_selector_candidate_saved_payload_summaries[0].profile_payload_dword_hex,
|
||||
peer_site_selector_candidate_saved_payload_summaries[0].profile_status_kind,
|
||||
peer_site_selector_candidate_saved_payload_summaries[0].count,
|
||||
|
|
@ -4985,6 +5027,9 @@ fn build_periodic_company_service_trace_report(
|
|||
peer_site_selector_candidate_saved_companion_byte_summaries
|
||||
.first()
|
||||
.map(|entry| entry.companion_byte_hex.as_str()),
|
||||
peer_site_selector_candidate_saved_policy_trailing_word_summaries
|
||||
.first()
|
||||
.map(|entry| entry.policy_trailing_word_hex.as_str()),
|
||||
peer_site_selector_candidate_saved_footer_padding_summaries
|
||||
.first()
|
||||
.map(|entry| entry.padding_len)
|
||||
|
|
@ -5004,6 +5049,19 @@ fn build_periodic_company_service_trace_report(
|
|||
notes.push(
|
||||
"Direct disassembly now also separates the narrower peer-class gate from that payload residue: 0x0047fd50 resolves the linked peer through [site+0x04], reads candidate class byte [candidate+0x8c], and returns true only for values 0/1/2 while rejecting 3/4 and above. That means the newly isolated post-secondary byte is not the already-grounded station-or-transit class gate itself; it remains a separate saved discriminator above the restored name-pair payload.".to_string(),
|
||||
);
|
||||
if let (Some(dominant_companion), Some(dominant_trailing_word)) = (
|
||||
peer_site_selector_candidate_saved_companion_byte_summaries.first(),
|
||||
peer_site_selector_candidate_saved_policy_trailing_word_summaries.first(),
|
||||
) {
|
||||
notes.push(format!(
|
||||
"The same focused 0x36b1 triplet probe now also keeps the fixed-policy trailing word narrow at {} x{} while the profile side stays dominated by companion byte {} and payload/status pair {} / {}. Together that keeps the checked-in triplet seam looking like local structure/profile state rather than the missing acquisition owner-company lane [site+0x276] or cached tri-lane [site+0x310/+0x338/+0x360].",
|
||||
dominant_trailing_word.policy_trailing_word_hex,
|
||||
dominant_trailing_word.count,
|
||||
dominant_companion.companion_byte_hex,
|
||||
peer_site_selector_candidate_saved_payload_summaries[0].profile_payload_dword_hex,
|
||||
peer_site_selector_candidate_saved_payload_summaries[0].profile_status_kind,
|
||||
));
|
||||
}
|
||||
notes.push(
|
||||
"The replay strip is tighter now too. 0x00444690 is the current late world bring-up caller of 0x004133b0, that outer owner drains queued site ids through 0x0040e450 and then sweeps every live placed structure through 0x0040ee10, and 0x0040ee10 itself reaches 0x0040edf6 -> 0x00480710 plus the later 0x0040e360 follow-on. A separate runtime path at 0x004160aa also re-enters 0x0040ee10 later. So [peer+0x08] replay is no longer the open question, and [site+0x04] is no longer an owner mystery either: the local linked-site helper strip seeds [site+0x3cc/+0x3d0] from 0x62b2fc / 0x62b268, reaches the save-backed 0x0045c150 / 0x0045c310 owner directly, that owner fills [owner+0x23e/+0x242] from tagged payload 0x5dc1, and 0x0045c36e then feeds [owner+0x23e] through 0x00456100 -> 0x00455b70 -> 0x0052edf0 into [site+0x04]. The remaining non-hook target is now the smaller shellless-simulation question: which subset of those persisted site/peer fields is actually sufficient to run 0x004014b0 and its city-connection sibling without shell state.".to_string(),
|
||||
);
|
||||
|
|
@ -5027,6 +5085,7 @@ fn build_periodic_company_service_trace_report(
|
|||
peer_site_selector_candidate_saved_payload_delta_summaries,
|
||||
peer_site_selector_candidate_saved_footer_padding_summaries,
|
||||
peer_site_selector_candidate_saved_companion_byte_summaries,
|
||||
peer_site_selector_candidate_saved_policy_trailing_word_summaries,
|
||||
peer_site_selector_candidate_saved_nonzero_companion_name_pair_summaries,
|
||||
peer_site_restore_input_fields,
|
||||
peer_site_runtime_input_fields,
|
||||
|
|
@ -28021,6 +28080,15 @@ mod tests {
|
|||
trace.peer_site_selector_candidate_saved_companion_byte_summaries[0].companion_byte_hex,
|
||||
"0x00"
|
||||
);
|
||||
assert_eq!(
|
||||
trace.peer_site_selector_candidate_saved_policy_trailing_word_summaries[0]
|
||||
.policy_trailing_word_hex,
|
||||
"0x0101"
|
||||
);
|
||||
assert_eq!(
|
||||
trace.peer_site_selector_candidate_saved_policy_trailing_word_summaries[0].count,
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
trace.peer_site_selector_candidate_saved_nonzero_companion_name_pair_summaries[0]
|
||||
.primary_name,
|
||||
|
|
|
|||
|
|
@ -151,6 +151,13 @@ Working rule:
|
|||
it iterates the live placed-structure collection `0x0062b26c`, filters rows through
|
||||
`0x0040c990`, optional owner-company match `[site+0x276]`, and row vtable slot `+0x70`,
|
||||
which keeps that branch on the live application side rather than replay
|
||||
- the focused save-side triplet probe is now available directly too:
|
||||
`runtime inspect-save-placed-structure-triplets <save.gms>` dumps the grounded
|
||||
`0x36b1/0x55f1/0x55f2/0x55f3` rows without the wider periodic trace wrapper
|
||||
- real-save output from that focused probe rules the checked-in triplet seam down further:
|
||||
on grounded `p.gms`, all 2026 rows keep policy trailing word `0x0101`, the profile side is
|
||||
dominated by companion byte `0x00` with status `unset` (`1885` rows) plus farm-growth buckets
|
||||
(`138` rows), and the only nonzero companion-byte residue is `3` `TextileMill` rows
|
||||
- the create-side family is grounded separately too:
|
||||
city-connection direct placement already reaches
|
||||
`0x00402cb0 -> 0x00403ed5/0x0040446b -> 0x004134d0 -> 0x0040ef10`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue