feat: follow a RetroDECK bios folder moved alone

RetroDECK moves each of its folders on its own and records the new place
in retrodeck.json paths (api_data_processing.sh:913-935). Only
rd_home_path was read, so files went to <home>/bios after a move.
This commit is contained in:
Abdessamad Derraz committed 2026-10-22 03:32:14 +02:00
1 parent e27a583975
commit 8dd463933e
5 files changed
+84 -6

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 = "9fbee0f382ca68880697ed573b79ecc1ffca793a406a3f258dad16e40d2845d6"
$defaultInstallSha256 = "b37588b71f2a6273cedf31ee05fc7b7053b5489f074349fef9cfae0dcf2555ff"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+38 -3
View File
@@ -1154,9 +1154,12 @@ def found_label(plat: str, path: Path) -> str:
platform whose files it gets when that is another name."""
name = detected_os(plat, path)
shown = display_name(plat)
moved = "".join(f", {folder}/ at {place}"
for folder, place in sorted(_MOVED_FOLDERS.get(path, {}).items())
if folder in ("bios", "roms"))
if not name or name == shown:
return f"{shown} at {path}"
return f"{name} at {path} ({shown} files)"
return f"{shown} at {path}{moved}"
return f"{name} at {path} ({shown} files){moved}"
def _android_storage_root() -> Path:
@@ -1564,7 +1567,9 @@ def detect_platforms(os_type: str) -> list[tuple[str, Path]]:
f"{retrodeck_json}; assuming {home / 'retrodeck'}",
file=sys.stderr,
)
found.append(("retrodeck", Path(rd_home) if rd_home else home / "retrodeck"))
rd_root = Path(rd_home) if rd_home else home / "retrodeck"
_record_moved_folders(rd_root, retrodeck_json)
found.append(("retrodeck", rd_root))
elif retrodeck_cfg.exists():
rd_home = _parse_bash_var(retrodeck_cfg, "rdhome")
found.append(("retrodeck", Path(rd_home) if rd_home else home / "retrodeck"))
@@ -1987,6 +1992,33 @@ def _safe_target_dir(value: object, field: str) -> str:
return value
# A platform whose folders move one by one: root -> {folder under the root:
# where it is now}. RetroDECK's per-folder move (RetroDECK 7786a849,
# functions/api_data_processing.sh:913-935) records each new place in
# retrodeck.json paths.<folder>_path (config/retrodeck/retrodeck.json:3-23),
# and its manifest places files under bios/ and roms/ of the home folder.
_MOVED_FOLDERS: dict[Path, dict[str, Path]] = {}
def _record_moved_folders(root: Path, retrodeck_json: Path) -> None:
"""Each RetroDECK folder that is no longer <home>/<name>."""
try:
paths = json.loads(retrodeck_json.read_text(encoding="utf-8", errors="replace"))["paths"]
except (OSError, ValueError, KeyError, TypeError):
return
if not isinstance(paths, dict):
return
moved = {}
for key, value in paths.items():
name = key[:-len("_path")] if key.endswith("_path") else ""
if name and name != "rd_home" and isinstance(value, str) and value.strip():
place = Path(value.strip())
if place != root / name:
moved[name] = place
if moved:
_MOVED_FOLDERS[root] = moved
def _destination_path(root: Path, value: object) -> Path:
"""Place a manifest destination below *root*.
@@ -1997,6 +2029,9 @@ def _destination_path(root: Path, value: object) -> Path:
emulator reads it.
"""
relative = _safe_relative_path(value, "dest")
moved = _MOVED_FOLDERS.get(root, {})
if len(relative.parts) > 1 and relative.parts[0] in moved:
return moved[relative.parts[0]].resolve() / Path(*relative.parts[1:])
return root.resolve() / Path(*relative.parts)
+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="9fbee0f382ca68880697ed573b79ecc1ffca793a406a3f258dad16e40d2845d6"
DEFAULT_INSTALL_SHA256="b37588b71f2a6273cedf31ee05fc7b7053b5489f074349fef9cfae0dcf2555ff"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+43
View File
@@ -2668,6 +2668,49 @@ class AnUnreadableRetroDeckConfigIsSaid(unittest.TestCase):
class RetroDeckFoldersMovedOneByOne(unittest.TestCase):
"""RetroDECK moves its bios folder on its own and records the new place
in retrodeck.json paths.bios_path, but only rd_home_path was read: the
files went to <home>/bios, a folder RetroDECK no longer reads."""
def _detect(self, paths: dict) -> tuple[Path, list]:
home = Path(_tempdir(self))
conf = home / ".var/app/net.retrodeck.retrodeck/config/retrodeck"
conf.mkdir(parents=True)
(conf / "retrodeck.json").write_text(json.dumps({"paths": paths}))
with unittest.mock.patch.object(install.Path, "home", return_value=home), \
unittest.mock.patch.object(install, "_detect_embedded", return_value=[]), \
unittest.mock.patch.dict(install._MOVED_FOLDERS, clear=True), \
contextlib.redirect_stderr(io.StringIO()):
found = install.detect_platforms("linux")
dests = {name: install._destination_path(home / "retrodeck", f"{name}/a.bin")
for name in ("bios", "roms", "saves")}
label = install.found_label("retrodeck", home / "retrodeck")
return home, [found, dests, label]
def test_a_moved_bios_folder_is_followed(self):
home = Path(_tempdir(self))
sd = home / "sd"
root, (found, dests, label) = self._detect({
"rd_home_path": "", "bios_path": str(sd / "bios"), "roms_path": str(sd / "roms"),
})
rd_home = root / "retrodeck"
self.assertIn(("retrodeck", rd_home), found)
self.assertEqual(dests["bios"], (sd / "bios").resolve() / "a.bin")
self.assertEqual(dests["roms"], (sd / "roms").resolve() / "a.bin")
self.assertEqual(dests["saves"], rd_home.resolve() / "saves" / "a.bin")
self.assertIn(f"bios/ at {sd / 'bios'}", label)
def test_folders_in_place_change_nothing(self):
home = Path(_tempdir(self))
rd_home = home / "rd"
_root, (found, dests, label) = self._detect({
"rd_home_path": str(rd_home), "bios_path": str(rd_home / "bios"),
})
self.assertIn(("retrodeck", rd_home), found)
self.assertNotIn("bios/ at", label)
class TheBoardTheSystemNamesIsTheTarget(unittest.TestCase):
"""Batocera, Recalbox and ROCKNIX each write the board they were built
for, but the installer read none of them, so a Batocera RPi4 installed
+1 -1
View File
@@ -372,7 +372,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` (Linux and macOS) | 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/net.retrodeck.retrodeck/config/retrodeck/retrodeck.json` | RetroDECK | `paths.rd_home_path`, falling back to the pre-migration `retrodeck.cfg` and its `rdhome`. A folder RetroDECK moved on its own is followed: files for `bios/` go to `paths.bios_path`, files for `roms/` to `paths.roms_path`, and the run names the new place |
| `~/.var/app/org.libretro.RetroArch/config/retroarch/retroarch.cfg` | RetroArch (Flatpak) | `system_directory` |
| `~/snap/retroarch/current/.config/retroarch/retroarch.cfg` | RetroArch (Snap) | `system_directory` |
| `~/.config/retroarch/retroarch.cfg` | RetroArch (native) | `system_directory` |