fix: find EmuDeck Windows where its scripts look

functions/all.ps1:4-14 (emudeck-we 037460ef) reads the APPDATA
settings.ps1, else the one under the profile, and takes C:\Emulation when
neither names a folder. Only the first was read and a forced platform fell
back to %USERPROFILE%\Emulation\bios. A network install names its share
through $VolName (JSONtoPS1.ps1:166-172), and a UTF-16 settings file is
decoded.
This commit is contained in:
Abdessamad Derraz committed 2026-10-21 05:31:35 +02:00
1 parent cdb2cfa6f0
commit 15cb52d551
6 files changed
+100 -15

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@
$ErrorActionPreference = "Stop"
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
$defaultInstallSha256 = "f05f862fcebc16e99d692a20a09163665828ea0383643fcc96a41b11d77fcff7"
$defaultInstallSha256 = "67bba818ec05142e431155c27919e087d0d5aa1d5e148019ebae630f0dd26bdf"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+33 -10
View File
@@ -206,6 +206,9 @@ def _os_default_dests(os_type: str) -> dict[str, Path]:
"retroarch": _host_path("C:/RetroArch-Win64/system"),
# RetroBat's installer defaults to C:\RetroBat (installer.iss).
"retrobat": _host_path("C:/RetroBat/bios"),
# EmuDeck's Windows edition: C:\Emulation when its settings name
# no folder (emudeck-we functions/all.ps1:12-14).
"emudeck": _host_path("C:/Emulation/bios"),
}
if os_type == "wsl":
# RetroArch may be the Linux one inside the distribution, while RetroBat
@@ -749,7 +752,11 @@ def _parse_ps1_var(path: Path, key: str) -> str | None:
return None
normalized = key.lstrip("$")
try:
for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
raw = path.read_bytes()
# Windows PowerShell's Out-File writes UTF-16, Set-Content -Encoding
# UTF8 a byte order mark: EmuDeck's settings.ps1 passes through both.
encoding = "utf-16" if raw[:2] in (b"\xff\xfe", b"\xfe\xff") else "utf-8-sig"
for line in raw.decode(encoding, errors="replace").splitlines():
line = line.strip()
check = line.lstrip("$")
if check.startswith(f"{normalized}="):
@@ -1231,13 +1238,9 @@ def detect_platforms(os_type: str) -> list[tuple[str, Path]]:
break
if os_type in ("windows", "wsl"):
# EmuDeck Windows
home = Path.home()
emudeck_ps1 = Path(os.environ.get("APPDATA", "")) / "EmuDeck" / "settings.ps1"
if emudeck_ps1.exists():
emu_path = _parse_ps1_var(emudeck_ps1, "$emulationPath")
if emu_path:
found.append(("emudeck", Path(emu_path) / "bios"))
emudeck = _emudeck_windows_bios()
if emudeck is not None:
found.append(("emudeck", emudeck))
# RetroArch Windows: open_default_config_file reads retroarch.cfg
# beside the exe, then under %APPDATA%\RetroArch, and a value left at
@@ -1302,6 +1305,27 @@ def detect_platforms(os_type: str) -> list[tuple[str, Path]]:
return found
def _emudeck_windows_bios() -> Path | None:
"""The BIOS folder of EmuDeck's Windows edition, as its scripts find it.
functions/all.ps1:4-14 (emudeck-we 037460ef) loads
%APPDATA%\\EmuDeck\\settings.ps1, else %USERPROFILE%\\EmuDeck\\settings.ps1,
and takes C:\\Emulation when neither sets $emulationPath. A network
install sets it through a drive mapped to $VolName in that session
(JSONtoPS1.ps1:166-172), which is the share itself here.
"""
candidates = [Path(os.environ[var]) / "EmuDeck" / "settings.ps1"
for var in ("APPDATA", "USERPROFILE") if os.environ.get(var)]
settings = next((path for path in candidates if path.is_file()), None)
if settings is None:
return None
emu_path = _parse_ps1_var(settings, "$emulationPath")
if emu_path and ("$" in emu_path or "+" in emu_path):
share = _parse_ps1_var(settings, "$VolName")
emu_path = f"{share.rstrip(chr(92))}\\Emulation" if share else None
return _host_path(emu_path or "C:\\Emulation") / "bios"
# Identity Name in pkg/msvc-uwp/RetroArch-msvcUWP/Package.appxmanifest.
RETROARCH_UWP_ID = "1e4cf179-f3c2-404f-b9f3-cb2070a5aad8"
@@ -3828,8 +3852,7 @@ def _default_dest(os_type: str, platform_name: str) -> Path | None:
if own is not None:
return own
if os_type == "windows":
# EmuDeck's Windows edition keeps the same folder under the profile.
return DEFAULT_DESTS["emudeck"] if platform_name == "emudeck" else None
return None
if platform_name == "retrobat":
return None
return DEFAULT_DESTS.get(platform_name)
+1 -1
View File
@@ -18,7 +18,7 @@ esac
TEMP_INSTALLER=""
TEMP_DIRECTORY=""
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
DEFAULT_INSTALL_SHA256="f05f862fcebc16e99d692a20a09163665828ea0383643fcc96a41b11d77fcff7"
DEFAULT_INSTALL_SHA256="67bba818ec05142e431155c27919e087d0d5aa1d5e148019ebae630f0dd26bdf"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+7
View File
@@ -392,11 +392,18 @@ platforms:
- os: linux
method: path_exists
path: $HOME/Emulation/bios
# functions/all.ps1:4-14 (emudeck-we 037460ef): the APPDATA settings,
# else the profile ones, and C:\Emulation when neither names a folder.
- os: windows
method: config_file
config: '%APPDATA%\EmuDeck\settings.ps1'
parse_key: $emulationPath
bios_subdir: bios
- os: windows
method: config_file
config: '%USERPROFILE%\EmuDeck\settings.ps1'
parse_key: $emulationPath
bios_subdir: bios
standalone_copies: *standalone_copies
lakka:
config: lakka.yml
+56 -1
View File
@@ -3180,8 +3180,9 @@ class NoFallbackOutsideWhatAPlatformReads(unittest.TestCase):
self.assertIsNone(install._default_dest("windows", "batocera"))
self.assertIsNone(install._default_dest("windows", "retropie"))
self.assertEqual(install._default_dest("linux", "batocera"), Path("/userdata/bios"))
# emudeck-we functions/all.ps1:12-14, not the profile folder.
self.assertEqual(
install._default_dest("windows", "emudeck"), install.DEFAULT_DESTS["emudeck"]
install._default_dest("windows", "emudeck"), install._host_path("C:/Emulation/bios")
)
def test_a_forced_platform_without_a_folder_is_refused(self):
@@ -3847,5 +3848,59 @@ class RetroBatIsFoundWhereItSaysItIs(unittest.TestCase):
self.assertEqual(run.call_args[0][0][:2], ["reg.exe", "query"])
class EmuDeckWindowsIsFoundAsItsScriptsFindIt(unittest.TestCase):
"""EmuDeck's Windows scripts read %APPDATA%\\EmuDeck\\settings.ps1, else
%USERPROFILE%\\EmuDeck\\settings.ps1, and take C:\\Emulation when neither
names a folder (functions/all.ps1:4-14). Only the first was read, and a
forced --platform emudeck wrote into %USERPROFILE%\\Emulation\\bios."""
def _env(self) -> tuple[Path, Path]:
root = Path(_tempdir(self))
appdata, profile = root / "appdata", root / "profile"
patcher = unittest.mock.patch.dict(
os.environ, {"APPDATA": str(appdata), "USERPROFILE": str(profile)})
patcher.start()
self.addCleanup(patcher.stop)
return appdata, profile
@staticmethod
def _settings(folder: Path, text: str, encoding: str = "utf-8-sig") -> None:
(folder / "EmuDeck").mkdir(parents=True, exist_ok=True)
(folder / "EmuDeck" / "settings.ps1").write_bytes(text.encode(encoding))
def test_the_profile_settings_are_read_when_appdata_has_none(self):
_appdata, profile = self._env()
self._settings(profile, '\n$emulationPath="/games/Emulation"\n')
self.assertEqual(install._emudeck_windows_bios(), Path("/games/Emulation/bios"))
def test_appdata_comes_first(self):
appdata, profile = self._env()
self._settings(profile, '$emulationPath="/b/Emulation"\n')
self._settings(appdata, '$emulationPath="/a/Emulation"\n', "utf-16")
self.assertEqual(install._emudeck_windows_bios(), Path("/a/Emulation/bios"))
def test_settings_naming_no_folder_mean_c_emulation(self):
appdata, _profile = self._env()
self._settings(appdata, '$device="Windows"\n')
self.assertEqual(install._emudeck_windows_bios(),
install._host_path("C:\\Emulation") / "bios")
def test_a_network_install_is_its_share(self):
appdata, _profile = self._env()
self._settings(appdata, '$networkInstallation="true"\n'
'$VolName="/nas/games"\n'
'$emulationPath=$testdrive.ToString() + ":\\Emulation"\n')
self.assertEqual(install._emudeck_windows_bios(),
Path("/nas/games\\Emulation") / "bios")
def test_no_settings_detects_nothing(self):
self._env()
self.assertIsNone(install._emudeck_windows_bios())
def test_a_forced_platform_falls_back_to_c_emulation(self):
self.assertEqual(install._default_dest("windows", "emudeck"),
install._host_path("C:/Emulation/bios"))
if __name__ == "__main__":
unittest.main()
+2 -2
View File
@@ -320,7 +320,7 @@ WSL does not pass them. A forced `--platform retrobat` falls back to
|----------|----------|----------------|
| `HKCU\SOFTWARE\RetroBat` `LatestKnownInstallPath`, which `RetroBat.exe` writes on every start (read through `reg.exe` under WSL) | RetroBat | that folder + `\bios`, before the fixed folders above |
| `~/.config/EmuDeck/settings.sh` | EmuDeck | `emulationPath` + `/bios` |
| `%APPDATA%\EmuDeck\settings.ps1` | EmuDeck | `$emulationPath` + `\bios` |
| `%APPDATA%\EmuDeck\settings.ps1`, else `%USERPROFILE%\EmuDeck\settings.ps1` | EmuDeck | `$emulationPath` + `\bios`, `C:\Emulation\bios` when it names none (a network install: the `$VolName` share + `\Emulation\bios`) |
| `~/.var/app/net.retrodeck.retrodeck/config/retrodeck/retrodeck.json` | RetroDECK | `paths.rd_home_path`, falling back to the pre-migration `retrodeck.cfg` and its `rdhome` |
| `~/.var/app/org.libretro.RetroArch/config/retroarch/retroarch.cfg` | RetroArch (Flatpak) | `system_directory` |
| `~/snap/retroarch/current/.config/retroarch/retroarch.cfg` | RetroArch (Snap) | `system_directory` |
@@ -363,7 +363,7 @@ When a platform named with `--platform` is not found on the machine, the
destination falls back to the directory its detection looks for:
`/userdata/bios` for Batocera, `/recalbox/share/bios` for Recalbox,
`/storage/system` for Lakka, `~/retrodeck` for RetroDECK, `~/Emulation/bios`
for EmuDeck, `/storage/roms/bios` for ROCKNIX, `/media/fat/games` for MiSTer,
for EmuDeck (`C:\Emulation\bios` on Windows), `/storage/roms/bios` for ROCKNIX, `/media/fat/games` for MiSTer,
`~/RetroPie/BIOS` for RetroPie, `/romm/library/bios` for RomM,
`C:\RetroBat\bios` for RetroBat, and RetroArch's own default system
directory for RetroArch. BizHawk has no such folder: it reads `Firmware`