feat: mark files a core finds by content

DuckStation identifies its BIOS by MD5 in a folder it scans
(bios.cpp:208), so its entries' names are labels. The export wrote them
into Batocera's and ROCKNIX's lists as files to hold: a user with only
scph5501.bin read 104 missing BIOS where DuckStation boots. Labels also
lent their md5 to a name another core opens, and paired by name they
replaced System.dat's scph5000.bin hashes.

found_by: name | content, on a profile or a file and keyed by build like
load_from. The truth records which cores open each name (_named_by). A
label pairs with a platform entry by hash only, is never added, merges
with an opened name only on a shared hash, and is counted in the export
report rather than listed as missing.
This commit is contained in:
Abdessamad Derraz committed 2026-10-20 16:32:22 +02:00
1 parent eaf6d1859d
commit e5174a07bf
8 files changed
+182 -6

No files matched your search

+17 -1
View File
@@ -61,6 +61,15 @@ def checked_hashes(entry: dict | None) -> dict | None:
return {k: v for k, v in entry.items() if k not in HASH_FIELDS or k in kinds}
def is_label(entry: dict) -> bool:
"""A name only content-scanning cores give: no core opens that file.
DuckStation identifies its BIOS by MD5 in a folder it scans, so
scph1001_v20.bin names nothing a platform could check by path.
"""
return bool(entry.get("_cores")) and "_named_by" in entry and not entry["_named_by"]
def _is_placeholder(name: str) -> bool:
return "<" in name or ">" in name or "*" in name
@@ -282,6 +291,8 @@ class Report:
files_added: int = 0
hashes_corrected: list[str] = field(default_factory=list)
required_corrected: list[str] = field(default_factory=list)
# Entries only content-scanning cores name: never written as files.
labels: int = 0
@property
def corrections(self) -> int:
@@ -394,7 +405,8 @@ def pair_entries(truth_entries: list[dict], candidates: list[dict]) -> dict[int,
claimed: set[int] = set()
for test in _PAIRING_TESTS:
for t_index, truth_entry in enumerate(truth_entries):
if t_index in pairs:
# A label answers for no name or path, only for its content.
if t_index in pairs or (test is not by_hash and is_label(truth_entry)):
continue
for index, candidate in enumerate(candidates):
if index not in claimed and test(truth_entry, candidate):
@@ -549,6 +561,10 @@ def build_native_model(
)
continue
if is_label(truth_entry):
report.labels += 1
continue
# A file another core's entry already claimed is still the
# platform's file, not a new one: scph101.bin, declared by two
# PSX cores, was written into RetroDECK's manifest a second time.