From ed7e83e99bf230692f16d59f29928f0cb7a422f5 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Tue, 6 Oct 2026 12:46:35 +0200 Subject: [PATCH] fix: default each platform to the folder it reads --- install.ps1 | 2 +- install.py | 33 +++++++++++++++++++++++++++++++-- install.sh | 2 +- tests/test_install.py | 42 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/install.ps1 b/install.ps1 index 37a6b587..9630c90f 100644 --- a/install.ps1 +++ b/install.ps1 @@ -8,7 +8,7 @@ param( $ErrorActionPreference = "Stop" $defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -$defaultInstallSha256 = "42b1747ae867d41da39b11daf783c0106daa6ed18861a2214c196f3a5c9a3c60" +$defaultInstallSha256 = "60e18da18b5a9a43b18d37020b676ad47babd6d12c5314e8fcc1370e4864c4e5" $maximumInstallerBytes = 2MB $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } $temporary = $null diff --git a/install.py b/install.py index a906bb68..eeef4695 100644 --- a/install.py +++ b/install.py @@ -103,8 +103,35 @@ DEFAULT_DESTS = { "rocknix": Path("/storage/roms/bios"), "misterfpga": Path("/media/fat/games"), "retropie": Path.home() / "RetroPie" / "BIOS", + "romm": Path("/romm/library/bios"), } + +def _os_default_dests(os_type: str) -> dict[str, Path]: + """Where a platform keeps its BIOS on this OS, when it is not detected. + + The same folders the platform's detection looks for: a platform named on + the command line but not found must not land in ~/bios, which none of + them reads. + """ + home = Path.home() + if os_type == "windows": + profile = Path(os.environ.get("USERPROFILE", str(home))) + appdata = Path(os.environ.get("APPDATA", str(home / "AppData" / "Roaming"))) + return { + "retroarch": appdata / "RetroArch" / "system", + "retrobat": profile / "RetroBat" / "bios", + "bizhawk": profile / "BizHawk" / "Firmware", + } + if os_type == "darwin": + return { + "retroarch": home / "Library" / "Application Support" / "RetroArch" / "system", + } + return { + "retroarch": home / ".config" / "retroarch" / "system", + "bizhawk": home / ".config" / "BizHawk" / "Firmware", + } + # Set when the run is interrupted, so queued downloads end instead of draining # the pool: every future is submitted before the first one completes. _stop = threading.Event() @@ -1385,7 +1412,7 @@ def _prompt_manual_platform(os_type: str) -> list[tuple[str, Path]]: plat = AVAILABLE_PLATFORMS[int(choice) - 1] elif choice in AVAILABLE_PLATFORMS: plat = choice - default_dest = DEFAULT_DESTS.get(plat, Path.home() / "bios") + default_dest = _default_dest(os_type, plat) try: raw = input(f"BIOS directory [{default_dest}]: ").strip() except (EOFError, KeyboardInterrupt): @@ -1405,7 +1432,9 @@ def _default_dest(os_type: str, platform_name: str) -> Path: """ if os_type == "android" and platform_name == "retroarch": return _android_storage_root() / "RetroArch" / "system" - return DEFAULT_DESTS.get(platform_name, Path.home() / "bios") + return _os_default_dests(os_type).get( + platform_name, DEFAULT_DESTS.get(platform_name, Path.home() / "bios") + ) def _manual_usage_hint(os_type: str) -> list[str]: diff --git a/install.sh b/install.sh index 1d6d7bdb..af80de0a 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ esac TEMP_INSTALLER="" TEMP_DIRECTORY="" DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -DEFAULT_INSTALL_SHA256="42b1747ae867d41da39b11daf783c0106daa6ed18861a2214c196f3a5c9a3c60" +DEFAULT_INSTALL_SHA256="60e18da18b5a9a43b18d37020b676ad47babd6d12c5314e8fcc1370e4864c4e5" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/tests/test_install.py b/tests/test_install.py index 6e48468f..5ce43617 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -183,6 +183,44 @@ class TestDefaultDests(unittest.TestCase): ) +class TestDefaultsFollowTheRegistry(unittest.TestCase): + """A platform named but not detected fell back to ~/bios on every OS for + RetroArch, RetroBat, BizHawk and RomM, a folder none of them reads.""" + + ENV = {"USERPROFILE": "/u", "APPDATA": "/u/AppData/Roaming"} + + def _expand(self, raw: str) -> Path: + text = raw.replace("$HOME", str(Path.home())) + for name, value in self.ENV.items(): + text = text.replace(f"%{name}%", value) + return Path(text.replace("\\", "/")) + + def test_the_default_is_a_folder_detection_looks_for(self): + import yaml + + registry = yaml.safe_load( + (Path(__file__).resolve().parent.parent / "platforms" / "_registry.yml") + .read_text(encoding="utf-8") + )["platforms"] + checked = 0 + with unittest.mock.patch.dict(os.environ, self.ENV): + for name, entry in registry.items(): + by_os: dict[str, set[Path]] = {} + for rule in (entry.get("install") or {}).get("detect", []): + if rule.get("bios_path"): + by_os.setdefault(rule["os"], set()).add(self._expand(rule["bios_path"])) + elif rule.get("method") == "path_exists": + by_os.setdefault(rule["os"], set()).add(self._expand(rule["path"])) + elif rule.get("parse_key") == "system_directory": + config = self._expand(rule["config"]) + by_os.setdefault(rule["os"], set()).add(config.parent / "system") + for os_type, candidates in by_os.items(): + with self.subTest(platform=name, os=os_type): + self.assertIn(install._default_dest(os_type, name), candidates) + checked += 1 + self.assertGreater(checked, 10) + + class TestEmbeddedDetection(unittest.TestCase): """MiSTer is identified by the main binary at the SD card root.""" @@ -1025,7 +1063,9 @@ class TestManualPlatformPrompt(unittest.TestCase): answers = iter(["1", ""]) with unittest.mock.patch("builtins.input", lambda _prompt="": next(answers)): chosen = install._prompt_manual_platform("linux") - self.assertEqual(chosen, [("retroarch", Path.home() / "bios")]) + self.assertEqual( + chosen, [("retroarch", Path.home() / ".config" / "retroarch" / "system")] + ) def test_name_and_explicit_directory(self): answers = iter(["batocera", "~/my bios"])