From 1b388e3cdc3cccab27eecc56d3a27269b0230c1d Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:29:02 +0100 Subject: [PATCH] fix: credit a core the archive it reads members in FBNeo reads sm1.sm1 inside neogeo.zip. The site resolved each member entry by its crc32, credited loose copies FBNeo never opens, and no neogeo.zip card named FBNeo. A member entry now resolves through archive_entry as the pack does, and its card names the archive. --- scripts/cross_reference.py | 22 ++++++++++++++++------ scripts/generate_site.py | 7 +++++-- tests/test_site_render.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/scripts/cross_reference.py b/scripts/cross_reference.py index 09b889989..19405123f 100644 --- a/scripts/cross_reference.py +++ b/scripts/cross_reference.py @@ -27,6 +27,7 @@ from common import ( platform_arg, ProfileSelectionError, _norm_system_id, + archive_entry, directory_members, expand_directory_entries, get_mame_clone_map, @@ -545,6 +546,20 @@ class FileConsumers: if member is not entry } + @staticmethod + def entry_target(entry: dict, files: list[dict]) -> tuple[dict, str]: + """The entry a profile entry resolves through, and its destination. + + A ROM read inside an archive is the archive: FBNeo opens sm1.sm1 in + neogeo.zip, and resolved by its crc32 it credited a loose copy FBNeo + never opens, while no neogeo.zip card named FBNeo. The archive + members the profile reads decide between same-named copies, as in + the emulator pack and verify. + """ + if entry.get("archive"): + return archive_entry(entry["archive"], files), "" + return with_archive_members(entry, files), entry.get("path") or "" + def add_emulators(self, profiles: dict[str, dict]) -> None: """Each file a profile entry resolves to, a directory's members included. @@ -573,12 +588,7 @@ class FileConsumers: for dest, sha1 in members.items() ] else: - # The archive members the profile reads decide between - # same-named copies, as in the emulator pack and verify. - found = [self.sha1_of( - with_archive_members(entry, files), entry.get("path") or "", - owner=name, - )] + found = [self.sha1_of(*self.entry_target(entry, files), owner=name)] for sha1 in found: if sha1: self.emulators.setdefault(sha1, set()).add(name) diff --git a/scripts/generate_site.py b/scripts/generate_site.py index c5206c4f0..f2a47ed66 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -8182,10 +8182,13 @@ def _render_emulator_file( if consumers and f.get("type") == "directory": details.extend(_directory_copies(f, consumers)) if consumers: - sha1 = consumers.sha1_of(f, f.get("path") or "", owner=owner) + sha1 = consumers.sha1_of( + *consumers.entry_target(f, profile.get("files") or []), owner=owner) held = consumers.path_of(sha1) if sha1 and in_repo else "" parts = held.split("/") - if len(parts) >= 3 and parts[0] == "bios": + if len(parts) >= 3 and parts[0] == "bios" and f.get("archive"): + details.append(_held_copy_line((held, f["name"]), consumers)) + elif len(parts) >= 3 and parts[0] == "bios": # Which copy answers this entry, linked to its collection card # (provenance, every hash, download): an entry declaring no hash # said "in repo" without naming the file. diff --git a/tests/test_site_render.py b/tests/test_site_render.py index 0236dc2bd..e943dffde 100644 --- a/tests/test_site_render.py +++ b/tests/test_site_render.py @@ -454,3 +454,33 @@ class OneDefinitionPerName(unittest.TestCase): self.assertNotIn(name, seen, f"{path.name}:{node.lineno} binds {name} " f"again, first at line {seen.get(name)}") seen[name] = node.lineno + + +@unittest.skipUnless((REPO_ROOT / "database.json").is_file(), "database.json is not built") +class AMemberIsReadInItsArchive(unittest.TestCase): + """FBNeo reads sm1.sm1 inside neogeo.zip. Resolved by its crc32, the + entry credited a loose sm1.sm1 FBNeo never opens, and no neogeo.zip card + named FBNeo, while the pack ships it one.""" + + def test_the_archive_is_credited_and_named_on_the_card(self): + import json + + import generate_site as gs + from common import archive_entry, resolve_local_file + from cross_reference import FileConsumers + + db = json.loads((REPO_ROOT / "database.json").read_text(encoding="utf-8")) + profile = yaml.safe_load((REPO_ROOT / "emulators" / "fbneo.yml").read_text(encoding="utf-8")) + files = profile["files"] + consumers = FileConsumers(db) + consumers.add_emulators({"fbneo": profile}) + credited = {consumers.path_of(sha1) for sha1 in consumers.emulators} + shipped, _status = resolve_local_file( + {**archive_entry("neogeo.zip", files), "source_profile": "fbneo"}, db) + self.assertIn(shipped, credited) + self.assertFalse([p for p in credited if p.endswith("/sm1.sm1")]) + sm1 = next(f for f in files if f["name"] == "sm1.sm1") + rows = "\n".join(gs._render_emulator_file(sm1, profile, consumers, files, + lambda _f: True, owner="fbneo")) + self.assertIn("Collection copy: member `sm1.sm1` of", rows) + self.assertIn(shipped, rows)