fix: give AdvanceMAME's sets both dir_rom folders

EmuELEC starts AdvanceMAME for arcade, mame and cps1-3 (es_systems.json).
advmame.sh sets dir_rom to /storage/roms/arcade for arcade and keeps
/storage/roms/mame for every other system (advmame.sh:26-30, 80), so an
arcade set is read in both. A binding gave each entry one place: an entry
launched both for a system a record names and for one it does not now
takes the place of each, in the pack, the manifest and verify. A system
the platform's list or machines put the entry under stands for the
entry's own ids, so XRoar's coco3.rom stays in RetroBat's bios/coco alone.

The AdvanceMAME sets are filed under arcade as well as their board.

A second-pass copy outside the BIOS tree now carries its layout path and
resolves by it, as the first pass does: EmuELEC's roms/mame/stvbios.zip
took bios/Arcade/MAME's set by that tail, roms/arcade/stvbios.zip the
Arcade/Arcade one.

Manifests: EmuELEC gains the 29 sets in roms/arcade, each the file
roms/mame holds. roms/mame/stvbios.zip changes from bios/Arcade/MAME to
bios/Arcade/Arcade/stvbios.zip, the copy the entry resolves to. Nothing
else moves.
This commit is contained in:
Abdessamad Derraz committed 2026-11-10 14:04:44 +01:00
1 parent 1b711b96f5
commit 6c41ff95ea
4 files changed
+183 -56

No files matched your search

