From 950e6fae304db836249edc6731788d139af7629a Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 15:09:52 +0200 Subject: [PATCH] feat: find BizHawk where EmuHawk reads --- install.ps1 | 2 +- install.py | 26 ++++++++++++++++++++++++++ install.sh | 2 +- tests/test_install.py | 26 ++++++++++++++++++++++++++ wiki/installer.md | 2 ++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/install.ps1 b/install.ps1 index 3ecdc55f..656ddcb3 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 = "133242b850a1e2dfa820f39b466c8b56a9363aee1cc8df1e9a1f9f28744a0cdc" +$defaultInstallSha256 = "456d3e573f55c206ce3719160afee9b1d02fcfb7b6b53dceb33ebfd5c712a7fa" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index 8a4f3461..685017ae 100644 --- a/install.py +++ b/install.py @@ -1070,11 +1070,37 @@ def detect_platforms(os_type: str) -> list[tuple[str, Path]]: ) if system_dir: found.append(("retroarch", system_dir)) + # BizHawk is a portable archive reading \Firmware + # (PathExtensions.cs DataDirectoryPath, PathEntryCollection "Firmware"). + if lb_root and not any(name == "bizhawk" for name, _ in found): + emu_dir = launchbox_emulators(lb_root / "Data" / "Emulators.xml").get("emuhawk.exe") + if emu_dir is not None: + found.append(("bizhawk", emu_dir / "Firmware")) + + if os_type in ("linux", "wsl", "darwin"): + bizhawk = _bizhawk_unix_firmware() + if bizhawk is not None and not any(name == "bizhawk" for name, _ in found): + found.append(("bizhawk", bizhawk)) _detect_by_path(os_type, found) return found +def _bizhawk_unix_firmware() -> Path | None: + """BizHawk's Firmware folder on Unix, where its data home says. + + PathExtensions.cs reads $BIZHAWK_DATA_HOME before the executable's + folder, and the Nix package sets it to + $XDG_DATA_HOME/emuhawk-monort- (Dist/launch-scripts.nix). + """ + data_home = os.environ.get("BIZHAWK_DATA_HOME", "").strip() + if data_home and Path(data_home).is_dir(): + return Path(data_home) / "Firmware" + xdg = os.environ.get("XDG_DATA_HOME", "").strip() or str(Path.home() / ".local" / "share") + homes = sorted(Path(xdg).glob("emuhawk-monort-*"), key=lambda p: p.stat().st_mtime) + return homes[-1] / "Firmware" if homes else None + + # A libretro core as RetroArch loads it: _libretro.so|dll|dylib, and # _libretro_android.so on Android. _CORE_FILE_RE = re.compile( diff --git a/install.sh b/install.sh index 810ce9a7..fc4de55b 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="133242b850a1e2dfa820f39b466c8b56a9363aee1cc8df1e9a1f9f28744a0cdc" +DEFAULT_INSTALL_SHA256="456d3e573f55c206ce3719160afee9b1d02fcfb7b6b53dceb33ebfd5c712a7fa" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/tests/test_install.py b/tests/test_install.py index 1906d7e1..832222a9 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -2379,5 +2379,31 @@ class TheInstallerPageNamesEveryEvidence(unittest.TestCase): self.assertNotIn("have no detection", page) + +class BizHawkIsFoundWhereItReads(unittest.TestCase): + """BizHawk reads /Firmware: $BIZHAWK_DATA_HOME on Unix (the + Nix package sets $XDG_DATA_HOME/emuhawk-monort-), else the + EmuHawk folder. Only two conventional folders were looked at, and a + forced --platform bizhawk wrote where EmuHawk never reads.""" + + def test_the_data_home_variable(self): + home = Path(_tempdir(self)) + with unittest.mock.patch.dict(os.environ, {"BIZHAWK_DATA_HOME": str(home)}): + self.assertEqual(install._bizhawk_unix_firmware(), home / "Firmware") + + def test_the_nix_data_home(self): + xdg = Path(_tempdir(self)) + (xdg / "emuhawk-monort-2.11.1").mkdir() + env = {"XDG_DATA_HOME": str(xdg), "BIZHAWK_DATA_HOME": ""} + with unittest.mock.patch.dict(os.environ, env): + self.assertEqual( + install._bizhawk_unix_firmware(), xdg / "emuhawk-monort-2.11.1" / "Firmware" + ) + + def test_launchbox_names_emuhawk(self): + source = " ".join((REPO_ROOT / "install.py").read_text(encoding="utf-8").split()) + self.assertIn('.get("emuhawk.exe")', source) + + if __name__ == "__main__": unittest.main() diff --git a/wiki/installer.md b/wiki/installer.md index 92134d85..71aa1245 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -300,6 +300,8 @@ found on one machine. | `%APPDATA%\RetroArch\retroarch.cfg` | RetroArch (Windows) | `system_directory`, read when the exe folder has no config | | `%ProgramFiles(x86)%\Steam\steamapps\common\RetroArch\retroarch.cfg` | RetroArch (Steam) | `system_directory` | | LaunchBox `Data\Emulators.xml` | RetroArch (portable) | the system directory that entry points at | +| LaunchBox `Data\Emulators.xml` naming `EmuHawk.exe` | BizHawk (portable) | `Firmware` beside `EmuHawk.exe` | +| `$BIZHAWK_DATA_HOME`, or `$XDG_DATA_HOME/emuhawk-monort-` (Nix package) | BizHawk (Unix) | `Firmware` under it | `system_directory` is read the way RetroArch expands it: a leading `~` is the home directory, a leading `:` is the application directory, and `default` means