From 577b654a9d30e0899164677f4cda830b87d1fcd7 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 22:15:42 +0100 Subject: [PATCH] feat: place retrobat's amiberry 8.3.0 roms RetroBat writes rom_path and whdboot_path below its amiberry emulator folder and copies the kickstarts and rom.key it finds in bios into that ROMs folder. The copies and both folders are launcher records, and the 8.3.0 standalone entries go to the emulators tree where the build reads them. --- platforms/retrobat.yml | 184 ++++++++++++++++++++++++++ scripts/scraper/retrobat_launchers.py | 86 ++++++++++++ tests/test_launchers_retrobat.py | 73 ++++++++++ tests/test_standalone_links.py | 21 +++ 4 files changed, 364 insertions(+) diff --git a/platforms/retrobat.yml b/platforms/retrobat.yml index 850342ea6..f9653d9e3 100644 --- a/platforms/retrobat.yml +++ b/platforms/retrobat.yml @@ -3668,6 +3668,190 @@ launchers: fails_when_missing: warning, kickstart_ext_rom_file not written source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:147-154, 541-545,\ \ 674-699" +- emulator: amiberry + build: standalone + kind: passes_path + names: [] + search_dir: "/ROMs" + passes_to: "written: rom_path in amiberry.conf" + fails_when_missing: false + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:185, 309-311, 341" +- emulator: amiberry + build: standalone + kind: passes_path + names: [] + search_dir: "/WHDBoot" + passes_to: "written: whdboot_path in amiberry.conf" + fails_when_missing: false + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:185, 326, 341" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick34005.A500 + - kick34005.A500.rom + - amiga-os-130.rom + - "Kickstart v1.3 rev 34.5 (1987)(Commodore)(A500-A1000-A2000-CDTV)[!].rom" + - "Kickstart v1.3 rev 34.5 (1987)(Commodore)(A500-A1000-A2000-CDTV).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick34005.A500" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:69-74, 185, 309-311,\ + \ 532, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick33180.A500 + - kick33180.A500.rom + - amiga-os-120.rom + - "Kickstart v1.2 rev 33.180 (1986)(Commodore)(A500-A2000)[!].rom" + - "Kickstart v1.2 rev 33.180 (1986)(Commodore)(A500-A1000-A2000).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick33180.A500" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:76-81, 185, 309-311,\ + \ 532, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick37175.A500 + - kick37175.A500.rom + - amiga-os-204.rom + - "Kickstart v2.04 rev 37.175 (1991)(Commodore)(A500+)[!].rom" + - "Kickstart v2.04 rev 37.175 (1991)(Commodore)(A500+).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick37175.A500" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:83-88, 185, 309-311,\ + \ 532, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick40063.A600 + - kick40063.A600.rom + - amiga-os-310-a600.rom + - "Kickstart v3.1 rev 40.63 (1993)(Commodore)(A500-A600-A2000)[!].rom" + - "Kickstart v3.1 rev 40.63 (1993)(Commodore)(A500-A600-A2000).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick40063.A600" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:90-95, 185, 309-311,\ + \ 532, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick40068.A1200 + - kick40068.A1200.rom + - amiga-os-310-a1200.rom + - "Kickstart v3.1 rev 40.68 (1993)(Commodore)(A1200)[!].rom" + - "Kickstart v3.1 rev 40.68 (1993)(Commodore)(A1200).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick40068.A1200" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:97-111, 185, 309-311,\ + \ 532, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick40068.A4000 + - kick40068.A4000.rom + - amiga-os-310-a4000.rom + - "Kickstart v3.1 rev 40.68 (1993)(Commodore)(A4000).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick40068.A4000" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:113-127, 185, 309-311,\ + \ 532, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick40060.CD32 + - kick40060.CD32.rom + - amiga-os-310-cd32.rom + - "Kickstart v3.1 rev 40.60 (1993)(Commodore)(CD32).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick40060.CD32" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:129-134, 138-143,\ + \ 185, 309-311, 532, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick40060.CD32.ext + - kick40060.CD32.ext.rom + - amiga-os-310-cd32-ext.rom + - "CD32 Extended-ROM rev 40.60 (1993)(Commodore)(CD32).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick40060.CD32.ext" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:129-145, 185, 309-311,\ + \ 541, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick34005.A500 + - kick34005.A500.rom + - amiga-os-130.rom + - "Kickstart v1.3 rev 34.5 (1987)(Commodore)(A500-A1000-A2000-CDTV)[!].rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick34005.A500" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:147-151, 185, 309-311,\ + \ 532, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - kick34005.CDTV + - kick34005.CDTV.rom + - amiga-os-130-cdtv-ext.rom + - "CDTV Extended-ROM v1.0 (1991)(Commodore)(CDTV)[!].rom" + - "CDTV Extended-ROM v1.0 (1992)(Commodore)(CDTV).rom" + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/kick34005.CDTV" + fails_when_missing: false + depends_on_option: first name found, the target is absent, smaller or older + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:147-154, 185, 309-311,\ + \ 541, 639-667, 674-699" +- emulator: amiberry + build: standalone + kind: copies + names: + - rom.key + case_insensitive: true + search_dir: bios, bios/amiga, bios/Amiga + passes_to: "/ROMs/rom.key" + fails_when_missing: false + depends_on_option: the target is absent or its size differs + source_ref: "emulatorLauncher/Generators/Amiberry.Generator.cs:185, 309-311, 616-637,\ + \ 674-699" - emulator: arcadeduck build: standalone kind: names_searched diff --git a/scripts/scraper/retrobat_launchers.py b/scripts/scraper/retrobat_launchers.py index af329a4ef..768a1061d 100644 --- a/scripts/scraper/retrobat_launchers.py +++ b/scripts/scraper/retrobat_launchers.py @@ -747,7 +747,12 @@ def _kickstart_table( ) +AMIBERRY_KEY = "amiberry" + + def amiberry(sources: dict[str, Source]) -> list[Record]: + """Kickstarts searched in bios and copied into the folders the generator + writes to amiberry.conf, both below the amiberry emulator folder.""" src = sources[f"{GENERATORS}/Amiberry.Generator.cs"] lists, search, method_span = _kickstart_table( src, "AmigaModel", r"private string FindBios\(string\[\] candidates\)" @@ -782,6 +787,87 @@ def amiberry(sources: dict[str, Source]) -> list[Record]: fails_when_missing=f"warning, {keys[field]} not written", ) ) + folder = src.find( + r'GetEmulatorPathAndExe\(emulator, out string path, out string exe(?:, "[^"]+")+\)', + "amiberry folder", + ) + home = f'' + roms = src.find( + r'(\w+) = Path\.Combine\(path, "(\w+)"\);\s*' + r'FileTools\.TryCreateDirectory\(\1\);\s*overrides\["rom_path"\] = \1;', + "rom_path folder", + ) + whdboot = src.find( + r'overrides\["whdboot_path"\] = Path\.Combine\(path, "(\w+)"\);', "whdboot_path folder" + ) + write = src.find(r"MergeConfLines\(confFile, overrides\);", "amiberry.conf write") + head = src.lines(folder.start(), folder.end()) + written = src.lines(write.start(), write.end()) + for directory, match, key in ((roms.group(2), roms, "rom_path"), + (whdboot.group(1), whdboot, "whdboot_path")): + records.append( + _record( + "amiberry", + "standalone", + "passes_path", + [], + source_ref((src.path, [head, src.lines(match.start(), match.end()), written])), + search_dir=f"{home}/{directory}", + passes_to=f"written: {key} in amiberry.conf", + fails_when_missing=False, + ) + ) + stage = src.find(r"private string StageKickstart\(string\[\] candidates\)", "StageKickstart") + stage_body = src.block_at(stage.end()) + stage_text = src.text[stage_body[0]:stage_body[1]] + if not re.search(r"Path\.Combine\(_amiberryRomsPath, candidates\[0\]\)", stage_text) or not re.search( + r"!dst\.Exists \|\| dst\.Length != src\.Length \|\| dst\.LastWriteTimeUtc < src\.LastWriteTimeUtc", + stage_text, + ): + raise LauncherPatternMissing(src.path, "StageKickstart copy to candidates[0] when stale") + stage_span = (src.line(stage.start()), src.lines(*stage_body)[1]) + for field, names, spans in lists: + call = src.find(rf"StageKickstart\(_model\.{field}\)", f"StageKickstart({field})") + records.append( + _record( + "amiberry", + "standalone", + "copies", + names, + source_ref((src.path, [*spans, head, src.lines(roms.start(), roms.end()), + src.lines(call.start(), call.end()), + stage_span, method_span])), + case_insensitive=True, + search_dir=search, + passes_to=f"{home}/{roms.group(2)}/{names[0]}", + fails_when_missing=False, + depends_on_option="first name found, the target is absent, smaller or older", + ) + ) + key = src.find(r"private void StageRomKey\(\)", "StageRomKey") + key_body = src.block_at(key.end()) + key_text = src.text[key_body[0]:key_body[1]] + named = re.search(r'FindBios\(new\[\] \{ "([^"]+)" \}\)', key_text) + if not named or not re.search( + r"!File\.Exists\(dest\) \|\| new FileInfo\(dest\)\.Length != new FileInfo\(source\)\.Length", + key_text, + ): + raise LauncherPatternMissing(src.path, "StageRomKey copy when absent or resized") + records.append( + _record( + "amiberry", + "standalone", + "copies", + [named.group(1)], + source_ref((src.path, [head, src.lines(roms.start(), roms.end()), + (src.line(key.start()), src.lines(*key_body)[1]), method_span])), + case_insensitive=True, + search_dir=search, + passes_to=f"{home}/{roms.group(2)}/{named.group(1)}", + fails_when_missing=False, + depends_on_option="the target is absent or its size differs", + ) + ) return records diff --git a/tests/test_launchers_retrobat.py b/tests/test_launchers_retrobat.py index ca3db8e35..5d9c7bafd 100644 --- a/tests/test_launchers_retrobat.py +++ b/tests/test_launchers_retrobat.py @@ -92,6 +92,38 @@ AMIBERRY = """ var searchDirs = new List { biosPath, Path.Combine(biosPath, "amiga"), Path.Combine(biosPath, "Amiga") }; return null; } + + public override ProcessStartInfo Generate(string system, string emulator, string core, string rom) + { + if (!GetEmulatorPathAndExe(emulator, out string path, out string exe, "Amiberry.exe", "amiberry.exe")) + return null; + } + + private void SetupAmiberrySettings(string path, string system) + { + _amiberryRomsPath = Path.Combine(path, "ROMs"); + FileTools.TryCreateDirectory(_amiberryRomsPath); + overrides["rom_path"] = _amiberryRomsPath; + overrides["whdboot_path"] = Path.Combine(path, "WHDBoot"); + MergeConfLines(confFile, overrides); + } + + private void StageRomKey() + { + string source = FindBios(new[] { "rom.key" }); + string dest = Path.Combine(_amiberryRomsPath, "rom.key"); + if (!File.Exists(dest) || new FileInfo(dest).Length != new FileInfo(source).Length) + File.Copy(source, dest, true); + } + + private string StageKickstart(string[] candidates) + { + string source = FindBios(candidates); + string dest = Path.Combine(_amiberryRomsPath, candidates[0]); + if (!dst.Exists || dst.Length != src.Length || dst.LastWriteTimeUtc < src.LastWriteTimeUtc) + File.Copy(source, dest, true); + return dest; + } """ MEDNAFEN = """ @@ -353,6 +385,7 @@ class NamesInLauncherOrder(unittest.TestCase): def test_amiberry_identical_lists_are_one_record(self): records = rl.amiberry(_sources(**{f"{G}/Amiberry.Generator.cs": AMIBERRY})) + records = [r for r in records if r["kind"] == "names_searched"] self.assertEqual( [r["names"] for r in records], [ @@ -369,6 +402,46 @@ class NamesInLauncherOrder(unittest.TestCase): "copied to /kick40060.CD32.ext, kickstart_ext_rom_file", ) + def test_amiberry_folders_are_below_its_emulator_folder(self): + records = rl.amiberry(_sources(**{f"{G}/Amiberry.Generator.cs": AMIBERRY})) + folders = [r for r in records if r["kind"] == "passes_path"] + self.assertEqual( + [(r["search_dir"], r["passes_to"], r["source_ref"]) for r in folders], + [ + ('/ROMs', "written: rom_path in amiberry.conf", + f"{G}/Amiberry.Generator.cs:31, 37-41"), + ('/WHDBoot', + "written: whdboot_path in amiberry.conf", f"{G}/Amiberry.Generator.cs:31, 40-41"), + ], + ) + + def test_amiberry_copies_each_kickstart_into_its_roms_folder(self): + records = rl.amiberry(_sources(**{f"{G}/Amiberry.Generator.cs": AMIBERRY})) + copies = [r for r in records if r["kind"] == "copies"] + self.assertEqual( + [(r["names"], r["passes_to"], r["depends_on_option"]) for r in copies], + [ + (["kick40060.CD32", "kick40060.CD32.rom"], + '/ROMs/kick40060.CD32', + "first name found, the target is absent, smaller or older"), + (["kick40060.CD32.ext", "amiga-os-310-cd32-ext.rom"], + '/ROMs/kick40060.CD32.ext', + "first name found, the target is absent, smaller or older"), + (["rom.key"], '/ROMs/rom.key', + "the target is absent or its size differs"), + ], + ) + self.assertEqual(copies[0]["search_dir"], "bios, bios/amiga, bios/Amiga") + self.assertEqual( + copies[0]["source_ref"], + f"{G}/Amiberry.Generator.cs:3-5, 8-10, 15, 23-27, 31, 37-39, 52-59", + ) + + def test_amiberry_copy_without_its_staleness_test_raises(self): + broken = AMIBERRY.replace(" || dst.LastWriteTimeUtc < src.LastWriteTimeUtc", "") + with self.assertRaises(rl.LauncherPatternMissing): + rl.amiberry(_sources(**{f"{G}/Amiberry.Generator.cs": broken})) + def test_psx_cascade_splits_searched_from_fallback(self): records = rl.psx_cascades(_psx_sources()) searched, fallback = [r for r in records if r["emulator"] == "mednafen"] diff --git a/tests/test_standalone_links.py b/tests/test_standalone_links.py index 745a4addc..2a44c45d6 100644 --- a/tests/test_standalone_links.py +++ b/tests/test_standalone_links.py @@ -658,6 +658,27 @@ class AmiberryOnEachPlatform(unittest.TestCase): self.assertEqual(entry["standalone_path"], f"{folder}/{layout}") + def test_retrobat_reads_its_folders_below_the_amiberry_emulator_folder(self) -> None: + # RetroBat writes rom_path and whdboot_path below the amiberry + # folder of its install and copies the kickstarts it finds in bios + # into that ROMs folder. + name = "amiberry-8.3.0" + profile = self.profiles[name] + cfg = load_platform_config("retrobat", str(REPO_ROOT / "platforms")) + self.assertEqual( + standalone_bindings_for(name, profile, standalone_set(cfg)), + {"": ("emulators", "amiberry/ROMs"), "WHDBoot": ("emulators", "amiberry/WHDBoot")}, + ) + ((view, _standalone),) = platform_layouts(name, profile, standalone_set(cfg)) + for own, entry in zip(profile["files"], view["files"]): + layout = own.get("standalone_path") or own.get("path") or own["name"] + folder = "amiberry" if layout.startswith("WHDBoot/") else "amiberry/ROMs" + with self.subTest(name=entry["name"]): + self.assertNotIn("unplaced", entry) + self.assertEqual(entry.get("root"), "emulators") + self.assertEqual(entry["standalone_path"], f"{folder}/{layout}") + + class AmiberryBuildEachPlatformShips(unittest.TestCase): """Each platform resolves the name amiberry to the build its own package compiles. RetroBat keeps the 8.5.0 libretro core beside its standalone."""