diff --git a/emulators/dolphin.yml b/emulators/dolphin.yml index 64d47251f..03f3891ba 100644 --- a/emulators/dolphin.yml +++ b/emulators/dolphin.yml @@ -23,6 +23,9 @@ cores: - "dolphin-qt-gc" - "dolphin-qt-wii" systems: [nintendo-gamecube, nintendo-wii, sega-triforce] +standalone_directories: + - "$XDG_DATA_HOME/dolphin-emu" + - "~/.local/share/dolphin-emu" data_root: android: path: "Android/data/org.dolphinemu.dolphinemu/files" diff --git a/scripts/common.py b/scripts/common.py index d0f4236d5..9be55a3f3 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -2985,12 +2985,14 @@ def standalone_layout_path(entry: dict, profile: dict) -> str | None: ``standalone_path`` when the entry has one. A profile with no libretro build has one layout, so its ``path`` is that layout: UAE4All2 reads - kickstarts/kick13.rom below its working directory. + kickstarts/kick13.rom below its working directory. So does an entry only + the standalone build reads (``mode: standalone``), whose destination is + its ``path``: Dolphin's Load/Firmware/rtl_bt/ under its User directory. """ own = entry.get("standalone_path") if own: return own - if "libretro" in str(profile.get("type", "")): + if "libretro" in str(profile.get("type", "")) and entry.get("mode") != "standalone": return None return entry.get("path") or None diff --git a/scripts/packextras.py b/scripts/packextras.py index 3b3ad8354..ad394c0ba 100644 --- a/scripts/packextras.py +++ b/scripts/packextras.py @@ -1399,7 +1399,7 @@ def _collect_emulator_extras( # covered elsewhere belongs to another program's file. # Nor is one placed outside the BIOS tree by the layout. if (fname not in covered_names or f.get("unsourceable") or f.get("unplaced") - or f.get("root") in OUTER_ROOTS or f.get("unprefixed")): + or f.get("root") in OUTER_ROOTS): continue # Installed with the emulator or by the platform's package, # as the first pass already decided for the same entry. @@ -1429,7 +1429,12 @@ def _collect_emulator_extras( # of the directory core files go to, which the platform may not # declare (Recalbox has its System Cards under pcenginecd/ only, # geargrafx reads them from the root). - full_dest = prefixed_destination(dest, extras_prefix) + # A path of the install tree outside the BIOS folder keeps no + # prefix: EmuELEC's dolphin/dolphin-emu/GBA/gba_bios.bin. + full_dest = ( + sanitize_pack_path(dest) if f.get("unprefixed") + else prefixed_destination(dest, extras_prefix) + ) if full_dest in seen_dests and full_dest not in taken: continue if full_dest in taken and full_dest not in seen and fname not in platform_names: @@ -1475,6 +1480,8 @@ def _collect_emulator_extras( extra["contents"] = f["contents"] if f.get("system_dir"): extra["system_dir"] = f["system_dir"] + if f.get("unprefixed"): + extra["unprefixed"] = True extra["claim_order"] = (2, 0, emu_name) if _take(extra, full_dest, seen_dests, taken): extras.append(extra) diff --git a/tests/test_launcher_placement.py b/tests/test_launcher_placement.py index e0140569e..b03bd26d2 100644 --- a/tests/test_launcher_placement.py +++ b/tests/test_launcher_placement.py @@ -784,5 +784,28 @@ class CemuOnRetroDECK(unittest.TestCase): self.assertEqual(otp.get("unplaced"), "outside_bios_folder") +class DolphinUserDirectory(unittest.TestCase): + """EmuELEC runs Dolphin with XDG_DATA_HOME=/storage/roms/dolphin (dolphin.sh:28), + RetroDECK links $XDG_DATA_HOME/dolphin-emu/Triforce to bios/Triforce + (dolphin/component_prepare.sh:30, 49).""" + + def test_emuelec_installs_the_user_directory_below_its_roms(self) -> None: + view = standalone_view("dolphin", "emuelec") + placed = {e["name"]: e["standalone_path"] for e in view["files"] if e.get("layout_path")} + self.assertEqual(placed["segaboot.gcm"], "dolphin/dolphin-emu/Triforce/segaboot.gcm") + # A standalone-only entry's path is its place in the User directory. + self.assertEqual( + placed["rtl8723a_fw.bin"], "dolphin/dolphin-emu/Load/Firmware/rtl_bt/rtl8723a_fw.bin" + ) + self.assertTrue(all(e.get("unprefixed") for e in view["files"] if e.get("layout_path"))) + + def test_retrodeck_places_triforce_alone(self) -> None: + view = standalone_view("dolphin", "retrodeck") + placed = {e["name"]: e["standalone_path"] for e in view["files"] if e.get("layout_path")} + self.assertEqual(placed, {"segaboot.gcm": "Triforce/segaboot.gcm"}) + (ipl, *_) = [e for e in view["files"] if e["name"] == "IPL.bin"] + self.assertEqual(ipl.get("unplaced"), "outside_bios_folder") + + if __name__ == "__main__": unittest.main()