From cfafc213c05fef3f13836c7627411b2f077dec51 Mon Sep 17 00:00:00 2001 From: chazmm Date: Wed, 9 Sep 2026 01:06:22 +0100 Subject: [PATCH] Resolve scoring and HUD font records by native VLT identity --- crates/skate-data/src/scoring.rs | 40 ++++++++++++++++++++++++++++---- tools/prepare_hud.py | 5 ++-- tools/test_hud_font_identity.py | 37 +++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 tools/test_hud_font_identity.py diff --git a/crates/skate-data/src/scoring.rs b/crates/skate-data/src/scoring.rs index e4520b96..5a3b7795 100644 --- a/crates/skate-data/src/scoring.rs +++ b/crates/skate-data/src/scoring.rs @@ -10,6 +10,16 @@ use skate_core::{ const SCORABLE: &str = "Hash_6918469984A8C596"; const TUNING: &str = "Hash_349215E2E817703C"; +fn scorable_keys(data: &Collections) -> std::collections::BTreeSet { + // Setup's name dictionary may resolve the class as `scoring_trick`. + // Presence checks must use the same native identities as Collections::field. + data.entries() + .iter() + .filter(|row| crate::attrib_hash::numeric_name(&row.class_name) == SCORABLE) + .map(|row| crate::attrib_hash::numeric_name(&row.key)) + .collect() +} + #[derive(Clone, Debug)] pub struct Definition { pub metadata: Scorable, @@ -53,14 +63,11 @@ impl ScoringData { } pub fn load(data: &Collections) -> Result { let mut definitions = Vec::new(); + let available = scorable_keys(data); for (id, &(identifier, class, score_type)) in IDENTIFIERS.iter().enumerate() { // The executable enum includes unused entries without VLT records. // Only actual records are eligible for recognition. - if !data.entries().iter().any(|r| { - r.class_name == SCORABLE - && (r.key == identifier - || r.key == crate::attrib_hash::numeric_name(identifier)) - }) { + if !available.contains(&crate::attrib_hash::numeric_name(identifier)) { continue; } let words = |name| data.words::<1>(SCORABLE, identifier, name).map(|v| v[0]); @@ -132,6 +139,29 @@ impl ScoringData { } } +#[cfg(test)] +mod identity_tests { + use super::*; + + #[test] + fn scoring_presence_accepts_readable_and_numeric_vault_names() { + let key = IDENTIFIERS[0].0; + let numeric_key = crate::attrib_hash::numeric_name(key); + for class in ["scoring_trick", SCORABLE, "0x6918469984a8c596"] { + for name in [key, numeric_key.as_str()] { + let data: Collections = serde_json::from_value(serde_json::json!({ + "version": 1, + "collections": [{"class": class, "key": name, "parent": "", + "source": "fixture", "sha256": "", "fields": {}}, + {"class": "unrelated", "key": "other", "parent": "", + "source": "fixture", "sha256": "", "fields": {}}] + })).unwrap(); + assert_eq!(scorable_keys(&data), [numeric_key.clone()].into()); + } + } + } +} + #[derive(Clone, Debug)] pub struct CollectorTuning { scalars: std::collections::BTreeMap, diff --git a/tools/prepare_hud.py b/tools/prepare_hud.py index f81b6a08..a1bbccee 100644 --- a/tools/prepare_hud.py +++ b/tools/prepare_hud.py @@ -24,11 +24,12 @@ def font_mapping(collections: Path, cache: AssetCache) -> dict: data = json.loads(collections.read_text(encoding='utf-8')) result = {} for row in data['collections']: - if row['class'] != 'Hash_FECFBCAF356518C4': + class_id = int(row['class'][5:], 16) if row['class'].startswith('Hash_') else hash64(row['class']) + if class_id != 0xFECFBCAF356518C4: continue def field(name): return row['fields'].get(name, row['fields'].get(f'Hash_{hash64(name):016X}')) - apt_name = row['fields']['Hash_340832CCFD9FDEB4']['data'] + apt_name = field('AptName')['data'] filename = field('FileName')['data'] matches = [r for r in cache.manifest['bundles'] if 'font' in r and Path(r['name']).name == filename] diff --git a/tools/test_hud_font_identity.py b/tools/test_hud_font_identity.py new file mode 100644 index 00000000..5d98dd84 --- /dev/null +++ b/tools/test_hud_font_identity.py @@ -0,0 +1,37 @@ +import json +from pathlib import Path +import tempfile +import unittest + +from prepare_hud import font_mapping +from asset_pipeline.vlt import hash64 + + +class Cache: + manifest = {'bundles': [{'name': 'fonts/original', 'font': {'family': 'Original family'}}]} + + def add_font_alias(self, name, target, provenance): + self.alias = (name, target) + + +class FontIdentityTests(unittest.TestCase): + def test_readable_and_numeric_exports_resolve_the_same_original_font(self): + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / 'collections.json' + for named_class in (True, False): + for named_fields in (True, False): + fields = {'AptName': {'data': 'Original APT name'}, 'FileName': {'data': 'original'}} + fields.update({key: {'data': '3F800000'} for key in ('ScaleX', 'ScaleY', 'OffsetX', 'OffsetY')}) + if not named_fields: + fields = {f'Hash_{hash64(key):016X}': value for key, value in fields.items()} + path.write_text(json.dumps({'collections': [{ + 'class': 'fe_fonts' if named_class else 'Hash_FECFBCAF356518C4', + 'key': 'original', 'sha256': '', 'fields': fields}]})) + cache = Cache() + result = font_mapping(path, cache) + self.assertEqual(cache.alias, ('Original APT name', 'Original family')) + self.assertEqual(result['Original APT name']['native_layout']['ScaleX'], 1.0) + + +if __name__ == '__main__': + unittest.main()