Expose recovered Tier2 building source tables

This commit is contained in:
Jan Petykiewicz 2026-04-19 14:58:27 -07:00
commit 311712b051

View file

@ -63,6 +63,15 @@ pub struct BuildingTypeNamedBindingComparison {
pub source_only_canonical_stems: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BuildingTypeRecoveredTableSummary {
pub recovered_style_themes: Vec<String>,
pub recovered_source_kinds: Vec<String>,
pub present_style_station_entries: Vec<String>,
pub present_standalone_entries: Vec<String>,
pub bare_port_warehouse_files: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BuildingTypeSourceReport {
pub directory_path: String,
@ -72,6 +81,7 @@ pub struct BuildingTypeSourceReport {
pub bca_selector_pattern_count: usize,
#[serde(default)]
pub named_binding_comparison: Option<BuildingTypeNamedBindingComparison>,
pub recovered_table_summary: BuildingTypeRecoveredTableSummary,
pub notes: Vec<String>,
pub bca_selector_patterns: Vec<BuildingTypeBcaSelectorPatternSummary>,
pub files: Vec<BuildingTypeSourceFile>,
@ -215,6 +225,7 @@ pub fn inspect_building_types_dir_with_bindings(
"BuildingTypes sources are grouped by a canonical stem that lowercases and strips spaces, underscores, and hyphens so paired .bca/.bty variants collapse onto one asset token.".to_string(),
"This report is an offline asset-pool view only; it does not by itself assign live candidate ids or prove scenario candidate-table availability.".to_string(),
"For .bca files, the report also exposes the narrow selector-byte window at offsets 0xb8..0xbb used by the grounded aux-candidate and live-candidate stream decoders.".to_string(),
"The recovered stock table above the Tier-2 building seam combines one style/theme subset with one source-kind table; this report now surfaces the matching on-disk filename families directly.".to_string(),
];
let named_binding_comparison = if let Some(bindings_path) = bindings_path {
@ -222,6 +233,7 @@ pub fn inspect_building_types_dir_with_bindings(
} else {
None
};
let recovered_table_summary = summarize_recovered_table_families(&entries, &files);
Ok(BuildingTypeSourceReport {
directory_path: path.display().to_string(),
@ -230,6 +242,7 @@ pub fn inspect_building_types_dir_with_bindings(
unique_canonical_stem_count: entries.len(),
bca_selector_pattern_count: bca_selector_patterns.len(),
named_binding_comparison,
recovered_table_summary,
notes,
bca_selector_patterns,
files,
@ -293,6 +306,85 @@ fn canonicalize_building_stem(stem: &str) -> String {
.collect()
}
fn summarize_recovered_table_families(
entries: &[BuildingTypeSourceEntry],
files: &[BuildingTypeSourceFile],
) -> BuildingTypeRecoveredTableSummary {
const RECOVERED_STYLE_THEMES: [&str; 6] = [
"Victorian",
"Tudor",
"SoWest",
"Persian",
"Kyoto",
"ClpBrd",
];
const RECOVERED_SOURCE_KINDS: [&str; 5] = [
"StationSml",
"StationMed",
"StationLrg",
"ServiceTower",
"Maintenance",
];
let entry_by_canonical = entries
.iter()
.map(|entry| (entry.canonical_stem.clone(), entry))
.collect::<BTreeMap<_, _>>();
let mut present_style_station_entries = Vec::new();
for style in RECOVERED_STYLE_THEMES {
for source_kind in ["StationSml", "StationMed", "StationLrg"] {
let canonical = canonicalize_building_stem(&format!("{style}{source_kind}"));
if let Some(entry) = entry_by_canonical.get(&canonical) {
if let Some(raw_stem) = entry.raw_stems.first() {
present_style_station_entries.push(raw_stem.clone());
}
}
}
}
present_style_station_entries.sort();
present_style_station_entries.dedup();
let mut present_standalone_entries = Vec::new();
for raw_name in ["ServiceTower", "Maintenance"] {
let canonical = canonicalize_building_stem(raw_name);
if let Some(entry) = entry_by_canonical.get(&canonical) {
if let Some(raw_stem) = entry.raw_stems.first() {
present_standalone_entries.push(raw_stem.clone());
}
}
}
present_standalone_entries.sort();
present_standalone_entries.dedup();
let mut bare_port_warehouse_files = files
.iter()
.filter(|file| {
matches!(
file.canonical_stem.as_str(),
"port" | "warehouse"
)
})
.map(|file| file.file_name.clone())
.collect::<Vec<_>>();
bare_port_warehouse_files.sort();
bare_port_warehouse_files.dedup();
BuildingTypeRecoveredTableSummary {
recovered_style_themes: RECOVERED_STYLE_THEMES
.into_iter()
.map(str::to_string)
.collect(),
recovered_source_kinds: RECOVERED_SOURCE_KINDS
.into_iter()
.map(str::to_string)
.collect(),
present_style_station_entries,
present_standalone_entries,
bare_port_warehouse_files,
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
struct BuildingBindingArtifact {
bindings: Vec<BuildingBindingRow>,
@ -323,4 +415,68 @@ mod tests {
assert_eq!(probe.byte_0xb8_hex, "0x12");
assert_eq!(probe.byte_0xbb_hex, "0x78");
}
#[test]
fn summarizes_recovered_table_families_from_entries_and_files() {
let entries = vec![
BuildingTypeSourceEntry {
canonical_stem: canonicalize_building_stem("VictorianStationSml"),
raw_stems: vec!["VictorianStationSml".to_string()],
source_kinds: vec![BuildingTypeSourceKind::Bty],
file_names: vec!["VictorianStationSml.bty".to_string()],
},
BuildingTypeSourceEntry {
canonical_stem: canonicalize_building_stem("ClpBrdStationLrg"),
raw_stems: vec!["ClpbrdStationLrg".to_string()],
source_kinds: vec![BuildingTypeSourceKind::Bty],
file_names: vec!["ClpbrdStationLrg.bty".to_string()],
},
BuildingTypeSourceEntry {
canonical_stem: canonicalize_building_stem("Maintenance"),
raw_stems: vec!["Maintenance".to_string()],
source_kinds: vec![BuildingTypeSourceKind::Bty],
file_names: vec!["Maintenance.bty".to_string()],
},
BuildingTypeSourceEntry {
canonical_stem: canonicalize_building_stem("ServiceTower"),
raw_stems: vec!["ServiceTower".to_string()],
source_kinds: vec![BuildingTypeSourceKind::Bty],
file_names: vec!["ServiceTower.bty".to_string()],
},
];
let files = vec![
BuildingTypeSourceFile {
file_name: "Port.bty".to_string(),
raw_stem: "Port".to_string(),
canonical_stem: canonicalize_building_stem("Port"),
source_kind: BuildingTypeSourceKind::Bty,
byte_len: None,
bca_selector_probe: None,
},
BuildingTypeSourceFile {
file_name: "Warehouse.bca".to_string(),
raw_stem: "Warehouse".to_string(),
canonical_stem: canonicalize_building_stem("Warehouse"),
source_kind: BuildingTypeSourceKind::Bca,
byte_len: None,
bca_selector_probe: None,
},
];
let summary = summarize_recovered_table_families(&entries, &files);
assert!(summary
.present_style_station_entries
.contains(&"VictorianStationSml".to_string()));
assert!(summary
.present_style_station_entries
.contains(&"ClpbrdStationLrg".to_string()));
assert_eq!(
summary.present_standalone_entries,
vec!["Maintenance".to_string(), "ServiceTower".to_string()]
);
assert_eq!(
summary.bare_port_warehouse_files,
vec!["Port.bty".to_string(), "Warehouse.bca".to_string()]
);
}
}