feat: assemble the naomi.zip demul reads

Demul 0.7 reads 44 NAOMI BIOS rows from naomi.zip, the HOTD2, F355 and Airline Pilots BIOS among them. No copy held them all. The set is built with build_torrentzip from members already in the collection, each checked against the crc32 and sha1 of demul.exe's table, and kept in bios/Other/demul/. Batocera, KNULLI and RetroBat place it at bios/naomi.zip, the file MAME cores there also open, so it carries the nine MAME members the Demul table does not list.

The resolver left another emulator's own copy out of the member and superset ranking, so Flycast and the MAME cores keep the copies they were served.
This commit is contained in:
Abdessamad Derraz committed 2026-11-05 23:37:06 +01:00
1 parent 7cc732bd7f
commit 755259517f
7 files changed
+142 -35

No files matched your search

Binary file not shown.
+23 -3
View File
@@ -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"
],
+19 -9
View File
@@ -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"
]
}
]
},
{
+7 -7
View File
@@ -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"
],
+24 -14
View File
@@ -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"
]
}
]
},
{
+14 -2
View File
@@ -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/<emulator>/) 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"):
+55
View File
@@ -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."""