fix: place libretro dolphin files where it reads

This commit is contained in:
Abdessamad Derraz committed 2026-10-16 14:03:17 +02:00
1 parent 48b00730ff
commit b173a2a851
9 files changed
+215 -81

No files matched your search

+31 -34
View File
@@ -1303,33 +1303,8 @@ def generate_emulator_pack(
db,
standalone,
)
# A tree only one build reads follows that build, like a file.
for dd in filter_files_by_mode(
profile.get("data_directories", []), standalone
):
ref_key = dd.get("ref", "")
if not ref_key or not data_registry or ref_key not in data_registry:
if ref_key:
data_dir_notices.append(ref_key)
continue
entry = data_registry[ref_key]
local_cache = entry.get("local_cache", "")
if not local_cache or not os.path.isdir(local_cache):
data_dir_notices.append(ref_key)
continue
dd_dest = dd.get("destination", "")
if pack_structure:
mode_key = "standalone" if standalone else "libretro"
prefix = pack_structure.get(mode_key, "")
if prefix:
dd_dest = f"{prefix}/{dd_dest}" if dd_dest else prefix
total_files += _pack_data_tree(
zf, local_cache, dd_dest, seen_destinations, seen_parents, seen_lower
)
if not files:
print(f" No files needed for {profile.get('emulator', emu_name)}")
continue
# Collect archives as atomic units
archives: set[str] = set()
@@ -1393,7 +1368,7 @@ def generate_emulator_pack(
continue
if fe.get("archive"):
continue
dest = _resolve_destination(fe, pack_structure, standalone)
dest = _resolve_destination(fe, standalone)
if not dest:
continue
if region_drops and dest in region_drops:
@@ -1541,6 +1516,33 @@ def generate_emulator_pack(
packed_at[dest.lower()] = local_path
total_files += 1
# The trees after the files, as in a platform pack: a dump the
# profile declares wins over the copy a tree bundles at the same
# path (Dolphin's real dsp_rom.bin over Sys/GC's free one).
# A tree only one build reads follows that build, like a file.
for dd in filter_files_by_mode(
profile.get("data_directories", []), standalone
):
ref_key = dd.get("ref", "")
if not ref_key or not data_registry or ref_key not in data_registry:
if ref_key:
data_dir_notices.append(ref_key)
continue
entry = data_registry[ref_key]
local_cache = entry.get("local_cache", "")
if not local_cache or not os.path.isdir(local_cache):
data_dir_notices.append(ref_key)
continue
dd_dest = dd.get("destination", "")
if pack_structure:
mode_key = "standalone" if standalone else "libretro"
prefix = pack_structure.get(mode_key, "")
if prefix:
dd_dest = f"{prefix}/{dd_dest}" if dd_dest else prefix
total_files += _pack_data_tree(
zf, local_cache, dd_dest, seen_destinations, seen_parents, seen_lower
)
# Remove empty ZIP (no files packed and no missing = nothing to ship)
if total_files == 0 and not (missing_files or rejected_files or instruction_files or unsourceable_files):
os.unlink(zip_path)
@@ -1884,7 +1886,6 @@ def generate_md5_pack(
for fe in system.get("files", []):
plat_file_index.setdefault(fe.get("name", "").lower(), []).append(fe)
emu_pack_structure = None
emu_display = ""
if emulator_name and emulators_dir:
profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
@@ -1899,7 +1900,6 @@ def generate_md5_pack(
print(f"Error: {exc}", file=sys.stderr)
return None
emu_display = profile.get("emulator", emulator_name)
emu_pack_structure = profile.get("pack_structure")
for fe in profile.get("files", []):
plat_file_index.setdefault(fe.get("name", "").lower(), []).append(fe)
for alias in fe.get("aliases", []):
@@ -1955,12 +1955,9 @@ def generate_md5_pack(
if matched_fe:
if emulator_name:
# Laid out as the emulator pack lays it out: a profile
# without pack_structure still names its path and its
# standalone_path, and --standalone has to reach them.
dest = _resolve_destination(
matched_fe, emu_pack_structure, standalone
)
# Laid out as the emulator pack lays it out: its path and
# its standalone_path, which --standalone has to reach.
dest = _resolve_destination(matched_fe, standalone)
else:
dest = matched_fe.get("destination", matched_fe.get("name", name))
elif paths:
+2 -3
View File
@@ -695,7 +695,7 @@ def _collect_emulator_extras(
continue
if file_mode == "libretro" and is_standalone:
continue
if not read_from_system_dir(f):
if not read_from_system_dir(f, is_standalone):
continue
if is_standalone:
raw = f.get("standalone_path") or f.get("path") or fname
@@ -943,12 +943,11 @@ def emulator_region_drops(
region_index = region_mod.build_region_index(dict(selected))
region_groups: dict[str, list[tuple[str, str]]] = {}
for emu_name, profile in sorted(selected):
structure = profile.get("pack_structure")
# Decided over what the pack keeps, as on the platform side: an
# optional winner removed afterwards left its slot empty.
entries = filter_files_by_mode(profile.get("files", []), standalone)
for fe in _kept(entries, required_only):
dest = _resolve_destination(fe, structure, standalone)
dest = _resolve_destination(fe, standalone)
if dest:
group_id = _emulator_region_group(emu_name, profile, fe)
region_groups.setdefault(group_id, []).append(
+11 -14
View File
@@ -59,10 +59,16 @@ def _flat(arcname: str, prefix: str, flatten: bool) -> str:
return arcname[len(prefix) + 1:]
return arcname
def _resolve_destination(
file_entry: dict, pack_structure: dict | None, standalone: bool
) -> str:
"""Resolve the ZIP destination path for a file entry."""
def _resolve_destination(file_entry: dict, standalone: bool) -> str:
"""Resolve the ZIP destination path for a file entry.
``path:`` is where the libretro build reads the file from the system
directory and ``standalone_path:`` where the standalone app reads it, so
a file's destination is written whole. ``pack_structure`` places a
profile's data directories and archives: prefixing files with it gave
PPSSPP/PPSSPP/ppge_atlas.zim, and the platform packs, which never read
it, put libretro Dolphin's IPL at the root of system/.
"""
# 1. standalone_path override
if standalone and file_entry.get("standalone_path"):
rel = file_entry["standalone_path"]
@@ -73,16 +79,7 @@ def _resolve_destination(
else:
rel = file_entry.get("name", "")
rel = sanitize_pack_path(rel)
# Prepend pack_structure prefix
if pack_structure:
mode_key = "standalone" if standalone else "libretro"
prefix = pack_structure.get(mode_key, "")
if prefix:
rel = f"{prefix}/{rel}"
return rel
return sanitize_pack_path(rel)
def _archive_destination(
+9 -3
View File
@@ -448,14 +448,18 @@ def validate_cli_modes(args, mode_attrs: list[str]) -> None:
raise SystemExit(f"Options are mutually exclusive: --{' --'.join(mode_attrs)}")
def read_from_system_dir(file_entry: dict) -> bool:
def read_from_system_dir(file_entry: dict, standalone: bool = False) -> bool:
"""Whether the emulator reads this entry from the system directory.
A file loaded from the save, content or working directory is not a BIOS:
no platform lists it, no pack carries it, and the truth model must not
ask a platform to declare it.
ask a platform to declare it. Two builds can read one file from two
places: libretro Dolphin keeps its User tree in the save directory, the
standalone app in its own folder, so ``load_from`` can be keyed by build.
"""
load_from = file_entry.get("load_from", "")
if isinstance(load_from, dict):
load_from = load_from.get("standalone" if standalone else "libretro", "")
return not load_from or load_from == "system_dir"
@@ -478,7 +482,9 @@ def outside_gap_scope(file_entry: dict, is_standalone: bool) -> bool:
mode == "libretro" and is_standalone
):
return True
return not read_from_system_dir(file_entry) or bool(file_entry.get("agnostic"))
return not read_from_system_dir(file_entry, is_standalone) or bool(
file_entry.get("agnostic")
)
def filter_files_by_mode(files: list[dict], standalone: bool) -> list[dict]:
+3 -6
View File
@@ -1512,11 +1512,10 @@ def verify_emulator(
standalone,
)
if region_drops:
structure = profile.get("pack_structure")
files = [
fe
for fe in files
if _resolve_destination(fe, structure, standalone) not in region_drops
if _resolve_destination(fe, standalone) not in region_drops
]
# Check data directories (only notice if not cached)
@@ -1705,10 +1704,8 @@ def verify_emulator(
result["hle_fallback"] = hle
result["ground_truth"] = build_ground_truth(name, validation_index)
# The slot the emulator pack places this entry in: standalone_path
# under --standalone, the pack_structure prefix, the sanitised path.
dest = _resolve_destination(
file_entry, profile.get("pack_structure"), standalone
) or name
# under --standalone, the sanitised path.
dest = _resolve_destination(file_entry, standalone) or name
emu_results.setdefault(dest, []).append((result, required, hle))
for dest, alternatives in emu_results.items():