From cc18b4f2d61d76616aa3c48633567d2b71ae31fe Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 05:13:20 +0200 Subject: [PATCH] fix: detect platforms the registry describes The registry declares where each platform keeps its BIOS and every manifest publishes it, but the installer detects before it fetches any manifest and never had RetroPie, BizHawk, RetroBat's paths, or EmuDeck's default folder. It now carries the registry's path rules, held equal to the registry by a test. --- install.ps1 | 2 +- install.py | 42 ++++++++++++++++++++++++++++++++++++ install.sh | 2 +- tests/test_install.py | 50 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/install.ps1 b/install.ps1 index dc2e8869..2a57af8b 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 = "431f7d05350e222a3f4746dd13116bf570ec0f80117eed47d747372c61b57d77" +$defaultInstallSha256 = "dc6b428278a4352923c19ddcf055fd994aac1df77f35cb1d097e8f4c5d887ae1" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index 78611275..7dacd8aa 100644 --- a/install.py +++ b/install.py @@ -870,6 +870,47 @@ def detect_frontends(os_type: str, home: Path | None = None) -> list[str]: return frontends +# The registry's path_exists rules (platforms/_registry.yml install.detect), +# one row each: (os, platform, path, BIOS directory when it is not the path). +# The manifests publish them too, but detection runs before any manifest is +# fetched; a test holds this table equal to the registry. Without it a Linux +# home holding RetroPie/BIOS or .config/BizHawk/Firmware detected nothing. +_PATH_RULES: tuple[tuple[str, str, str, str | None], ...] = ( + ("windows", "retrobat", "C:\\RetroBat\\bios", None), + ("windows", "retrobat", "%USERPROFILE%\\RetroBat\\bios", None), + ("linux", "emudeck", "$HOME/Emulation/bios", None), + ("linux", "retrodeck", "$HOME/.var/app/net.retrodeck.retrodeck", "$HOME/retrodeck"), + ("linux", "romm", "/romm/library/bios", None), + ("linux", "retropie", "$HOME/RetroPie/BIOS", None), + ("windows", "bizhawk", "%USERPROFILE%\\BizHawk\\Firmware", None), + ("linux", "bizhawk", "$HOME/.config/BizHawk/Firmware", None), + ("linux", "misterfpga", "/media/fat/MiSTer", "/media/fat/games"), +) + + +def _rule_path(template: str) -> Path: + """A rule's path on this machine: $HOME or %USERPROFILE%, then the rest.""" + bases = ( + ("$HOME", Path.home()), + ("%USERPROFILE%", Path(os.environ.get("USERPROFILE") or Path.home())), + ) + for variable, base in bases: + if template.startswith(variable): + rest = [part for part in re.split(r"[\\/]", template[len(variable):]) if part] + return base.joinpath(*rest) + return Path(template) + + +def _detect_by_path(os_type: str, found: list[tuple[str, Path]]) -> None: + """Add what a registry path rule finds, for a platform not found yet.""" + systems = ("linux", "windows") if os_type == "wsl" else (os_type,) + for rule_os, platform, path, bios in _PATH_RULES: + if rule_os not in systems or any(name == platform for name, _ in found): + continue + if _rule_path(path).exists(): + found.append((platform, _rule_path(bios or path))) + + def detect_platforms(os_type: str) -> list[tuple[str, Path]]: """Detect installed emulator platforms and their BIOS directories.""" found: list[tuple[str, Path]] = [] @@ -981,6 +1022,7 @@ def detect_platforms(os_type: str) -> list[tuple[str, Path]]: if system_dir: found.append(("retroarch", system_dir)) + _detect_by_path(os_type, found) return found diff --git a/install.sh b/install.sh index 1715734c..097e4551 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="431f7d05350e222a3f4746dd13116bf570ec0f80117eed47d747372c61b57d77" +DEFAULT_INSTALL_SHA256="dc6b428278a4352923c19ddcf055fd994aac1df77f35cb1d097e8f4c5d887ae1" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/tests/test_install.py b/tests/test_install.py index 1d7f354f..f5caf519 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -920,6 +920,56 @@ class StandaloneCopiesReadWhereThePackPutTheFile(unittest.TestCase): install._validate_manifest(manifest, "emudeck") +class DetectionFollowsTheRegistry(unittest.TestCase): + """The registry declared RetroPie at ~/RetroPie/BIOS and BizHawk at + ~/.config/BizHawk/Firmware, every manifest published it, and the + installer never read it: such a home detected nothing.""" + + def test_the_table_is_the_registry(self): + import yaml # noqa: PLC0415 + + registry = yaml.safe_load( + (Path(__file__).resolve().parents[1] / "platforms" / "_registry.yml").read_text() + ) + rules = { + (rule["os"], name, rule["path"], rule.get("bios_path")) + for name, entry in registry["platforms"].items() + for rule in (entry.get("install") or {}).get("detect", []) + if rule["method"] == "path_exists" + } + self.assertEqual(set(install._PATH_RULES), rules) + + def _home(self) -> Path: + home = Path(_tempdir(self)) + patcher = unittest.mock.patch.dict(os.environ, {"HOME": str(home), "USERPROFILE": str(home)}) + patcher.start() + self.addCleanup(patcher.stop) + return home + + def test_a_home_layout_is_detected(self): + home = self._home() + (home / "RetroPie" / "BIOS").mkdir(parents=True) + (home / ".config" / "BizHawk" / "Firmware").mkdir(parents=True) + found: list = [] + install._detect_by_path("linux", found) + self.assertIn(("retropie", home / "RetroPie" / "BIOS"), found) + self.assertIn(("bizhawk", home / ".config" / "BizHawk" / "Firmware"), found) + + def test_a_windows_profile_path_is_detected(self): + home = self._home() + (home / "BizHawk" / "Firmware").mkdir(parents=True) + found: list = [] + install._detect_by_path("windows", found) + self.assertEqual(found, [("bizhawk", home / "BizHawk" / "Firmware")]) + + def test_a_platform_already_found_is_not_added_again(self): + home = self._home() + (home / "Emulation" / "bios").mkdir(parents=True) + found = [("emudeck", home / "elsewhere" / "bios")] + install._detect_by_path("linux", found) + self.assertEqual(found, [("emudeck", home / "elsewhere" / "bios")]) + + class InstallerStaysOnPython38AndTheStdlib(unittest.TestCase): """install.py runs where the bootstraps let it run: Python 3.8, stdlib only.