feat: resolve each platform to its own amiberry

A platform that builds Amiberry itself runs that revision under the name,
on every board it builds: RetroPie 5.7.2 and 6.3.3, Recalbox 8.2.2,
Batocera 7.1.1-332, KNULLI 7.1.1. The 8.5.0 profile is the buildbot
libretro core. It now names the ROM table's own file names and hashes
the keyboard MCU images.
This commit is contained in:
Abdessamad Derraz committed 2026-11-09 21:55:42 +01:00
1 parent ebc658f3f0
commit 78e6134c75
3 files changed
+120 -51

No files matched your search

+31 -1
View File
@@ -1,5 +1,5 @@
emulator: Amiberry
type: standalone + libretro
type: libretro
core_classification: official_port
source: "https://github.com/BlitterStudio/amiberry"
upstream: "https://github.com/tonioni/WinUAE"
@@ -1551,10 +1551,12 @@ files:
crc32: "f53a0fca"
sha1: "efe17ca588c44a7f0f8c62be20f23278cfe06727"
validation: [sha1]
aliases: ["blizzard_scsi_kit_iv.rom"]
found_by: content
note: >
Blizzard SCSI Kit IV, ROM id 94. SCSI module ROM of the Blizzard 1230-IV
and 1260 board, read when the module is enabled.
The table names blizzard_scsi_kit_iv.rom as its file name.
source_ref: "source src/rommgr.cpp:543-544, source src/expansion.cpp:5587-5592, source src/cpuboard.cpp:2882, source src/cpuboard.cpp:2645-2660"
- name: "warp-engine.rom"
@@ -2229,6 +2231,18 @@ files:
so the table SHA-1 is not the hash of the file.
source_ref: "source src/rommgr.cpp:860-861, source src/expansion.cpp:6856-6858, source src/cpuboard.cpp:2927-2983, source src/rommgr.cpp:3152-3184, source src/cfgfile.cpp:10132-10142, source src/rommgr.cpp:1797-1810"
- name: "menu500.aca"
path: "roms/menu500.aca"
standalone_path: "menu500.aca"
system: commodore-amiga
required: false
min_size: 524288
found_by: name
note: >
ACA 500, ROM id 137. The table gives it no hash: the ROM scan names it by
its file name, menu500.aca, and the ACA 500 board opens it by that name.
source_ref: "source src/rommgr.cpp:875-876, source src/rommgr.cpp:1656-1665, source src/rommgr.cpp:3241-3244, source src/cpuboard.cpp:2937-2942"
- name: "blizzardppc_040.rom"
path: "roms/blizzardppc_040.rom"
standalone_path: "blizzardppc_040.rom"
@@ -2671,10 +2685,12 @@ files:
crc32: "00447765"
sha1: "2644e7cbba5cc54e880b4b4249fd1dd12eb4a3bc"
validation: [sha1]
aliases: ["fastlanez3.rom"]
found_by: content
note: >
Fastlane, ROM id 102. Boot ROM of the Fastlane controller. Without it the
controller has no boot ROM.
The table names fastlanez3.rom as its file name.
source_ref: "source src/rommgr.cpp:545-546, source src/expansion.cpp:5993-5999, source src/ncr9x_scsi.cpp:2003"
- name: "oktagon-2008.rom"
@@ -2686,10 +2702,12 @@ files:
crc32: "bb0d2f6a"
sha1: "56c441fa37d193393081b2e8ceae823bc7e97e49"
validation: [sha1]
aliases: ["oktagon2008.rom"]
found_by: content
note: >
Oktagon 2008, ROM id 103. Boot ROM of the Oktagon 2008 controller. Without
it the controller has no boot ROM.
The table names oktagon2008.rom as its file name.
source_ref: "source src/rommgr.cpp:547-548, source src/expansion.cpp:5609-5614, source src/ncr9x_scsi.cpp:2093"
- name: "dkb-rapidfire-spitfire-v1.31.rom"
@@ -4259,6 +4277,10 @@ files:
system: commodore-amiga
required: false
size: 2048
crc32: "4a3fc332"
sha1: "83b21d0c8b93fc9b9b3b287fde4ec8f3badac5a2"
validation: [sha1]
found_by: content
note: "6500-1/6570-036 keyboard MCU ROM for A500/A600/A1000 models"
source_ref: "source src/kbmcu/keyboard_mcu_6500_1.cpp:385-408, source src/rommgr.cpp:137-138"
@@ -4267,6 +4289,10 @@ files:
system: commodore-amiga
required: false
size: 8192
crc32: "2a77eec4"
sha1: "301ec6a69404457d912c89e3fc54095eda9f0e93"
validation: [sha1]
found_by: content
note: "68HC05 keyboard MCU ROM for A1200"
source_ref: "source src/kbmcu/keyboard_mcu_6805.cpp:544-567, source src/rommgr.cpp:139-140"
@@ -4275,6 +4301,10 @@ files:
system: commodore-amiga
required: false
size: 2048
crc32: "c23d280e"
sha1: "b5c4b8c38b9e4a864abc871d048bd1c3a02c8432"
validation: [sha1]
found_by: content
note: "D8039HLC keyboard MCU ROM for A2000"
source_ref: "source src/kbmcu/keyboard_mcu_d8039hlc.cpp:250-273, source src/rommgr.cpp:141-142"
+34 -24
View File
@@ -3180,18 +3180,30 @@ def resolve_platform_cores(
"""
cores_config = config.get("cores")
platform_key = str(config.get("_key") or "")
# The names a build the platform compiles itself answers to: there the
# name means that build, and a standalone + libretro profile of another
# revision under the same name is not what the platform runs (ROCKNIX's
# amiberry is 5.6.5). A libretro-only profile of the name stays: RetroPie
# builds minivmac37 standalone beside the minivmac core.
own_names = {
n
for name, p in profiles.items()
# The builds the platform compiles itself, by the names they answer to.
# Such a name means that build there, and another revision of the same
# project is not what the platform runs: ROCKNIX and RetroPie build only
# standalone Amiberry, Recalbox its standalone and libretro Amiberry 8.2.2.
# Only a platform listing the name both ways (dual_mode_cores) beside a
# standalone-only build of its own runs the buildbot core too (RetroBat).
# A core of another project under the name stays: RetroPie builds
# minivmac37 standalone beside the minivmac core.
own_builds = {
name: p for name, p in profiles.items()
if platform_key and platform_key in (p.get("built_by") or [])
for n in (name, *(str(c) for c in p.get("cores") or []))
}
def replaced_by_own(name: str, p: dict, libretro_only: bool = False) -> bool:
if platform_key in (p.get("built_by") or []):
return False
names = {name, *(str(c) for c in p.get("cores") or [])}
return any(
(not libretro_only or "libretro" in str(q.get("type", "")))
and q.get("source") == p.get("source")
and names & {key, *(str(c) for c in q.get("cores") or [])}
for key, q in own_builds.items()
)
def every_libretro() -> set[str]:
# A build only some platforms compile (built_by) is absent from the
# buildbot RetroArch installs from, so it is reached by name, or as one
@@ -3205,11 +3217,7 @@ def resolve_platform_cores(
not p.get("built_by")
or (platform_key and platform_key in p["built_by"])
)
and (
platform_key in (p.get("built_by") or [])
or "standalone" not in p.get("type", "")
or not own_names & {name, *(str(c) for c in p.get("cores") or [])}
)
and not replaced_by_own(name, p)
}
if cores_config == "all_libretro":
@@ -3224,21 +3232,22 @@ def resolve_platform_cores(
index = upstream_profile_index(profiles)
platform = str(config.get("_key") or "")
def designated(name: str) -> str | None:
def designated(name: str) -> set[str]:
# A build the platform compiles itself (built_by) is what its
# name means there: Recalbox's uae4all is Chips-fr/uae4all-rpi,
# RetroPie's the standalone uae4all2 under the same name.
own = sorted(
# RetroPie's the standalone uae4all2 under the same name. A
# platform building one name in several revisions, one per board,
# runs all of them: RetroPie's amiberry is 5.7.2 and 6.3.3 on x86.
own = {
p for p in index.get(name, ())
if platform and platform in (profiles[p].get("built_by") or [])
)
return own[0] if own else preferred_profile(index, name)
}
if own:
return own
preferred = preferred_profile(index, name)
return {preferred} if preferred is not None else set()
result = {
profile
for c in core_set
if (profile := designated(c)) is not None
}
result = {profile for c in core_set for profile in designated(c)}
# A name the platform launches both ways designates its libretro
# build too. Batocera, Recalbox and RetroBat run pcsx2 standalone and
# as LRPS2, two profiles: the key match kept the standalone one and
@@ -3249,6 +3258,7 @@ def resolve_platform_cores(
for profile in index.get(str(c), set())
if "libretro" in str(profiles[profile].get("type", ""))
and not profiles[profile].get("built_by")
and not replaced_by_own(profile, profiles[profile], libretro_only=True)
}
# Support "all_libretro" as a list element: combines all libretro
# profiles with explicitly listed standalone cores (e.g. RetroDECK
+55 -26
View File
@@ -621,44 +621,59 @@ class XroarOnEachPlatform(unittest.TestCase):
class AmiberryOnEachPlatform(unittest.TestCase):
"""The standalone Amiberry scans rom_path for its ROMs. Batocera, KNULLI
and RetroPie set rom_path to bios/amiga."""
"""The standalone Amiberry scans rom_path for its ROMs. Batocera and KNULLI
set it to bios/amiga, Recalbox to bios/amiga/bios."""
BOUND = ("batocera", "knulli", "retropie")
BOUND = {
"batocera": ("amiberry-7.1.1-332", "amiga"),
"knulli": ("amiberry-7.1.1", "amiga"),
"recalbox": ("amiberry-8.2.2", "amiga/bios"),
}
@classmethod
def setUpClass(cls) -> None:
cls.profile = load_emulator_profiles(str(REPO_ROOT / "emulators"))["amiberry"]
cls.profiles = load_emulator_profiles(str(REPO_ROOT / "emulators"))
def test_rom_path_is_bios_amiga(self) -> None:
for platform in self.BOUND:
def test_rom_path_is_the_platform_folder(self) -> None:
for platform, (name, folder) in self.BOUND.items():
with self.subTest(platform=platform):
cfg = load_platform_config(platform, str(REPO_ROOT / "platforms"))
self.assertEqual(
standalone_bindings_for("amiberry", self.profile, standalone_set(cfg)),
{"": ("", "amiga")},
standalone_bindings_for(name, self.profiles[name], standalone_set(cfg)),
{"": ("", folder)},
)
def test_every_rom_path_entry_lands_in_bios_amiga(self) -> None:
for platform in self.BOUND:
def test_every_rom_path_entry_lands_in_that_folder(self) -> None:
for platform, (name, folder) in self.BOUND.items():
profile = self.profiles[name]
cfg = load_platform_config(platform, str(REPO_ROOT / "platforms"))
(view, _standalone), = platform_layouts(
"amiberry", self.profile, standalone_set(cfg)
)
for own, entry in zip(self.profile["files"], view["files"]):
layout = own.get("standalone_path")
if not layout:
views = platform_layouts(name, profile, standalone_set(cfg))
((view, _standalone),) = [(v, s) for v, s in views if s]
for own, entry in zip(profile["files"], view["files"]):
if own.get("mode") == "libretro":
continue
layout = own.get("standalone_path") or own.get("path") or own["name"]
with self.subTest(platform=platform, name=entry["name"]):
self.assertEqual(entry["standalone_path"], f"amiga/{layout}")
self.assertNotIn("/", layout.removeprefix("mt32-roms/"))
self.assertNotIn("unplaced", entry)
self.assertEqual(entry["standalone_path"], f"{folder}/{layout}")
class AmiberryBuildEachPlatformShips(unittest.TestCase):
"""ROCKNIX, AmberELEC and EmuELEC compile older Amiberry revisions: on each
the name amiberry is that build, never the 8.5.0 profile."""
"""Each platform resolves the name amiberry to the build its own package
compiles. RetroBat keeps the 8.5.0 libretro core beside its standalone."""
OWN = {"rocknix": "amiberry-5.6.5", "amberelec": "amiberry-7.1.1", "emuelec": "amiberry-8.0.0"}
OWN = {
"rocknix": {"amiberry-5.6.5"},
"amberelec": {"amiberry-7.1.1"},
"emuelec": {"amiberry-8.0.0"},
"batocera": {"amiberry-7.1.1-332"},
"knulli": {"amiberry-7.1.1"},
"recalbox": {"amiberry-8.2.2"},
"retrobat": {"amiberry", "amiberry-8.3.0"},
"retropie": {"amiberry-5.7.2", "amiberry-6.3.3"},
"emudeck": {"amiberry"},
"retrodeck": {"amiberry"},
}
@classmethod
def setUpClass(cls) -> None:
@@ -673,12 +688,26 @@ class AmiberryBuildEachPlatformShips(unittest.TestCase):
def test_each_platform_resolves_its_own_build(self) -> None:
for platform, own in self.OWN.items():
with self.subTest(platform=platform):
self.assertEqual(self.resolved(platform), {own})
self.assertEqual(self.resolved(platform), own)
def test_the_others_keep_the_main_profile(self) -> None:
for platform in ("batocera", "knulli", "retrobat", "retropie", "recalbox"):
with self.subTest(platform=platform):
self.assertEqual(self.resolved(platform), {"amiberry"})
def test_retropie_reads_kickstarts_below_its_start_folder(self) -> None:
# amiberry 5.7 and 6.3 read <start path>/kickstarts, which RetroPie
# links to bios/amiga. Its whdboot and roms folders stay below it.
cfg = load_platform_config("retropie", str(REPO_ROOT / "platforms"))
for name in ("amiberry-5.7.2", "amiberry-6.3.3"):
profile = self.profiles[name]
self.assertEqual(
standalone_bindings_for(name, profile, standalone_set(cfg)),
{"kickstarts": ("", "amiga")},
)
((view, _standalone),) = platform_layouts(name, profile, standalone_set(cfg))
for entry in view["files"]:
with self.subTest(name=name, file=entry["name"]):
if entry["path"].startswith("kickstarts/"):
self.assertEqual(entry["standalone_path"],
"amiga/" + entry["path"][len("kickstarts/"):])
else:
self.assertEqual(entry.get("unplaced"), "outside_bios_folder")
def test_rocknix_reads_kickstarts_through_the_copied_link(self) -> None:
# amiberry 5.6.5 reads <cwd>/kickstarts. ROCKNIX runs it in