fix: read xroar coco3 ROMs from RetroBat coco

RetroBat passes XRoar -rompath bios/coco for the coco system, and its
es_features offers the CoCo 3 machines there. The per-system record now
carries those machines (machines:), and an entry whose name the
platform list does not declare is launched for the system offering
its own profile system as a machine, by _norm_system_id. coco3.rom and
coco3p.rom move from bios/dragon to bios/coco. RetroBat offers no
MC-10 machine, so the mc10 ROMs keep the default bios/dragon.
This commit is contained in:
Abdessamad Derraz committed 2026-11-09 20:15:47 +01:00
1 parent 339297c75c
commit 4b17bf0460
8 files changed
+138 -8

No files matched your search

+24 -2
View File
@@ -2697,6 +2697,8 @@ EXECUTED_PREFIX = "exec:"
# systems a launcher record is written for, as JSON [system, name]: it says
# which launch reads a profile entry of that name.
SYSTEM_FILE_PREFIX = "sysfile:"
# The key, in that index, of a machine a launcher record offers.
MACHINE_KEY = "machine:"
def core_system_dirs(
@@ -2781,6 +2783,18 @@ def standalone_set(config: dict) -> set[str]:
for f in system.get("files") or []
if f.get("name")
}
# The machines a record offers under its systems, by normalized id.
from launchers import native_systems # noqa: PLC0415
natives = native_systems(config)
names |= {
SYSTEM_FILE_PREFIX + json.dumps(
[natives.get(str(native), str(native)), MACHINE_KEY + _norm_system_id(str(m))]
)
for record in config.get("launchers") or []
for native in record.get("systems") or []
for m in record.get("machines") or []
}
names |= {
EXECUTED_PREFIX + json.dumps([run.emulator, run.name, run.place, run.source_ref])
for run in executed_files(config)
@@ -3000,9 +3014,17 @@ def platform_layouts(
def bindings_of(entry: dict) -> dict[str, tuple[str, str]]:
systems = frozenset()
if per_system:
own = entry_launch_systems(entry, {})
systems = entry_launch_systems(entry, index)
if systems == entry_launch_systems(entry, {}):
systems = systems.union(*(family.get(s, set()) for s in systems))
if systems == own:
systems = systems.union(*(family.get(s, set()) for s in own))
if systems == own:
# The platform's list names none of the entry's names: its
# own system selects the launch that offers it as a machine
# (RetroBat runs XRoar's coco3 under coco).
systems = systems.union(*(
index.get(MACHINE_KEY + _norm_system_id(s), set()) for s in own
))
if systems not in by_systems:
by_systems[systems] = standalone_bindings_for(
emu_name, profile, standalone_cores, systems
+3 -1
View File
@@ -35,6 +35,8 @@ class Launcher:
fails_scope: str | None = None
depends_on_option: str | None = None
device: str | None = None
# The machines the platform offers under the record's systems.
machines: list[str] | None = None
def entry(self) -> dict[str, Any]:
"""The platform YAML form, in a fixed key order, unset keys left out."""
@@ -42,7 +44,7 @@ class Launcher:
"emulator", "build", "kind", "slot", "names", "expands",
"case_insensitive", "search_dir", "passes_to",
"fails_when_missing", "fails_scope", "depends_on_option",
"device", "source_ref",
"device", "machines", "source_ref",
)
return {key: getattr(self, key) for key in order if getattr(self, key) is not None}
+42
View File
@@ -292,6 +292,33 @@ def _throw_message(src: Source, start: int, end: int) -> tuple[str, int]:
return f"ApplicationException '{message}'", src.line(found.start())
def system_feature_choices(
src: Source, emulator: str, option: str, system: str
) -> tuple[list[str], tuple[int, int]]:
"""Values es_features.cfg offers for an option of an emulator under one system."""
text = src.text
for found in re.finditer(r'<emulator name="([^"]*)"', text):
if emulator in [name.strip() for name in found.group(1).split(",")]:
start, end = found.start(), text.find("</emulator>", found.start())
break
else:
raise LauncherPatternMissing(src.path, f"emulator {emulator}")
for block in re.finditer(r'<system name="([^"]*)">', text[start:end]):
if system not in [name.strip() for name in block.group(1).split(",")]:
continue
scope = start + block.start()
close = text.find("</system>", scope)
feature = src.find(
rf'<feature [^>]*value="{re.escape(option)}"', f"{option} of {system}", scope, close
)
stop = text.find("</feature>", feature.end())
values = re.findall(r'<choice [^>]*value="([^"]*)"', text[feature.end():stop])
if not values:
raise LauncherPatternMissing(src.path, f"choices of {option} for {system}")
return values, src.lines(feature.start(), stop + len("</feature>"))
raise LauncherPatternMissing(src.path, f"system {system} of {emulator}")
def feature_choices(
src: Source, emulator: str, option: str, core: str | None = None
) -> tuple[list[str], tuple[int, int]]:
@@ -2983,6 +3010,21 @@ def xroar(sources: dict[str, Source]) -> list[Record]:
for record, conditions in found
for split in _per_system(src, _with_conditions(record, conditions))
]
# The machines ES offers under a system the folder is chosen for: XRoar
# runs a CoCo 3 for coco, so its ROMs are read from that system's folder.
features = sources[FEATURES]
for record in records:
if not record.get("systems"):
continue
machines: list[str] = []
lines = []
for system in record["systems"]:
choices, span = system_feature_choices(features, "xroar", "xroar_machine", system)
machines += [m for m in choices if m not in machines]
lines.append(span)
record["machines"] = machines
ref = record.pop("source_ref")
record["source_ref"] = f"{ref}, {source_ref((features.path, lines))}"
line = src.find(r'string (\w+) = ("rompath (?:[^"\\\n]|\\.)*");', "config rompath line")
written = _unescape(line.group(2)[1:-1])
relative = re.fullmatch(r'rompath "(.*)"', written)