Narrow tier2 stock selector to machine shop outlier

This commit is contained in:
Jan Petykiewicz 2026-04-19 15:38:14 -07:00
commit cf6d36b8e7
4 changed files with 176 additions and 5 deletions

View file

@ -97,6 +97,7 @@ pub struct BuildingTypeRecoveredTableSummary {
pub nonzero_bty_header_name_0x5e_summaries: Vec<BuildingTypeBtyHeaderNameSummary>,
pub nonzero_bty_header_name_0x7c_summaries: Vec<BuildingTypeBtyHeaderNameSummary>,
pub bty_header_name_0x5e_dword_summaries: Vec<BuildingTypeBtyHeaderNameDwordSummary>,
pub bty_name_0x5e_bca_selector_summaries: Vec<BuildingTypeBtyNameBcaSelectorSummary>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -125,6 +126,18 @@ pub struct BuildingTypeBtyHeaderNameDwordSummary {
pub sample_file_names: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BuildingTypeBtyNameBcaSelectorSummary {
pub header_offset_hex: String,
pub header_value: String,
pub dword_0xbb: u32,
pub dword_0xbb_hex: String,
pub byte_0xba_hex: String,
pub byte_0xbb_hex: String,
pub file_count: usize,
pub sample_file_names: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BuildingTypeSourceReport {
pub directory_path: String,
@ -496,6 +509,8 @@ fn summarize_recovered_table_families(
summarize_nonzero_bty_header_name_lane(files, 0x7c, |probe| &probe.name_0x7c);
let bty_header_name_0x5e_dword_summaries =
summarize_bty_header_name_lane_by_dword(files, 0x5e, |probe| &probe.name_0x5e);
let bty_name_0x5e_bca_selector_summaries =
summarize_bty_name_0x5e_bca_selector_patterns(entries, files);
BuildingTypeRecoveredTableSummary {
recovered_style_themes: RECOVERED_STYLE_THEMES
@ -514,6 +529,7 @@ fn summarize_recovered_table_families(
nonzero_bty_header_name_0x5e_summaries,
nonzero_bty_header_name_0x7c_summaries,
bty_header_name_0x5e_dword_summaries,
bty_name_0x5e_bca_selector_summaries,
}
}
@ -609,6 +625,81 @@ fn summarize_bty_header_name_lane_by_dword(
summaries
}
fn summarize_bty_name_0x5e_bca_selector_patterns(
entries: &[BuildingTypeSourceEntry],
files: &[BuildingTypeSourceFile],
) -> Vec<BuildingTypeBtyNameBcaSelectorSummary> {
let file_by_name = files
.iter()
.map(|file| (file.file_name.as_str(), file))
.collect::<BTreeMap<_, _>>();
let mut groups = BTreeMap::<(String, u32, String, String), Vec<String>>::new();
for entry in entries {
let bty_file = entry
.file_names
.iter()
.filter_map(|name| file_by_name.get(name.as_str()))
.find(|file| matches!(file.source_kind, BuildingTypeSourceKind::Bty));
let bca_file = entry
.file_names
.iter()
.filter_map(|name| file_by_name.get(name.as_str()))
.find(|file| matches!(file.source_kind, BuildingTypeSourceKind::Bca));
let (Some(bty_file), Some(bca_file)) = (bty_file, bca_file) else {
continue;
};
let (Some(bty_probe), Some(bca_probe)) =
(&bty_file.bty_header_probe, &bca_file.bca_selector_probe)
else {
continue;
};
let header_value = bty_probe.name_0x5e.trim();
if header_value.is_empty() {
continue;
}
groups
.entry((
header_value.to_string(),
bty_probe.dword_0xbb,
bca_probe.byte_0xba_hex.clone(),
bca_probe.byte_0xbb_hex.clone(),
))
.or_default()
.push(bty_file.file_name.clone());
}
let mut summaries = groups
.into_iter()
.map(
|((header_value, dword_0xbb, byte_0xba_hex, byte_0xbb_hex), mut file_names)| {
file_names.sort();
file_names.dedup();
BuildingTypeBtyNameBcaSelectorSummary {
header_offset_hex: "0x5e".to_string(),
header_value,
dword_0xbb,
dword_0xbb_hex: format!("0x{dword_0xbb:08x}"),
byte_0xba_hex,
byte_0xbb_hex,
file_count: file_names.len(),
sample_file_names: file_names.into_iter().take(24).collect(),
}
},
)
.collect::<Vec<_>>();
summaries.sort_by(|left, right| {
right
.file_count
.cmp(&left.file_count)
.then_with(|| left.dword_0xbb.cmp(&right.dword_0xbb))
.then_with(|| left.header_value.cmp(&right.header_value))
.then_with(|| left.byte_0xba_hex.cmp(&right.byte_0xba_hex))
.then_with(|| left.byte_0xbb_hex.cmp(&right.byte_0xbb_hex))
});
summaries
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
struct BuildingBindingArtifact {
bindings: Vec<BuildingBindingRow>,
@ -697,6 +788,12 @@ mod tests {
source_kinds: vec![BuildingTypeSourceKind::Bty],
file_names: vec!["ServiceTower.bty".to_string()],
},
BuildingTypeSourceEntry {
canonical_stem: canonicalize_building_stem("Port"),
raw_stems: vec!["Port".to_string()],
source_kinds: vec![BuildingTypeSourceKind::Bca, BuildingTypeSourceKind::Bty],
file_names: vec!["Port.bca".to_string(), "Port.bty".to_string()],
},
];
let files = vec![
BuildingTypeSourceFile {
@ -726,12 +823,21 @@ mod tests {
}),
},
BuildingTypeSourceFile {
file_name: "Warehouse.bca".to_string(),
raw_stem: "Warehouse".to_string(),
canonical_stem: canonicalize_building_stem("Warehouse"),
file_name: "Port.bca".to_string(),
raw_stem: "Port".to_string(),
canonical_stem: canonicalize_building_stem("Port"),
source_kind: BuildingTypeSourceKind::Bca,
byte_len: None,
bca_selector_probe: None,
bca_selector_probe: Some(BuildingTypeBcaSelectorProbe {
byte_0xb8: 0x00,
byte_0xb8_hex: "0x00".to_string(),
byte_0xb9: 0x00,
byte_0xb9_hex: "0x00".to_string(),
byte_0xba: 0x00,
byte_0xba_hex: "0x00".to_string(),
byte_0xbb: 0x00,
byte_0xbb_hex: "0x00".to_string(),
}),
bty_header_probe: None,
},
];
@ -753,7 +859,7 @@ mod tests {
);
assert_eq!(
summary.bare_port_warehouse_files,
vec!["Port.bty".to_string(), "Warehouse.bca".to_string()]
vec!["Port.bca".to_string(), "Port.bty".to_string()]
);
assert_eq!(summary.nonzero_bty_header_dword_summaries.len(), 1);
assert_eq!(
@ -802,5 +908,18 @@ mod tests {
sample_file_names: vec!["Port.bty".to_string()],
}]
);
assert_eq!(
summary.bty_name_0x5e_bca_selector_summaries,
vec![BuildingTypeBtyNameBcaSelectorSummary {
header_offset_hex: "0x5e".to_string(),
header_value: "TextileMill".to_string(),
dword_0xbb: 0x01f4,
dword_0xbb_hex: "0x000001f4".to_string(),
byte_0xba_hex: "0x00".to_string(),
byte_0xbb_hex: "0x00".to_string(),
file_count: 1,
sample_file_names: vec!["Port.bty".to_string()],
}]
);
}
}