+87 -29
View File
@@ -162,7 +162,9 @@ files:
path: "neogeo.zip"
required: true
category: bios_zip
system: snk-neogeo-mvs
systems:
- snk-neogeo-mvs
- arcade
source_ref: "src/drivers/neogeo.c:7941-7955,8555"
note: >-
Eight MVS BIOS variants selected with misc_bios (euro, euro-s1, us,
@@ -230,7 +232,9 @@ files:
path: "pgm.zip"
required: true
category: bios_zip
system: igs-pgm
systems:
- igs-pgm
- arcade
source_ref: "src/drivers/pgm.c:4415-4426, src/drivers/pgm.c:6904"
contents:
- name: pgm_p01s.rom
@@ -254,7 +258,9 @@ files:
path: "stvbios.zip"
required: true
category: bios_zip
system: sega-stv
systems:
- sega-stv
- arcade
source_ref: "src/drivers/stv.c:3251-3253,4281"
note: >-
Nine BIOS variants selected with misc_bios (japan, japana, us, japanb,
@@ -311,7 +317,9 @@ files:
path: "megaplay.zip"
required: true
category: bios_zip
system: sega-megaplay
systems:
- sega-megaplay
- arcade
source_ref: "src/drivers/megaplay.c:793-796,991"
note: >-
Two BIOS variants selected with misc_bios (ver1, ver2), ver1 by
@@ -333,7 +341,9 @@ files:
path: "megatech.zip"
required: true
category: bios_zip
system: sega-megatech
systems:
- sega-megatech
- arcade
source_ref: "src/drivers/megatech.c:456-459,868"
contents:
- name: epr12664.20
@@ -346,7 +356,9 @@ files:
path: "decocass.zip"
required: true
category: bios_zip
system: deco-cassette
systems:
- deco-cassette
- arcade
source_ref: "src/drivers/decocass.c:729-732,1215"
note: >-
Two main CPU BIOS variants selected with misc_bios (bios0, bios1),
@@ -399,7 +411,9 @@ files:
path: "playch10.zip"
required: true
category: bios_zip
system: nintendo-playch10
systems:
- nintendo-playch10
- arcade
source_ref: "src/drivers/playch10.c:1650-1653,1661"
note: >-
Two BIOS variants selected with misc_bios (dual, single), dual by
@@ -451,7 +465,9 @@ files:
path: "nss.zip"
required: true
category: bios_zip
system: nintendo-nss
systems:
- nintendo-nss
- arcade
source_ref: "src/drivers/nss.c:526-528,659"
contents:
- name: spc700.rom
@@ -480,7 +496,9 @@ files:
path: "skns.zip"
required: true
category: bios_zip
system: super-kaneko-nova
systems:
- super-kaneko-nova
- arcade
source_ref: "src/drivers/suprnova.c:1272-1279,1767"
note: >-
Five region BIOS ROMs, all loaded into the same region.
@@ -516,7 +534,9 @@ files:
path: "konamigx.zip"
required: true
category: bios_zip
system: konami-gx
systems:
- konami-gx
- arcade
source_ref: "src/drivers/konamigx.c:2180-2183,3521"
contents:
- name: 300a01.34k
@@ -529,7 +549,9 @@ files:
path: "konamigv.zip"
required: true
category: bios_zip
system: konami-gv
systems:
- konami-gv
- arcade
source_ref: "src/drivers/konamigv.c:974-978,1060"
contents:
- name: 999a01.7e
@@ -542,7 +564,9 @@ files:
path: "cvs.zip"
required: true
category: bios_zip
system: century-cvs
systems:
- century-cvs
- arcade
source_ref: "src/drivers/cvs.c:422-432,1403"
contents:
- name: 5b.bin
@@ -566,7 +590,9 @@ files:
path: "cpzn1.zip"
required: true
category: bios_zip
system: capcom-zn1
systems:
- capcom-zn1
- arcade
source_ref: "src/drivers/zn.c:3188-3190, src/drivers/zn.c:4635"
contents:
- name: coh-1000c.353
@@ -579,7 +605,9 @@ files:
path: "cpzn2.zip"
required: true
category: bios_zip
system: capcom-zn2
systems:
- capcom-zn2
- arcade
source_ref: "src/drivers/zn.c:3423-3425, src/drivers/zn.c:4653"
contents:
- name: coh-3002c.353
@@ -592,7 +620,9 @@ files:
path: "tps.zip"
required: true
category: bios_zip
system: tecmo-tps
systems:
- tecmo-tps
- arcade
source_ref: "src/drivers/zn.c:3866-3868, src/drivers/zn.c:4700"
contents:
- name: coh-1002m.353
@@ -605,7 +635,9 @@ files:
path: "taitofx1.zip"
required: true
category: bios_zip
system: taito-fx1
systems:
- taito-fx1
- arcade
source_ref: "src/drivers/zn.c:4162-4164, src/drivers/zn.c:4733"
contents:
- name: coh-1000t.353
@@ -618,7 +650,9 @@ files:
path: "psarc95.zip"
required: true
category: bios_zip
system: eighting-zn1
systems:
- eighting-zn1
- arcade
source_ref: "src/drivers/zn.c:4434-4436, src/drivers/zn.c:4760"
contents:
- name: coh-1002e.353
@@ -631,7 +665,9 @@ files:
path: "atluspsx.zip"
required: true
category: bios_zip
system: atlus-zn1
systems:
- atlus-zn1
- arcade
source_ref: "src/drivers/zn.c:4605-4607, src/drivers/zn.c:4772"
contents:
- name: coh-1001l.353
@@ -644,7 +680,9 @@ files:
path: "atpsx.zip"
required: true
category: bios_zip
system: atari-zn1
systems:
- atari-zn1
- arcade
source_ref: "src/drivers/zn.c:4520-4522, src/drivers/zn.c:4680"
contents:
- name: coh-1000w.353
@@ -657,7 +695,9 @@ files:
path: "acpsx.zip"
required: true
category: bios_zip
system: acclaim-zn1
systems:
- acclaim-zn1
- arcade
source_ref: "src/drivers/zn.c:4543-4545, src/drivers/zn.c:4689"
contents:
- name: coh-1000a.353
@@ -670,7 +710,9 @@ files:
path: "coh1002v.zip"
required: true
category: bios_zip
system: videosystem-zn1
systems:
- videosystem-zn1
- arcade
source_ref: "src/drivers/zn.c:4090-4096, src/drivers/zn.c:4722"
contents:
- name: coh-1002v.ic353
@@ -683,7 +725,9 @@ files:
path: "aleck64.zip"
required: true
category: bios_zip
system: nintendo-aleck64
systems:
- nintendo-aleck64
- arcade
source_ref: "src/drivers/aleck64.c:697-699,778"
contents:
- name: pifdata.bin
@@ -696,7 +740,9 @@ files:
path: "atarisy1.zip"
required: true
category: bios_zip
system: atari-system1
systems:
- atari-system1
- arcade
source_ref: "src/drivers/atarisy1.c:837-850, src/drivers/atarisy1.c:2299"
contents:
- name: "136032.114.j11"
@@ -720,7 +766,9 @@ files:
path: "cedmag.zip"
required: true
category: bios_zip
system: efo-cedar-magnet
systems:
- efo-cedar-magnet
- arcade
source_ref: "src/drivers/cedar_magnet.c:881-890, src/drivers/cedar_magnet.c:948"
contents:
- name: "magnet-master-vid-e03.bin"
@@ -733,7 +781,9 @@ files:
path: "ar_bios.zip"
required: true
category: bios_zip
system: arcadia-systems
systems:
- arcadia-systems
- arcade
source_ref: "src/drivers/arsystems.c:356-377,823"
note: >-
Three BIOS variants selected with misc_bios (onep300, onep211,
@@ -801,7 +851,9 @@ files:
path: "maxaflex.zip"
required: true
category: bios_zip
system: exidy-maxaflex
systems:
- exidy-maxaflex
- arcade
source_ref: "src/drivers/maxaflex.c:520-529,585"
contents:
- name: atarixl.rom
@@ -825,7 +877,9 @@ files:
path: "crysbios.zip"
required: true
category: bios_zip
system: brezzasoft-crystal
systems:
- brezzasoft-crystal
- arcade
source_ref: "src/drivers/crystal.c:924-929,1032"
contents:
- name: mx27l1000.u14
@@ -838,7 +892,9 @@ files:
path: "macsbios.zip"
required: true
category: bios_zip
system: imax-macs
systems:
- imax-macs
- arcade
source_ref: "src/drivers/macs.c:396-404,614"
contents:
- name: macsos_l.u43
@@ -857,7 +913,9 @@ files:
path: "bctvidbs.zip"
required: true
category: bios_zip
system: barcrest-mpu4
systems:
- barcrest-mpu4
- arcade
source_ref: "src/drivers/mpu4.c:2016-2019,2177"
contents:
- name: vid.p1
+61 -23
View File
@@ -3184,6 +3184,29 @@ def standalone_bindings_for(
)
def _placed_entry(entry: dict, profile: dict, placed: tuple[str, str]) -> dict:
"""A standalone entry moved to the (tree, path) a platform reads it from."""
tree, path = placed
moved = {**entry, "standalone_path": path,
"layout_path": standalone_layout_path(entry, profile)}
if entry.get("load_from"):
# The build's own root no longer applies: RetroDECK hands
# Cemu's MLC, read from its data directory, to bios/cemu.
load_from = entry["load_from"]
moved["load_from"] = {
**(load_from if isinstance(load_from, dict) else {"libretro": load_from}),
"standalone": "system_dir",
}
if tree == "install":
# Below the roms root inside the install tree (AmberELEC's
# /storage/roms): a plain dest the platform prefix skips.
return {**moved, "unprefixed": True}
# A file the launcher runs from the roms directory (ROCKNIX's
# roms/pico-8) or an emulator folder (RetroBat's emulators/pico8)
# is relative to it, and says so.
return {**moved, "root": tree} if tree else moved
def platform_layouts(
emu_name: str, profile: dict, standalone_cores: set[str]
) -> list[tuple[dict, bool]]:
@@ -3218,7 +3241,14 @@ def platform_layouts(
for system in own:
family.setdefault(system, set()).update(entry_launch_systems(f, index) - own)
def bindings_of(entry: dict) -> dict[str, tuple[str, str]]:
# The systems the launcher records name: a launch for one of them reads
# the folder its record gives, every other launch the folder the records
# naming none give.
named_systems = frozenset().union(*(
h.systems for h in _handed(standalone_cores) if h.emulator in names
)) if per_system else frozenset()
def launch_systems(entry: dict) -> frozenset[str]:
systems: frozenset[str] = frozenset()
if per_system:
own = entry_launch_systems(entry, {})
@@ -3232,12 +3262,39 @@ def platform_layouts(
systems = systems.union(*(
index.get(MACHINE_KEY + _norm_system_id(s), set()) for s in own
))
return systems
def bindings_for(systems: frozenset[str]) -> dict[str, tuple[str, str]]:
if systems not in by_systems:
by_systems[systems] = standalone_bindings_for(
emu_name, profile, standalone_cores, systems
)
return by_systems[systems]
def bindings_of(entry: dict) -> dict[str, tuple[str, str]]:
return bindings_for(launch_systems(entry))
def destinations(entry: dict) -> list[tuple[str, str] | None]:
"""Each place the entry's launches read it from, in launch order.
An entry of a system a record names is read where that record puts
it. One also launched for systems no record names is read, for those
launches, where the records naming none put it: EmuELEC starts
AdvanceMAME with dir_rom roms/arcade for arcade and roms/mame for
mame and cps1-3 (advmame.sh:26-30), and an arcade set is read in both.
"""
systems = launch_systems(entry)
found = [standalone_destination(entry, profile, bindings_for(systems))]
# A system the platform's list or machines put the entry under
# stands for the entry's own ids: XRoar's coco3 is launched as coco.
own = entry_launch_systems(entry, {})
launched = (systems - own) | (own & named_systems) if systems != own else systems
if launched & named_systems and launched - named_systems:
generic = standalone_destination(entry, profile, bindings_for(frozenset()))
if generic not in found:
found.append(generic)
return found
layout_entries = [
f for f in profile.get("files") or []
if isinstance(f, dict) and standalone_layout_path(f, profile) is not None
@@ -3255,30 +3312,11 @@ def platform_layouts(
if not isinstance(f, dict) or standalone_layout_path(f, profile) is None:
files.append(f)
continue
placed = standalone_destination(f, profile, bindings_of(f))
if placed is None or placed[0] == "outside":
places = [p for p in destinations(f) if p is not None and p[0] != "outside"]
if not places:
files.append({**f, "unplaced": OUTSIDE_BIOS_FOLDER})
continue
tree, path = placed
moved = {**f, "standalone_path": path,
"layout_path": standalone_layout_path(f, profile)}
if f.get("load_from"):
# The build's own root no longer applies: RetroDECK hands
# Cemu's MLC, read from its data directory, to bios/cemu.
load_from = f["load_from"]
moved["load_from"] = {
**(load_from if isinstance(load_from, dict) else {"libretro": load_from}),
"standalone": "system_dir",
}
if tree == "install":
# Below the roms root inside the install tree (AmberELEC's
# /storage/roms): a plain dest the platform prefix skips.
files.append({**moved, "unprefixed": True})
continue
# A file the launcher runs from the roms directory (ROCKNIX's
# roms/pico-8) or an emulator folder (RetroBat's emulators/pico8)
# is relative to it, and says so.
files.append({**moved, "root": tree} if tree else moved)
files.extend(_placed_entry(f, profile, placed) for placed in places)
standalone = {**profile, "files": files}
dual = {c.removeprefix(DUAL_MODE_PREFIX) for c in standalone_cores
if c.startswith(DUAL_MODE_PREFIX)}
+5
View File
@@ -1545,6 +1545,11 @@ def _collect_emulator_extras(
extra["system_dir"] = f["system_dir"]
if f.get("unprefixed"):
extra["unprefixed"] = True
# A copy placed outside the BIOS tree resolves by its layout
# path alone, as the first pass does: EmuELEC's
# roms/mame/stvbios.zip took bios/Arcade/MAME's set by that tail.
if f.get("layout_path"):
extra["layout_path"] = f["layout_path"]
extra["claim_order"] = (2, 0, emu_name)
if _take(extra, full_dest, seen_dests, taken):
extras.append(extra)
+30 -4
View File
@@ -912,13 +912,38 @@ class AdvanceMameOnEmuELEC(unittest.TestCase):
"""advmame.rc sets dir_rom /storage/roms/mame, /storage/roms/arcade for
the arcade system (advmame.rc:11, advmame.sh:26-30)."""
def test_the_sets_land_in_roms_mame(self) -> None:
def test_the_sets_land_in_roms_mame_and_roms_arcade(self) -> None:
# Started for arcade it reads roms/arcade, for mame and cps1-3
# roms/mame (es_systems.json, advmame.sh:26-30): an arcade set is
# read in both.
view = standalone_view("advancemame", "emuelec")
sets = [e for e in view["files"] if e.get("category") == "bios_zip"]
self.assertEqual(len(sets), 29)
self.assertEqual(len(sets), 58)
places: dict[str, set[str]] = {}
for entry in sets:
self.assertEqual(entry["standalone_path"], "mame/" + entry["name"])
self.assertTrue(entry.get("unprefixed"))
places.setdefault(entry["name"], set()).add(entry["standalone_path"])
self.assertEqual(len(places), 29)
for name, paths in places.items():
self.assertEqual(paths, {"mame/" + name, "arcade/" + name})
def test_both_copies_are_one_file(self) -> None:
# Outside the BIOS tree the folder is no proof of content: each copy
# resolves by its layout path, so both folders hold the same file.
from common import load_database
db = load_database(str(REPO_ROOT / "database.json"))
with redirect_stdout(io.StringIO()):
manifest = generate_manifest(
"emuelec", str(REPO_ROOT / "platforms"), db, str(REPO_ROOT / "bios"),
str(REPO_ROOT / "platforms" / "_registry.yml"),
emulators_dir=str(REPO_ROOT / "emulators"), offline=True,
)
by_dest = {f["dest"]: f["sha1"] for f in manifest["files"]}
sets = [d.removeprefix("arcade/") for d in by_dest if d.startswith("arcade/")]
self.assertEqual(len(sets), 29)
for name in sets:
self.assertEqual(by_dest["arcade/" + name], by_dest["mame/" + name], name)
def test_the_roms_folder_is_no_proof_of_content(self) -> None:
# bios/Arcade/MAME/konamigv.zip is MAME 0.289's set, not AdvanceMAME's.
@@ -926,7 +951,8 @@ class AdvanceMameOnEmuELEC(unittest.TestCase):
db = load_database(str(REPO_ROOT / "database.json"))
view = standalone_view("advancemame", "emuelec")
(entry,) = [e for e in view["files"] if e["name"] == "konamigv.zip"]
entry = next(e for e in view["files"]
if e["name"] == "konamigv.zip" and e["standalone_path"].startswith("mame/"))
moved, _status = resolve_local_file(entry, db, dest_hint=entry["standalone_path"])
plain = {k: v for k, v in entry.items()
if k not in ("layout_path", "standalone_path", "unprefixed")}