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."""