fix: place Cemu's MLC where RetroDECK sets it

RetroDECK sets mlc_path to $bios_path/cemu (cemu/component_prepare.sh:14,
29), which replaces the MLC Cemu reads under its user data directory
(src/config/CemuConfig.cpp:25-26, ActiveSettings.cpp:295-320). The
profile names that key, so the layout's mlc01/ folder is placed in
bios/cemu. Two readers kept the binding from reaching the manifest:

- a moved entry kept its standalone load_from, data_dir, and the gap
  report set the MLC aside as read outside the system directory. The
  platform's folder now replaces the build's own root.
- an entry read outside the BIOS tree was no longer offered to the
  launcher layer, so RetroDECK's link of keys.txt to bios/cemu/keys.txt
  went unserved. It is offered again, fills no destination by its own
  path, and a launcher place it fills settles it.

The RetroDECK manifest gains the 55 MLC files under bios/cemu/. It
drops the root copies of keys.txt, otp.bin, seeprom.bin,
shareddata.bin, cafeLibs/ and resources/sharedFonts/, which standalone
Cemu reads in the Flatpak's $XDG_DATA_HOME/Cemu and names them as read
outside the BIOS folder. The libretro copies under bios/Cemu/ stay.
This commit is contained in:
Abdessamad Derraz committed 2026-11-10 12:20:55 +01:00
1 parent 0e0e24d905
commit 74ef0a37d5
5 files changed
+38 -4

No files matched your search

+4 -2
View File
@@ -20,6 +20,8 @@ cores:
- cemu
- "cemu-sa"
systems: [nintendo-wiiu]
standalone_directories:
- "mlc_path = mlc01"
data_directories:
- ref: cemu-gameprofiles
@@ -71,7 +73,7 @@ notes: |
directory of that name (saves sorted per core). Standalone Cemu uses the
portable directory next to the executable when it exists, otherwise the
user data directory given per system in data_root, and the MLC defaults to
<user data>/mlc01. path: entries below are
<user data>/mlc01, which mlc_path in settings.xml replaces. path: entries below are
the libretro destination under the system directory, or under the save
directory for the MLC (load_from), and standalone_path: is relative to the
Cemu user data directory, which holds the MLC by default (load_from
@@ -80,7 +82,7 @@ notes: |
ref: src/libretro/CemuLibretro.cpp:2110-2170,
src/libretro/CemuLibretro.cpp:4083-4091,
src/gui/wxgui/CemuApp.cpp:89-124, src/gui/wxgui/CemuApp.cpp:126-172,
src/config/ActiveSettings.cpp:295-321
src/config/ActiveSettings.cpp:295-321, src/config/CemuConfig.cpp:25-26
keys.txt is read once from the user data path. A missing file is created
with a comment header and an example key. Each line is stripped of
+8
View File
@@ -3117,6 +3117,14 @@ def platform_layouts(
tree, path = placed
moved = {**f, "standalone_path": path,
"layout_path": standalone_layout_path(f, profile)}
if f.get("load_from"):
# The build's own root no longer applies: RetroDECK hands
# Cemu's MLC, read from its data directory, to bios/cemu.
load_from = f["load_from"]
moved["load_from"] = {
**(load_from if isinstance(load_from, dict) else {"libretro": load_from}),
"standalone": "system_dir",
}
if tree == "install":
# Below the roms root inside the install tree (AmberELEC's
# /storage/roms): a plain dest the platform prefix skips.
+6 -1
View File
@@ -1093,7 +1093,9 @@ class LauncherLayer:
# core asked for it: the pack puts a file there either way.
reported_at: dict[str, dict] = {}
for _emulator, _standalone, _entry, report in candidates:
if report.get("in_repo"):
# A file the layout reads outside the BIOS tree fills no
# destination of it.
if report.get("in_repo") and not report.get("unplaced"):
destination = sanitize_pack_path(report.get("path") or report.get("name", ""))
reported_at.setdefault(destination, report)
by_layout: dict[tuple[str, bool], list[tuple[dict, dict]]] = {}
@@ -1137,6 +1139,9 @@ class LauncherLayer:
continue
if isinstance(filled, dict):
added.append(filled)
# The launcher hands the emulator that file from the BIOS
# tree: read outside it no more.
settled.update(id(report) for _c, report in matched if report.get("unplaced"))
if place.stands_for_default:
# The emulator opens the launcher's file instead of its
# own name, so a copy under that name is read by nobody:
+4 -1
View File
@@ -809,7 +809,10 @@ def find_undeclared_files(
# Read below the roms directory (ROCKNIX's roms/pico-8)
# or in an emulator folder (RetroBat's emulators/pico8).
entry["root"] = f["root"]
elif launcher_layer and not save_root:
if launcher_layer and not save_root and f.get("root") not in OUTER_ROOTS:
# One read outside the BIOS tree is still served where a
# launcher links that very file: RetroDECK links Cemu's
# keys.txt to bios/cemu/keys.txt.
launcher_candidates.append((emu_name, is_standalone, f, entry))
keys = set_keys((emu_name, is_standalone), f)
if duplicate:
+16
View File
@@ -768,5 +768,21 @@ class Eka2l1OnRetroBat(unittest.TestCase):
self.assertEqual(entry.get("unplaced"), "outside_bios_folder", entry["name"])
class CemuOnRetroDECK(unittest.TestCase):
"""RetroDECK sets mlc_path to $bios_path/cemu (cemu/component_prepare.sh:14, 29)
and keeps Cemu's user data in its Flatpak's $XDG_DATA_HOME/Cemu."""
def test_the_mlc_lands_in_cemu(self) -> None:
view = standalone_view("cemu", "retrodeck")
mlc = [e for e in view["files"] if (e.get("layout_path") or "").startswith("mlc01/")]
self.assertTrue(mlc)
for entry in mlc:
self.assertEqual(
entry["standalone_path"], "cemu/" + entry["layout_path"].removeprefix("mlc01/")
)
(otp,) = [e for e in view["files"] if e["name"] == "otp.bin"]
self.assertEqual(otp.get("unplaced"), "outside_bios_folder")
if __name__ == "__main__":
unittest.main()