mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-04-13 12:22:33 -05:00
feat: allow --platform + --system combination
This commit is contained in:
@@ -1068,3 +1068,14 @@ def list_system_ids(emulators_dir: str) -> None:
|
||||
for sys_id in sorted(system_emus):
|
||||
count = len(system_emus[sys_id])
|
||||
print(f" {sys_id:35s} ({count} emulator{'s' if count > 1 else ''})")
|
||||
|
||||
|
||||
def list_platform_system_ids(platform_name: str, platforms_dir: str) -> None:
|
||||
"""Print system IDs from a platform's YAML config."""
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
systems = config.get("systems", {})
|
||||
for sys_id in sorted(systems):
|
||||
file_count = len(systems[sys_id].get("files", []))
|
||||
mfr = systems[sys_id].get("manufacturer", "")
|
||||
mfr_display = f" [{mfr.split('|')[0]}]" if mfr else ""
|
||||
print(f" {sys_id:35s} ({file_count} file{'s' if file_count != 1 else ''}){mfr_display}")
|
||||
|
||||
@@ -27,7 +27,8 @@ sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import (
|
||||
_build_validation_index, build_zip_contents_index, check_file_validation,
|
||||
check_inside_zip, compute_hashes, fetch_large_file, filter_files_by_mode,
|
||||
group_identical_platforms, list_emulator_profiles, list_registered_platforms,
|
||||
group_identical_platforms, list_emulator_profiles, list_platform_system_ids,
|
||||
list_registered_platforms,
|
||||
filter_systems_by_target, list_system_ids, load_database,
|
||||
load_data_dir_registry, load_emulator_profiles, load_platform_config,
|
||||
md5_composite, resolve_local_file,
|
||||
@@ -232,6 +233,7 @@ def generate_pack(
|
||||
emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
required_only: bool = False,
|
||||
system_filter: list[str] | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for a platform.
|
||||
|
||||
@@ -248,7 +250,25 @@ def generate_pack(
|
||||
version = config.get("version", config.get("dat_version", ""))
|
||||
version_tag = f"_{version.replace(' ', '')}" if version else ""
|
||||
req_tag = "_Required" if required_only else ""
|
||||
zip_name = f"{platform_display.replace(' ', '_')}{version_tag}{req_tag}_BIOS_Pack.zip"
|
||||
|
||||
sys_tag = ""
|
||||
if system_filter:
|
||||
display_parts = []
|
||||
for sid in system_filter:
|
||||
s = sid.lower().replace("_", "-")
|
||||
for prefix in ("microsoft-", "nintendo-", "sony-", "sega-", "snk-",
|
||||
"panasonic-", "nec-", "epoch-", "mattel-", "fairchild-",
|
||||
"hartung-", "tiger-", "magnavox-", "philips-", "bandai-",
|
||||
"casio-", "coleco-", "commodore-", "sharp-", "sinclair-",
|
||||
"atari-"):
|
||||
if s.startswith(prefix):
|
||||
s = s[len(prefix):]
|
||||
break
|
||||
parts = s.split("-")
|
||||
display_parts.append("_".join(p.title() for p in parts if p))
|
||||
sys_tag = "_" + "_".join(display_parts)
|
||||
|
||||
zip_name = f"{platform_display.replace(' ', '_')}{version_tag}{req_tag}_BIOS_Pack{sys_tag}.zip"
|
||||
zip_path = os.path.join(output_dir, zip_name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
@@ -281,6 +301,18 @@ def generate_pack(
|
||||
platform_cores=plat_cores,
|
||||
)
|
||||
|
||||
if system_filter:
|
||||
from common import _norm_system_id
|
||||
norm_filter = {_norm_system_id(s) for s in system_filter}
|
||||
filtered = {sid: sys_data for sid, sys_data in pack_systems.items()
|
||||
if sid in system_filter or _norm_system_id(sid) in norm_filter}
|
||||
if not filtered:
|
||||
available = sorted(pack_systems.keys())[:10]
|
||||
print(f" WARNING: no systems matched filter {system_filter} "
|
||||
f"(available: {', '.join(available)})")
|
||||
return None
|
||||
pack_systems = filtered
|
||||
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
for file_entry in system.get("files", []):
|
||||
@@ -899,7 +931,10 @@ def main():
|
||||
list_emulator_profiles(args.emulators_dir)
|
||||
return
|
||||
if args.list_systems:
|
||||
list_system_ids(args.emulators_dir)
|
||||
if args.platform:
|
||||
list_platform_system_ids(args.platform, args.platforms_dir)
|
||||
else:
|
||||
list_system_ids(args.emulators_dir)
|
||||
return
|
||||
if args.list_targets:
|
||||
if not args.platform:
|
||||
@@ -914,18 +949,26 @@ def main():
|
||||
print(f" {t['name']:30s} {t['architecture']:10s} {t['core_count']:>4d} cores{aliases}")
|
||||
return
|
||||
|
||||
# Mutual exclusion
|
||||
modes = sum(1 for x in (args.platform, args.all, args.emulator, args.system) if x)
|
||||
if modes == 0:
|
||||
# Mode validation
|
||||
has_platform = bool(args.platform)
|
||||
has_all = args.all
|
||||
has_emulator = bool(args.emulator)
|
||||
has_system = bool(args.system)
|
||||
|
||||
# --platform/--all and --system can combine (system filters within platform)
|
||||
# --emulator is exclusive with everything else
|
||||
if has_emulator and (has_platform or has_all or has_system):
|
||||
parser.error("--emulator is mutually exclusive with --platform, --all, and --system")
|
||||
if has_platform and has_all:
|
||||
parser.error("--platform and --all are mutually exclusive")
|
||||
if not (has_platform or has_all or has_emulator or has_system):
|
||||
parser.error("Specify --platform, --all, --emulator, or --system")
|
||||
if modes > 1:
|
||||
parser.error("--platform, --all, --emulator, and --system are mutually exclusive")
|
||||
if args.standalone and not (args.emulator or args.system):
|
||||
parser.error("--standalone requires --emulator or --system")
|
||||
if args.target and not (args.platform or args.all):
|
||||
if args.standalone and not (has_emulator or (has_system and not has_platform and not has_all)):
|
||||
parser.error("--standalone requires --emulator or --system (without --platform)")
|
||||
if args.target and not (has_platform or has_all):
|
||||
parser.error("--target requires --platform or --all")
|
||||
if args.target and (args.emulator or args.system):
|
||||
parser.error("--target is incompatible with --emulator and --system")
|
||||
if args.target and has_emulator:
|
||||
parser.error("--target is incompatible with --emulator")
|
||||
|
||||
db = load_database(args.db)
|
||||
zip_contents = build_zip_contents_index(db)
|
||||
@@ -941,8 +984,8 @@ def main():
|
||||
sys.exit(1)
|
||||
return
|
||||
|
||||
# System mode
|
||||
if args.system:
|
||||
# System mode (standalone, without platform context)
|
||||
if has_system and not has_platform and not has_all:
|
||||
system_ids = [s.strip() for s in args.system.split(",") if s.strip()]
|
||||
result = generate_system_pack(
|
||||
system_ids, args.emulators_dir, db, args.bios_dir, args.output_dir,
|
||||
@@ -952,6 +995,10 @@ def main():
|
||||
sys.exit(1)
|
||||
return
|
||||
|
||||
system_filter = None
|
||||
if args.system:
|
||||
system_filter = [s.strip() for s in args.system.split(",") if s.strip()]
|
||||
|
||||
# Platform mode (existing)
|
||||
if args.all:
|
||||
platforms = list_registered_platforms(
|
||||
@@ -1016,6 +1063,7 @@ def main():
|
||||
zip_contents=zip_contents, data_registry=data_registry,
|
||||
emu_profiles=emu_profiles, target_cores=tc,
|
||||
required_only=args.required_only,
|
||||
system_filter=system_filter,
|
||||
)
|
||||
if zip_path and variants:
|
||||
rep_cfg = load_platform_config(representative, args.platforms_dir)
|
||||
|
||||
@@ -1602,5 +1602,84 @@ class TestE2E(unittest.TestCase):
|
||||
self.assertTrue(any("present_req.bin" in n for n in names))
|
||||
|
||||
|
||||
def test_132_platform_system_filter(self):
|
||||
"""--platform + --system filters systems within a platform pack."""
|
||||
from generate_pack import generate_pack
|
||||
output_dir = os.path.join(self.root, "pack_sysfilter")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
config = {
|
||||
"platform": "SysFilterTest",
|
||||
"verification_mode": "existence",
|
||||
"base_destination": "system",
|
||||
"systems": {
|
||||
"system-a": {
|
||||
"files": [
|
||||
{"name": "present_req.bin", "destination": "present_req.bin"},
|
||||
],
|
||||
},
|
||||
"system-b": {
|
||||
"files": [
|
||||
{"name": "present_opt.bin", "destination": "present_opt.bin"},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
with open(os.path.join(self.platforms_dir, "test_sysfilter.yml"), "w") as fh:
|
||||
yaml.dump(config, fh)
|
||||
zip_path = generate_pack(
|
||||
"test_sysfilter", self.platforms_dir, self.db, self.bios_dir, output_dir,
|
||||
system_filter=["system-a"],
|
||||
)
|
||||
self.assertIsNotNone(zip_path)
|
||||
with zipfile.ZipFile(zip_path) as zf:
|
||||
names = zf.namelist()
|
||||
self.assertTrue(any("present_req.bin" in n for n in names))
|
||||
self.assertFalse(any("present_opt.bin" in n for n in names))
|
||||
|
||||
def test_133_platform_system_filter_normalized(self):
|
||||
"""_norm_system_id normalization matches with manufacturer prefix."""
|
||||
from common import _norm_system_id
|
||||
self.assertEqual(
|
||||
_norm_system_id("sony-playstation"),
|
||||
_norm_system_id("playstation"),
|
||||
)
|
||||
|
||||
def test_134_list_systems_platform_context(self):
|
||||
"""list_platform_system_ids lists systems from a platform YAML."""
|
||||
from common import list_platform_system_ids
|
||||
import io
|
||||
config = {
|
||||
"platform": "ListSysTest",
|
||||
"verification_mode": "existence",
|
||||
"systems": {
|
||||
"alpha-sys": {
|
||||
"files": [
|
||||
{"name": "a.bin", "destination": "a.bin"},
|
||||
],
|
||||
},
|
||||
"beta-sys": {
|
||||
"files": [
|
||||
{"name": "b1.bin", "destination": "b1.bin"},
|
||||
{"name": "b2.bin", "destination": "b2.bin"},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
with open(os.path.join(self.platforms_dir, "test_listsys.yml"), "w") as fh:
|
||||
yaml.dump(config, fh)
|
||||
captured = io.StringIO()
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = captured
|
||||
try:
|
||||
list_platform_system_ids("test_listsys", self.platforms_dir)
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
output = captured.getvalue()
|
||||
self.assertIn("alpha-sys", output)
|
||||
self.assertIn("beta-sys", output)
|
||||
self.assertIn("1 file", output)
|
||||
self.assertIn("2 files", output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user