diff --git a/bios/Other/demul/naomi.zip b/bios/Other/demul/naomi.zip new file mode 100644 index 000000000..6e33c44b9 Binary files /dev/null and b/bios/Other/demul/naomi.zip differ diff --git a/database.json b/database.json index 6760bcf34..6dc94ad2e 100644 --- a/database.json +++ b/database.json @@ -1,8 +1,8 @@ { "schema_version": 1, - "generated_at": "2026-11-05T18:33:47Z", - "total_files": 19944, - "total_size": 16892646538, + "generated_at": "2026-11-05T22:35:40Z", + "total_files": 19945, + "total_size": 16909338231, "bios_dir": "bios", "files": { "738c7f5fd0c8b57ee2e87ae7a97bf8e21a821d07": { @@ -140307,6 +140307,16 @@ "crc32": "6893a2d1", "adler32": "fd861022" }, + "c620aafa730c597bddb6dcc7ed32673c96783c6d": { + "path": "bios/Other/demul/naomi.zip", + "name": "naomi.zip", + "size": 16691693, + "sha1": "c620aafa730c597bddb6dcc7ed32673c96783c6d", + "md5": "246fb8a02cf930a8d75df625ad5a8abc", + "sha256": "07398d8d153e0e77ff986cb87ce0b5fd5103b2726d8ec8e857fb695de71e0858", + "crc32": "1749ebfb", + "adler32": "c6e4322c" + }, "f6a0cbfda6838065065e830ff40f322e6a3b6b00": { "path": "bios/Other/dolphin-mmjr/Load/wiitdb.txt", "name": "wiitdb.txt", @@ -218495,6 +218505,7 @@ "afacfb61877401f1611998d2b590dffd": "a13cf9c5cdfe8605d82150b7573652f419b30197", "3ab3f9386a280de051e82c0f147385ce": "f6b64d7b039a90af956c2c0873b30f6639a59878", "2c72c2c8020be91779cf30e6965d6bf8": "a9cd6c37e1da95584eab7f4227da2c98abdb7321", + "246fb8a02cf930a8d75df625ad5a8abc": "c620aafa730c597bddb6dcc7ed32673c96783c6d", "d677421733ba64f4b493c98667d6ab78": "f6a0cbfda6838065065e830ff40f322e6a3b6b00", "a1d3c0e5a40f163f4fb00a5e0774c63c": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b", "9bef639b7426ef6948602a12b33522a8": "483a17ff25b68a8f18cae691da99f16beefbe370", @@ -225362,6 +225373,7 @@ "788aee0f30ee80ea54dcd705afe93944accafc31", "055e72e6866662ae3ae97575f7bb7882c77869db", "a39d7d17e52c26435ed1745503c7d6a68b36be1d", + "c620aafa730c597bddb6dcc7ed32673c96783c6d", "43f0018c32dd6d166c3b6d8ed2ffa672f398fa3a", "e352ae6943f7bead98cf73c0340dc07c1ff90135", "d7ef86bd03de7c1d0e2b0762e04b6f8f8d26dbdb" @@ -296456,6 +296468,7 @@ "a4fcab0b": "a13cf9c5cdfe8605d82150b7573652f419b30197", "dcbd4121": "f6b64d7b039a90af956c2c0873b30f6639a59878", "6893a2d1": "a9cd6c37e1da95584eab7f4227da2c98abdb7321", + "1749ebfb": "c620aafa730c597bddb6dcc7ed32673c96783c6d", "c1f446c6": "f6a0cbfda6838065065e830ff40f322e6a3b6b00", "9ac7f0c7": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b", "6fe1f772": "483a17ff25b68a8f18cae691da99f16beefbe370", @@ -316401,6 +316414,7 @@ "82e02a1c48ef74e449b75081d3d118bd935b81d52634dd7c6a8c95686fd83fd1": "a13cf9c5cdfe8605d82150b7573652f419b30197", "75600aba867acbdfdb85fcd142b524da769bdc611b855a760aeb0c6e2eaae17a": "f6b64d7b039a90af956c2c0873b30f6639a59878", "6ea2c350626d85a22410ad6ad8700388e28166ab8c5c2e88d83d7a696fceb335": "a9cd6c37e1da95584eab7f4227da2c98abdb7321", + "07398d8d153e0e77ff986cb87ce0b5fd5103b2726d8ec8e857fb695de71e0858": "c620aafa730c597bddb6dcc7ed32673c96783c6d", "3c042d3a3bac17f6a3a989c549fe127de1ba38dfdee9ffd6c13d5379238e5c13": "f6a0cbfda6838065065e830ff40f322e6a3b6b00", "38f9f59d505bc4e2d86b0196706195f33ad72b7fe9d029cf263072cde19d044f": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b", "4ad991be2b0aa305f09b90a79fc50f57833b30b008890b5ef1336cc3d9d0bae0": "483a17ff25b68a8f18cae691da99f16beefbe370", @@ -492707,6 +492721,12 @@ "cxbx-reloaded/keys.bin": [ "a9cd6c37e1da95584eab7f4227da2c98abdb7321" ], + "Other/demul/naomi.zip": [ + "c620aafa730c597bddb6dcc7ed32673c96783c6d" + ], + "demul/naomi.zip": [ + "c620aafa730c597bddb6dcc7ed32673c96783c6d" + ], "Other/dolphin-mmjr/Load/wiitdb.txt": [ "f6a0cbfda6838065065e830ff40f322e6a3b6b00" ], diff --git a/install/batocera.json b/install/batocera.json index 77c1a96a1..9be6ad0a9 100644 --- a/install/batocera.json +++ b/install/batocera.json @@ -6,7 +6,7 @@ "platform": "batocera", "display_name": "Batocera", "version": "43.1", - "generated": "2026-11-05T18:33:47Z", + "generated": "2026-11-05T22:35:40Z", "base_destination": "bios", "detect": [ { @@ -273,9 +273,9 @@ } ], "total_files": 14723, - "total_size": 9896828642, + "total_size": 9903100740, "pack_files": 14725, - "pack_size": 9896828642, + "pack_size": 9903100740, "total_omitted": 426, "omitted_files": [ { @@ -208729,13 +208729,23 @@ }, { "dest": "naomi.zip", - "sha1": "a39d7d17e52c26435ed1745503c7d6a68b36be1d", - "sha256": "9b85d2183bc3739efa2ebf1440c73abb252c6ca8944efcff3a7204645f7e03d4", - "size": 10419595, - "repo_path": "bios/Arcade/MAME/naomi.zip", + "sha1": "c620aafa730c597bddb6dcc7ed32673c96783c6d", + "sha256": "07398d8d153e0e77ff986cb87ce0b5fd5103b2726d8ec8e857fb695de71e0858", + "size": 16691693, + "repo_path": "bios/Other/demul/naomi.zip", "cores": [ - "demul", - "mame" + "demul" + ], + "alternatives": [ + { + "sha1": "a39d7d17e52c26435ed1745503c7d6a68b36be1d", + "sha256": "9b85d2183bc3739efa2ebf1440c73abb252c6ca8944efcff3a7204645f7e03d4", + "size": 10419595, + "repo_path": "bios/Arcade/MAME/naomi.zip", + "cores": [ + "mame" + ] + } ] }, { diff --git a/install/knulli.json b/install/knulli.json index f31953c1a..029415c75 100644 --- a/install/knulli.json +++ b/install/knulli.json @@ -6,7 +6,7 @@ "platform": "knulli", "display_name": "KNULLI", "version": "20260511", - "generated": "2026-11-05T18:33:47Z", + "generated": "2026-11-05T22:35:40Z", "base_destination": "bios", "detect": [ { @@ -30,9 +30,9 @@ ], "standalone_copies": [], "total_files": 9598, - "total_size": 9721492125, + "total_size": 9727764223, "pack_files": 9600, - "pack_size": 9721492125, + "pack_size": 9727764223, "total_omitted": 425, "omitted_files": [ { @@ -141285,10 +141285,10 @@ }, { "dest": "naomi.zip", - "sha1": "a39d7d17e52c26435ed1745503c7d6a68b36be1d", - "sha256": "9b85d2183bc3739efa2ebf1440c73abb252c6ca8944efcff3a7204645f7e03d4", - "size": 10419595, - "repo_path": "bios/Arcade/MAME/naomi.zip", + "sha1": "c620aafa730c597bddb6dcc7ed32673c96783c6d", + "sha256": "07398d8d153e0e77ff986cb87ce0b5fd5103b2726d8ec8e857fb695de71e0858", + "size": 16691693, + "repo_path": "bios/Other/demul/naomi.zip", "cores": [ "demul" ], diff --git a/install/retrobat.json b/install/retrobat.json index cfb6c27c3..a89dfc5a8 100644 --- a/install/retrobat.json +++ b/install/retrobat.json @@ -6,7 +6,7 @@ "platform": "retrobat", "display_name": "RetroBat", "version": "8.3.1", - "generated": "2026-11-05T18:33:47Z", + "generated": "2026-11-05T22:35:40Z", "base_destination": "bios", "detect": [ { @@ -285,9 +285,9 @@ } ], "total_files": 5561, - "total_size": 8543619032, + "total_size": 8549891130, "pack_files": 5563, - "pack_size": 8543619032, + "pack_size": 8549891130, "total_omitted": 203, "omitted_files": [ { @@ -72884,18 +72884,28 @@ }, { "dest": "naomi.zip", - "sha1": "a39d7d17e52c26435ed1745503c7d6a68b36be1d", - "sha256": "9b85d2183bc3739efa2ebf1440c73abb252c6ca8944efcff3a7204645f7e03d4", - "size": 10419595, - "repo_path": "bios/Arcade/MAME/naomi.zip", + "sha1": "c620aafa730c597bddb6dcc7ed32673c96783c6d", + "sha256": "07398d8d153e0e77ff986cb87ce0b5fd5103b2726d8ec8e857fb695de71e0858", + "size": 16691693, + "repo_path": "bios/Other/demul/naomi.zip", "cores": [ - "demul", - "groovymame", - "mame", - "mame2010", - "mame2014", - "mame2016", - "teknoparrot" + "demul" + ], + "alternatives": [ + { + "sha1": "a39d7d17e52c26435ed1745503c7d6a68b36be1d", + "sha256": "9b85d2183bc3739efa2ebf1440c73abb252c6ca8944efcff3a7204645f7e03d4", + "size": 10419595, + "repo_path": "bios/Arcade/MAME/naomi.zip", + "cores": [ + "groovymame", + "mame", + "mame2010", + "mame2014", + "mame2016", + "teknoparrot" + ] + } ] }, { diff --git a/scripts/common.py b/scripts/common.py index c6e0bfe25..78282a97b 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -1206,8 +1206,13 @@ def _covering_archives(paths: list[str]) -> list[str]: deleting an unrelated duplicate flipped which copy a core received. """ # A variant is served only when no primary is, so the primaries are - # the copies this choice is between. - pool = [path for path in paths if "/.variants/" not in path] or paths + # the copies this choice is between. A copy in an emulator's own tree + # (bios/Other//) is that emulator's file, not a revision of + # the set: an entry no profile owns is not decided by it. + pool = [ + path for path in paths + if "/.variants/" not in path and not path.startswith("bios/Other/") + ] or [path for path in paths if "/.variants/" not in path] or paths crcs = {path: zip_member_crcs(path) for path in pool} covering = [ path for path in pool @@ -1652,6 +1657,13 @@ def resolve_local_file( if zipped_file: candidates = [p for p in candidates if ".zip" in os.path.basename(p)] members = file_entry.get("members") or contents_members(file_entry) + if members or (len(candidates) > 1 and name.casefold().endswith(".zip")): + # Another emulator's own copy takes no part in the ranking: + # Demul's naomi.zip holds more NAOMI BIOS rows than any MAME + # set and broke the superset the MAME cores were served. + candidates = [ + p for p in candidates if not _foreign_tree(p, owners) + ] or candidates if members: candidates = _holding_members(candidates, members) elif len(candidates) > 1 and name.casefold().endswith(".zip"): diff --git a/tests/test_archive_choice.py b/tests/test_archive_choice.py index 602dc14ce..cee358bae 100644 --- a/tests/test_archive_choice.py +++ b/tests/test_archive_choice.py @@ -513,6 +513,61 @@ class ALooseEntryNamingItsArchive(unittest.TestCase): self.assertEqual(report["total_files"], 1) +class AnotherEmulatorsOwnSet(unittest.TestCase): + """Demul's naomi.zip in bios/Other/demul/ holds more NAOMI BIOS rows than + any MAME set. Ranked with the others it won the member count for Flycast + and broke the superset rule for the MAME cores, which then got a smaller + copy.""" + + def setUp(self): + (REPO_ROOT / "tmp").mkdir(exist_ok=True) + root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp")) + self.addCleanup(shutil.rmtree, root, True) + previous = os.getcwd() + os.chdir(root) + self.addCleanup(os.chdir, previous) + self.roms = {f"r{n}.ic27": bytes([n]) * 16 for n in range(5)} + layout = {"bios/Arcade/naomi.zip": ["r0.ic27", "r1.ic27"], + "bios/MAME/naomi.zip": ["r0.ic27", "r1.ic27", "r2.ic27"], + "bios/Other/demul/naomi.zip": list(self.roms)} + files, by_name = {}, {} + for relative, members in layout.items(): + Path(relative).parent.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile(relative, "w") as zf: + for member in members: + zf.writestr(member, self.roms[member]) + sha1 = hashlib.sha1(Path(relative).read_bytes()).hexdigest() + files[sha1] = {"path": relative, "name": "naomi.zip", "sha1": sha1} + by_name.setdefault("naomi.zip", []).append(sha1) + self.db = {"files": files, "indexes": {"by_name": by_name, "by_md5": {}, + "by_path_suffix": {}, "by_crc32": {}}} + + def _contents(self, names: list[str]) -> list[dict]: + return [{"name": n, "crc32": format(zlib.crc32(self.roms[n]), "08x")} for n in names] + + def test_its_member_count_does_not_rank_for_another_emulator(self): + from common import resolve_local_file + + entry = {"name": "naomi.zip", "source_profile": "flycast", + "contents": self._contents(list(self.roms))} + path, _status = resolve_local_file(entry, self.db) + self.assertEqual(path, "bios/MAME/naomi.zip") + + def test_it_does_not_break_the_superset_of_an_unowned_entry(self): + from common import resolve_local_file + + path, _status = resolve_local_file({"name": "naomi.zip"}, self.db) + self.assertEqual(path, "bios/MAME/naomi.zip") + + def test_its_owner_still_gets_it(self): + from common import resolve_local_file + + entry = {"name": "naomi.zip", "source_profile": "demul", + "contents": self._contents(list(self.roms))} + path, _status = resolve_local_file(entry, self.db) + self.assertEqual(path, "bios/Other/demul/naomi.zip") + + class ArchiveMembersIndexOnce(unittest.TestCase): """with_archive_members asks once per entry of a profile: scanning the whole list for each walked 38 million entries in one verify --all."""