mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 04:39:06 -05:00
feat: place save-dir files in each system folder
Batocera, KNULLI, Recalbox, RetroBat and RetroDECK hand a libretro core <saves>/<system> at each launch. Each copy lands below every system that runs the core. EmuDeck keeps one unsorted saves/retroarch/saves. The unsupported platforms record why.
This commit is contained in:
1 parent
9a1bf4baa6
commit
2d26385efe
8 files changed
+394
-124
No files matched your search
+62
-15
@@ -6,6 +6,7 @@ on Windows is not a pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from dataclasses import dataclass
|
||||
|
||||
from common import sanitize_pack_path
|
||||
@@ -17,36 +18,58 @@ class SaveLayout:
|
||||
"""Where a platform's frontend keeps the save directory it hands a core.
|
||||
|
||||
``folder`` names it twice: the folder of the pack that holds the files
|
||||
read below it, and its default place beside the system directory
|
||||
(RetroArch's own folder holds system/ and saves/). A pack is extracted
|
||||
into the system directory, so that folder is moved by hand, and the
|
||||
installer writes to the save directory the frontend's config names.
|
||||
read below it, and its default place relative to the folder holding the
|
||||
system directory (RetroArch's own folder holds system/ and saves/,
|
||||
EmuDeck's Emulation/ holds bios/ and saves/retroarch/saves/). A pack is
|
||||
extracted into the system directory, so that folder is moved by hand,
|
||||
and the installer writes to the save directory the frontend's config
|
||||
names.
|
||||
``by_core`` is set when the frontend hands each core <save
|
||||
directory>/<library_name>, RetroArch's sort_savefiles_enable.
|
||||
``by_system`` is set when the launcher hands a core <save
|
||||
directory>/<system>, the system it was launched for (Batocera's
|
||||
/userdata/saves/<system>): a file read there is placed once for each
|
||||
system that runs the core, the systems the platform file names in
|
||||
``libretro_systems``. ``by_core_for`` names the cores a launcher
|
||||
sorts by core where it sorts no other.
|
||||
"""
|
||||
|
||||
folder: str
|
||||
by_core: bool
|
||||
by_system: bool = False
|
||||
by_core_for: frozenset[str] = frozenset()
|
||||
|
||||
def sorts(self, names: Iterable[str]) -> bool:
|
||||
"""Whether the frontend inserts the core's folder for a core known
|
||||
by any of these names: always where it sorts saves by core, else
|
||||
only for the cores its launcher sorts (``by_core_for``, RetroBat's
|
||||
CoreSaveSort)."""
|
||||
return self.by_core or bool(self.by_core_for & {str(n) for n in names})
|
||||
|
||||
|
||||
def save_layout(config: dict) -> SaveLayout | None:
|
||||
"""The platform's save directory, or None when it is no fixed place.
|
||||
|
||||
Read from the registry's ``save_directory``. A platform that keeps
|
||||
saves beside the content, or in one folder per launched system, has
|
||||
none: a file read below it has no single destination.
|
||||
saves beside the content has none: a file read below it has no single
|
||||
destination. ``folder`` is a relative path, never one that climbs.
|
||||
"""
|
||||
block = config.get("save_directory")
|
||||
if not isinstance(block, dict):
|
||||
return None
|
||||
folder = sanitize_pack_path(str(block.get("folder") or ""))
|
||||
if not folder or "/" in folder:
|
||||
raw = str(block.get("folder") or "")
|
||||
folder = sanitize_pack_path(raw)
|
||||
if not folder or folder != raw:
|
||||
return None
|
||||
return SaveLayout(folder, bool(block.get("by_core")))
|
||||
return SaveLayout(
|
||||
folder, bool(block.get("by_core")), bool(block.get("by_system")),
|
||||
frozenset(str(core) for core in block.get("by_core_for") or []),
|
||||
)
|
||||
|
||||
|
||||
def save_destination(
|
||||
path: str, core_folder: str | None, layout: SaveLayout
|
||||
path: str, core_folder: str | None, layout: SaveLayout, system: str = "",
|
||||
sorted_by_core: bool | None = None,
|
||||
) -> str | None:
|
||||
"""The pack path of a file a core reads below the save directory.
|
||||
|
||||
@@ -54,16 +77,40 @@ def save_destination(
|
||||
is the folder a frontend sorting saves by core inserts: the core's
|
||||
library_name, empty when it inserts none (an empty library_name, or a
|
||||
core that reads the same place sorted or not), None when the profile
|
||||
does not say. None then, where the frontend sorts by core.
|
||||
does not say. None then, where the frontend sorts by core. ``system``
|
||||
is the folder a launcher saving by system inserts first, required
|
||||
where the layout is ``by_system``. ``sorted_by_core`` says whether the
|
||||
frontend sorts this core's saves, the layout's ``by_core`` by default.
|
||||
"""
|
||||
rel = sanitize_pack_path(path)
|
||||
if not rel:
|
||||
return None
|
||||
if layout.by_core and core_folder:
|
||||
return f"{layout.folder}/{sanitize_pack_path(core_folder)}/{rel}"
|
||||
if layout.by_core and core_folder is None:
|
||||
root = layout.folder
|
||||
if layout.by_system:
|
||||
folder = sanitize_pack_path(system)
|
||||
if not folder or "/" in folder:
|
||||
return None
|
||||
root = f"{root}/{folder}"
|
||||
by_core = layout.by_core if sorted_by_core is None else sorted_by_core
|
||||
if by_core and core_folder:
|
||||
return f"{root}/{sanitize_pack_path(core_folder)}/{rel}"
|
||||
if by_core and core_folder is None:
|
||||
return None
|
||||
return f"{layout.folder}/{rel}"
|
||||
return f"{root}/{rel}"
|
||||
|
||||
|
||||
def libretro_systems(config: dict, names: Iterable[str]) -> list[str]:
|
||||
"""The systems a platform runs any of these libretro core names for.
|
||||
|
||||
Read from the platform file's ``libretro_systems`` (core -> the systems
|
||||
whose launch offers it), which the platform's scraper writes from its
|
||||
system list. Sorted, each once.
|
||||
"""
|
||||
table = config.get("libretro_systems") or {}
|
||||
found: set[str] = set()
|
||||
for name in names:
|
||||
found.update(str(system) for system in table.get(str(name)) or [])
|
||||
return sorted(found)
|
||||
|
||||
|
||||
def core_save_folder(profile: dict) -> str | None:
|
||||
|
||||
Reference in new issue
Block a user