fix: let search order guard regional files

This commit is contained in:
Abdessamad Derraz committed 2026-10-10 10:56:09 +02:00
1 parent c2bdef4db4
commit 1cedc9930b
19 files changed
+1635 -790

No files matched your search

+6 -1
View File
@@ -90,7 +90,7 @@ class SelectionIndex:
`systems` drives --system. A regional entry adds its regions,
`region_groups` it always competes in as a core extra, and
`region_system_groups` it competes in only when that platform system
is kept. An entry that never competes carries no region field: an
is kept, and `priority` the rank the code searches it at. An entry that never competes carries no region field: an
untagged file always survives a region filter.
"""
out: dict = {}
@@ -109,4 +109,9 @@ class SelectionIndex:
declared = self.declared_by.get(dest)
if declared:
out["region_system_groups"] = sorted(declared)
# The code's search rank decides whether a world file may withdraw
# this one when no region matches.
priority = region_mod.lookup_priority(self.region_index, dest, name)
if priority is not None:
out["priority"] = priority
return out
+51 -4
View File
@@ -150,12 +150,15 @@ def build_region_index(profiles: dict) -> dict[str, dict]:
# Path-keyed so same-named entries stay separate (Dolphin declares
# three IPL.bin), name-keyed so a candidate identified by name alone
# sees the union and is never dropped on ambiguity.
priority = f.get("priority")
for key in {path, name} - {""}:
entry = index.setdefault(
key,
{"regions": set(), "has_untagged": False, "emulators": []},
{"regions": set(), "has_untagged": False, "emulators": [],
"priorities": set()},
)
entry["regions"] |= regions
entry["priorities"].add(priority if isinstance(priority, int) else None)
if not regions:
entry["has_untagged"] = True
if emu_name not in entry["emulators"]:
@@ -185,6 +188,33 @@ def lookup_regions(index: dict[str, dict], destination: str, name: str) -> set[s
return set(entry["regions"])
def _index_entry(index: dict[str, dict], destination: str, name: str) -> dict | None:
"""The index entry a candidate resolves to, by the lookup_regions order."""
if destination:
entry = index.get(destination)
if entry:
return entry
parts = destination.split("/")
for i in range(1, len(parts)):
entry = index.get("/".join(parts[i:]))
if entry:
return entry
return index.get(name)
def lookup_priority(index: dict[str, dict], destination: str, name: str) -> int | None:
"""The search rank the code gives a candidate, lower first.
None when no entry declares one, or when two declarations disagree: a
rank in doubt cannot decide what to drop.
"""
entry = _index_entry(index, destination, name)
priorities = (entry or {}).get("priorities", set())
if len(priorities) != 1:
return None
return next(iter(priorities))
def _competing_ranks(
members: list[tuple[str, str]],
index: dict[str, dict],
@@ -212,8 +242,8 @@ def resolve_region_drops(
"""Destinations to skip for a requested region priority list.
Per group, an exact/parent regional match beats other regional candidates.
A world candidate beats unmatched regional fallbacks, while untagged files
always survive. If neither a requested nor a world candidate exists, all
A world candidate beats unmatched regional fallbacks the code does not
search before it, while untagged files always survive. If neither a requested nor a world candidate exists, all
regional candidates survive so filtering can never empty a group.
"""
if not requested:
@@ -243,7 +273,24 @@ def resolve_region_drops(
keep |= {destination for r, destination in matched if r == best}
drop |= {destination for _r, destination in regional if destination not in keep}
elif world:
drop |= {destination for _r, destination in regional}
# A world file beats an unmatched regional one, unless the code
# provably looks for the regional one first: DuckStation ranks its
# PS1 images 5 to 50 and its world PS2 images 100, so a request
# for Brazil would otherwise leave it only the PS2 fallbacks.
world_ranks = [
lookup_priority(index, destination, name)
for destination, name in members
if destination in world
]
known = None not in world_ranks
best_world = min(world_ranks) if known and world_ranks else None
for _r, destination in regional:
name = next(n for d, n in members if d == destination)
ours = lookup_priority(index, destination, name)
if best_world is not None and ours is not None and ours < best_world:
keep.add(destination)
else:
drop.add(destination)
else:
# Preserve every unmatched candidate as a visible fallback.
keep |= {destination for _r, destination in regional}