From 132c69a4b2021c38953c25f2f24b337570759116 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Tue, 20 Oct 2026 01:20:45 +0200 Subject: [PATCH] fix: a ROCKNIX runner follows its build ROCKNIX registers DuckStation twice, as the libretro core under retroarch and as duckstation-sa under its own emulator. The exporter looked a standalone runner up as a core name, so standalone DuckStation's 98 additions were written for users of the libretro core, PS2 images included, and standalone ARMSX2 was refused. A libretro runner now answers with the retroarch pair, a standalone with its emulator's own. --- scripts/exporter/rocknix_exporter.py | 25 +++++++++++++++++------- tests/test_exporters.py | 29 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/scripts/exporter/rocknix_exporter.py b/scripts/exporter/rocknix_exporter.py index a5f61ad43..4483308a1 100644 --- a/scripts/exporter/rocknix_exporter.py +++ b/scripts/exporter/rocknix_exporter.py @@ -32,16 +32,27 @@ class Exporter(BatoceraExporter): def __init__(self) -> None: super().__init__() - self._emulator_of: dict[str, str] = {} + self._pairs: set[tuple[str, str]] = set() def _runner_pair(self, runner: tuple[bool, str]) -> tuple[str, str | None] | None: """ROCKNIX registers libretro cores under `retroarch` and each standalone under its own emulator (`azahar azahar-sa`), in the emulators package. The `libretro` Batocera writes names nothing - there.""" - _standalone, name = runner - emulator = self._emulator_of.get(name) - return (emulator, name) if emulator else None + there. + + The build decides which registration answers. A libretro core is the + `retroarch` pair of that core. A standalone is its own emulator's + pair: DuckStation is registered both ways, and looking the standalone + up as a core name sent its 98 files to the libretro core's users. + """ + standalone, name = runner + if not standalone: + return ("retroarch", name) if ("retroarch", name) in self._pairs else None + own = sorted( + pair for pair in self._pairs + if pair[0] == name or (pair[1] == name and pair[0] != "retroarch") + ) + return own[0] if own else None def render( self, @@ -50,8 +61,8 @@ class Exporter(BatoceraExporter): originals: dict[str, str], scraped: dict | None = None, ) -> dict[str, str]: - self._emulator_of = { - core: emulator + self._pairs = { + (emulator, core) for _system, emulator, core in _ADD_EMU_CORE.findall( originals.get(EMULATORS_MK, "") ) diff --git a/tests/test_exporters.py b/tests/test_exporters.py index a49de4cc2..e661ecf38 100644 --- a/tests/test_exporters.py +++ b/tests/test_exporters.py @@ -1695,5 +1695,34 @@ class RocknixNamesRunnersItsOwnWay(unittest.TestCase): self.assertNotIn("c.bin", text) +class RocknixRunnersFollowTheBuild(unittest.TestCase): + """ROCKNIX registers DuckStation twice: as the libretro core under + retroarch, and as duckstation-sa under its own emulator. The exporter + looked a standalone runner up as a core name, gating standalone + DuckStation's 98 files on the libretro core, and refused ARMSX2.""" + + MK = ( + "add_emu_core psx retroarch duckstation false\n" + "add_emu_core psx duckstation duckstation-sa false\n" + "add_emu_core ps2 armsx2 armsx2-sa true\n" + ) + + def test_each_build_answers_with_its_own_pair(self): + from exporter.rocknix_exporter import EMULATORS_MK, Exporter + + exporter = Exporter() + exporter._pairs = { + (emulator, core) + for _s, emulator, core in __import__("re").findall( + r"add_emu_core\s+(\S+)\s+(\S+)\s+(\S+)", self.MK + ) + } + self.assertEqual(exporter._runner_pair((False, "duckstation")), ("retroarch", "duckstation")) + self.assertEqual(exporter._runner_pair((True, "duckstation")), ("duckstation", "duckstation-sa")) + self.assertEqual(exporter._runner_pair((True, "armsx2")), ("armsx2", "armsx2-sa")) + self.assertIsNone(exporter._runner_pair((False, "armsx2"))) + self.assertTrue(EMULATORS_MK) + + if __name__ == "__main__": unittest.main()