From 3ca746478d84991714df8709529334bb223fa774 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 21 Oct 2026 10:50:08 +0200 Subject: [PATCH] fix: keep only aliases the emulator opens Aliases equal to a name a platform launcher chooses were read in each emulator's code at its pin. Removed where the code opens no such name: xemu Complex_4627v1.03.bin (the core offers Complex_4627_v1.03.bin), mednafen gecard.pce, dosbox bios.rom, raze Roland_SC-55.sf2, nine DraStic names, and clk's upper-case MSX names, whose paths now read msx.rom, msx2.rom and msx2ext.rom as ROMCatalogue.cpp:777-841 names them. Kept aliases now name the code list they come from in the entry note. --- emulators/amiberry.yml | 4 +- emulators/beetle_pce.yml | 1 - emulators/beetle_psx.yml | 4 +- emulators/clk.yml | 9 +- emulators/dosbox-staging.yml | 1 - emulators/dosbox-x.yml | 1 - emulators/drastic.yml | 3 - emulators/gsplus.yml | 2 +- emulators/mednafen_pce.yml | 1 - emulators/mednafen_pce_fast.yml | 1 - emulators/openmsx.yml | 3 +- emulators/raze.yml | 1 - emulators/reicast.yml | 2 +- emulators/xemu.yml | 8 +- emulators/xroar.yml | 36 ++++---- tests/test_launcher_aliases.py | 153 ++++++++++++++++++++++++++++++++ wiki/testing-guide.md | 2 +- 17 files changed, 186 insertions(+), 46 deletions(-) create mode 100644 tests/test_launcher_aliases.py diff --git a/emulators/amiberry.yml b/emulators/amiberry.yml index 13cbb308..42d8d6c5 100644 --- a/emulators/amiberry.yml +++ b/emulators/amiberry.yml @@ -129,7 +129,7 @@ files: required: false size: 524288 aliases: [amiga-os-310-cd32.rom, kick40060.CD32] - note: "CD32 Kickstart v3.1 rev 40.60" + note: "CD32 Kickstart v3.1 rev 40.60. candidates_cd32 tries cd32.rom, amiga-os-310-cd32.rom, kick40060.CD32, kick31.rom, then kick.rom." source_ref: "source libretro/libretro.cpp:1811 (candidates_cd32)" - name: cd32ext.rom @@ -146,7 +146,7 @@ files: system: commodore-cdtv required: false aliases: [kick34005.CDTV] - note: "CDTV extended ROM v1.00, used as kickstart candidate" + note: "CDTV extended ROM v1.00, used as kickstart candidate. candidates_cdtv tries cdtv.rom, kick34005.CDTV, kick13.rom, then kick.rom." source_ref: "source libretro/libretro.cpp:1812 (candidates_cdtv)" - name: cdtvext.rom diff --git a/emulators/beetle_pce.yml b/emulators/beetle_pce.yml index 4d75588c..c85c06e9 100644 --- a/emulators/beetle_pce.yml +++ b/emulators/beetle_pce.yml @@ -55,5 +55,4 @@ files: description: "Games Express CD Card" required: false source_ref: "libretro.cpp:1057-1058, mednafen/settings.cpp:41, mednafen/pce/pce.cpp:374-411" - aliases: ["gecard.pce"] note: "pce_cdbios = 'Games Express' or auto-detected via DetectGECD()." diff --git a/emulators/beetle_psx.yml b/emulators/beetle_psx.yml index fb466937..d2df9ecf 100644 --- a/emulators/beetle_psx.yml +++ b/emulators/beetle_psx.yml @@ -110,7 +110,7 @@ files: mode: libretro source_ref: "libretro.c:523-529" aliases: ["PSXONPSP660.bin"] - note: "override_bios=1. Falls back to region BIOS if not found." + note: "override_bios=1. Tried as psxonpsp660.bin, PSXONPSP660.bin, then PSXONPSP660.BIN. Falls back to region BIOS if not found." - name: "ps1_rom.bin" description: "PS3 embedded PS1 BIOS, first 512KB extracted (region-free override)" @@ -121,7 +121,7 @@ files: mode: libretro source_ref: "libretro.c:531-537" aliases: ["PS1_ROM.bin"] - note: "override_bios=2. Falls back to region BIOS if not found." + note: "override_bios=2. Tried as ps1_rom.bin, PS1_ROM.bin, then PS1_ROM.BIN. Falls back to region BIOS if not found." - name: "openbios.bin" description: "OpenBIOS (open-source PS1 HLE BIOS, region-free override)" diff --git a/emulators/clk.yml b/emulators/clk.yml index 537db3fd..e38d61ff 100644 --- a/emulators/clk.yml +++ b/emulators/clk.yml @@ -730,8 +730,7 @@ files: # MSX (machine_name: "MSX") # ------------------------------------------------------- - name: msx.rom - aliases: [MSX.ROM] - path: MSX/MSX.ROM + path: MSX/msx.rom system: microsoft-msx description: "Generic MSX BIOS" size: 32768 @@ -778,8 +777,7 @@ files: source_ref: "Machines/Utility/ROMCatalogue.cpp:809-816" - name: msx2.rom - aliases: [MSX2.ROM] - path: MSX/MSX2.ROM + path: MSX/msx2.rom system: microsoft-msx2 description: "Generic MSX2 BIOS" size: 32768 @@ -789,8 +787,7 @@ files: source_ref: "Machines/Utility/ROMCatalogue.cpp:818-825" - name: msx2ext.rom - aliases: [MSX2EXT.ROM] - path: MSX/MSX2EXT.ROM + path: MSX/msx2ext.rom system: microsoft-msx2 description: "MSX2 extension ROM" size: 16384 diff --git a/emulators/dosbox-staging.yml b/emulators/dosbox-staging.yml index 6d387b36..3f63ba72 100644 --- a/emulators/dosbox-staging.yml +++ b/emulators/dosbox-staging.yml @@ -637,7 +637,6 @@ files: description: "IBM PCjr system ROM" sha1: "1f5f7013f18c08ff50d7942e76c4fbd782412414" required: false - aliases: ["bios.rom"] min_size: 57344 validation: [size] note: > diff --git a/emulators/dosbox-x.yml b/emulators/dosbox-x.yml index 5c443ace..f873c926 100644 --- a/emulators/dosbox-x.yml +++ b/emulators/dosbox-x.yml @@ -535,7 +535,6 @@ files: sha1: "1f5f7013f18c08ff50d7942e76c4fbd782412414" required: false system: ibm-pcjr - aliases: ["bios.rom"] min_size: 57344 validation: [size] note: > diff --git a/emulators/drastic.yml b/emulators/drastic.yml index 3c489e8d..93465e8f 100644 --- a/emulators/drastic.yml +++ b/emulators/drastic.yml @@ -131,7 +131,6 @@ notes: | files: - name: nds_bios_arm9.bin - aliases: [NDS_Bios9.bin, bios9.bin, biosnds9.rom] path: system/nds_bios_arm9.bin system: nintendo-ds required: false @@ -154,7 +153,6 @@ files: source_ref: "drastic r2.5.2.2 initialize_memory 0x1c4b0-0x1c4d4, libdrastic_arm64.so 0x2b6f0-0x2b708, DraSticActivity.smali:4084-4090, drastic_bios_readme.txt:1-31" - name: nds_bios_arm7.bin - aliases: [NDS_Bios7.bin, bios7.bin, biosnds7.rom] path: system/nds_bios_arm7.bin system: nintendo-ds required: false @@ -177,7 +175,6 @@ files: source_ref: "drastic r2.5.2.2 initialize_memory 0x1c508-0x1c52c, libdrastic_arm64.so 0x2b740-0x2b758, DraSticActivity.smali:4078-4083" - name: nds_firmware.bin - aliases: [NDS_Lite_Firmware.bin, dsfirmware.bin, firmware.bin] path: system/nds_firmware.bin system: nintendo-ds required: false diff --git a/emulators/gsplus.yml b/emulators/gsplus.yml index 1a351be4..cf6469ce 100644 --- a/emulators/gsplus.yml +++ b/emulators/gsplus.yml @@ -36,7 +36,7 @@ files: required: true description: "Apple IIGS system ROM" source_ref: "gsplus/src/config.c:136, gsplus/src/config.c:477-480, gsplus/src/config.c:1260-1317" - note: "Accepted sizes: 32768 (Apple //e), 131072 (ROM 01), 262144 (ROM 03)" + note: "g_kegs_rom_names tries the configured ROM path, then ROM, ROM.01, ROM.03, APPLE2GS.ROM, APPLE2GS.ROM2, xgs.rom, XGS.ROM, Rom03gd and 342-0077-b. Accepted sizes: 32768 (Apple //e), 131072 (ROM 01), 262144 (ROM 03)" size: [32768, 131072, 262144] validation: [size] diff --git a/emulators/mednafen_pce.yml b/emulators/mednafen_pce.yml index f1d8d0fc..153ee458 100644 --- a/emulators/mednafen_pce.yml +++ b/emulators/mednafen_pce.yml @@ -44,7 +44,6 @@ files: - name: "gexpress.pce" description: "Games Express CD Card" required: false - aliases: ["gecard.pce"] source_ref: "libretro.cpp:1057-1058, mednafen/settings.cpp:41, mednafen/pce/pce.cpp:373-411" note: "pce_cdbios = 'Games Express' or auto-detected via DetectGECD()." diff --git a/emulators/mednafen_pce_fast.yml b/emulators/mednafen_pce_fast.yml index f249c52b..d03a6565 100644 --- a/emulators/mednafen_pce_fast.yml +++ b/emulators/mednafen_pce_fast.yml @@ -42,7 +42,6 @@ files: - name: "gexpress.pce" description: "Games Express CD Card" required: false - aliases: ["gecard.pce"] source_ref: "libretro.c:1906-1907" note: "pce_fast_cdbios = 'Games Express'. For unlicensed Games Express titles." diff --git a/emulators/openmsx.yml b/emulators/openmsx.yml index 69bfc748..efa59f83 100644 --- a/emulators/openmsx.yml +++ b/emulators/openmsx.yml @@ -152,7 +152,8 @@ files: required: true description: "Kanji font ROM (shared by FS-A1WSX and FS-A1GT machines)" validation: [sha1] - source_ref: "share/machines/Boosted_MSX2+_JP.xml:28, share/machines/Boosted_MSXturboR_with_IDE.xml:216" + source_ref: "share/machines/Boosted_MSX2+_JP.xml:27-28, share/machines/Panasonic_FS-A1WSX.xml:29-30, share/machines/Boosted_MSXturboR_with_IDE.xml:215-216, share/machines/Panasonic_FS-A1GT.xml:141-142" + note: "Panasonic_FS-A1WSX.xml and Boosted_MSX2+_JP.xml name it fs-a1wsx_kanjifont.rom, Panasonic_FS-A1GT.xml and Boosted_MSXturboR_with_IDE.xml name it fs-a1gt_kanjifont.rom, all four with the same sha1." # ------------------------------------------------------- # Boosted_MSXturboR_with_IDE (msxturbor) diff --git a/emulators/raze.yml b/emulators/raze.yml index 4c804b20..338a101a 100644 --- a/emulators/raze.yml +++ b/emulators/raze.yml @@ -66,7 +66,6 @@ files: note: "I_FatalError(\"Cannot find raze.pk3\") if absent. Built from the tree at each release. The hash is the copy in the 1.11.0b Windows zip." - name: "raze.sf2" - aliases: ["Roland_SC-55.sf2"] category: game_data path: "soundfonts/raze.sf2" required: false diff --git a/emulators/reicast.yml b/emulators/reicast.yml index 66ebc0d0..db6c182e 100644 --- a/emulators/reicast.yml +++ b/emulators/reicast.yml @@ -61,4 +61,4 @@ files: validation: [size] description: "Dreamcast flash memory" source_ref: "core/hw/holly/sb_mem.cpp:22-23, 37-48, 58-64, core/types.h:212, 217" - note: "Written back as dc_nvmem.bin on exit. Missing, LoadRomFiles() fails even with a boot ROM present and the HLE BIOS runs." + note: "Tried as dc_nvmem.bin, dc_flash_wb.bin, dc_flash.bin, then dc_flash.bin.bin. Written back as dc_nvmem.bin on exit. Missing, LoadRomFiles() fails even with a boot ROM present and the HLE BIOS runs." diff --git a/emulators/xemu.yml b/emulators/xemu.yml index 020a3791..48aadd67 100644 --- a/emulators/xemu.yml +++ b/emulators/xemu.yml @@ -82,7 +82,7 @@ files: - name: Complex_4627.bin path: Complex_4627.bin - aliases: [Complex.bin, Complex_4627v1.03.bin, cerbios.bin] + aliases: [Complex.bin, cerbios.bin] required: true size: [262144, 524288, 1048576] validation: [size] @@ -98,12 +98,6 @@ files: Complex_4627_v1.03.bin, complex_4627.bin, cerbios.bin, Cerbios.bin, bios.bin, xbox_bios.bin and Complex.bin offered as alternatives. The core refuses to load when the chosen file is absent. - RetroDECK writes the BIOS folder's Complex.bin into flashrom_path of - the xemu.toml it prepares. ROCKNIX ships an xemu.toml whose - flashrom_path is bios/xemu/bios/Complex_4627v1.03.bin. Batocera writes - bios/cerbios.bin into flashrom_path for its chihiro system and - bios/Complex_4627.bin for xbox. EmuDeck writes - bios/Complex_4627v1.03.bin into flashrom_path. source_ref: standalone: "system/vl.c:3041-3053, hw/xbox/xbox.c:63-123" libretro: "ui/xemu-libretro.c:115-118, ui/xemu-libretro.c:195-196, ui/xemu-libretro.c:210-236" diff --git a/emulators/xroar.yml b/emulators/xroar.yml index 78924acb..38d7ff49 100644 --- a/emulators/xroar.yml +++ b/emulators/xroar.yml @@ -47,7 +47,7 @@ files: validation: [crc32] aliases: [dragon32, d32rom, "Dragon Data Ltd - Dragon 32 - IC17"] source_ref: "src/xroar.c:649 (romlist d32), src/dragon/dragon32.c:40 (default @dragon32), src/dragon/dragon32.c:107,110-116,131-132 (load + CRC)" - note: "Combined 16K ROM. Loaded into a single 16384-byte rombank slot." + note: "Combined 16K ROM. Loaded into a single 16384-byte rombank slot. Romlist d32 tries d32, dragon32, d32rom, Dragon Data Ltd - Dragon 32 - IC17." # ---- Dragon 64 ---- @@ -60,7 +60,7 @@ files: validation: [crc32] aliases: [d64rom1, d64rom1.rom, dragrom, "Dragon Data Ltd - Dragon 64 - IC17", "Dragon Data Ltd - TANO IC18", "Eurohard S.A. - Dragon 200 IC18"] source_ref: "src/xroar.c:647 (romlist d64_1), src/xroar.c:653 (romlist dragon64), src/dragon/dragon64.c:151-157,169-171 (load + CRC)" - note: "IC17 ROM. Required for the machine to start." + note: "IC17 ROM. Required for the machine to start. Romlist d64_1 tries d64_1, d64rom1, Dragon Data Ltd - Dragon 64 - IC17, Dragon Data Ltd - TANO IC18, Eurohard S.A. - Dragon 200 IC18, dragrom." - name: "d64_2.rom" system: dragon64 @@ -71,7 +71,7 @@ files: validation: [crc32] aliases: [d64rom2, "Dragon Data Ltd - Dragon 64 - IC18", "Dragon Data Ltd - TANO IC17", "Eurohard S.A. - Dragon 200 IC17"] source_ref: "src/xroar.c:648 (romlist d64_2), src/xroar.c:654 (romlist dragon64_alt), src/dragon/dragon64.c:160-166,174-176 (load + CRC)" - note: "IC18 ROM. 64K mode alternate BASIC. Machine starts without it (32K mode only)." + note: "IC18 ROM. 64K mode alternate BASIC. Machine starts without it (32K mode only). Romlist d64_2 tries d64_2, d64rom2, Dragon Data Ltd - Dragon 64 - IC18, Dragon Data Ltd - TANO IC17, Eurohard S.A. - Dragon 200 IC17." # ---- Dragon 200-E ---- @@ -85,7 +85,7 @@ files: validation: [crc32] aliases: [d200e_rom1, ic18_v1.4e.ic34] source_ref: "src/xroar.c:650 (romlist d200e_1), src/xroar.c:656 (romlist dragon200e), src/xroar.c:741 (crclist)" - note: "Required for Dragon 200-E. Falls back to d64_1 then generic dragon if not found." + note: "Required for Dragon 200-E. Falls back to d64_1 then generic dragon if not found. Romlist d200e_1 tries d200e_1, d200e_rom1, ic18_v1.4e.ic34." - name: "d200e_2.rom" system: dragon64 @@ -96,7 +96,7 @@ files: crc32: "48b985df" aliases: [d200e_rom2, ic17_v1.4e.ic37] source_ref: "src/xroar.c:651 (romlist d200e_2), src/xroar.c:657 (romlist dragon200e_alt), src/rom.c:91 (0x48b985df = Dragon 200-E 64K BASIC)" - note: "64K mode alternate. Falls back to d64_2 if not found." + note: "64K mode alternate. Falls back to d64_2 if not found. Romlist d200e_2 tries d200e_2, d200e_rom2, ic17_v1.4e.ic37." - name: "d200e_26.rom" system: dragon64 @@ -107,7 +107,7 @@ files: crc32: "565724bc" aliases: [rom26.ic1] source_ref: "src/xroar.c:658 (romlist dragon200e_charset), src/dragon/dragon.c:430-447 (ext_charset load), src/rom.c:92 (0x565724bc = Dragon 200-E Character Set)" - note: "Spanish character set. Loaded into a 4096-byte rombank. Machine works without it (uses standard charset)." + note: "Spanish character set. Loaded into a 4096-byte rombank. Machine works without it (uses standard charset). Romlist dragon200e_charset tries d200e_26, rom26.ic1." # ---- Dragon Professional (Alpha) ---- @@ -124,6 +124,7 @@ files: Required for Dragon Professional machine. Loaded as extbas_rom into the BOOT bank. rombank_verify_crc accepts any member of @dragonpro_boot, which also holds 0xd6172b56 for the v0.4 slot. + Romlist dragonpro_boot tries alpha-boot-v1.0, alpha_bt_10, Dragon Data Ltd - Dragon Alpha Boot v1.0 150584, alpha-boot-v0.4, alpha_bt_04. - name: "alpha-boot-v0.4.rom" system: dragon64 @@ -139,6 +140,7 @@ files: v1.0 image is found. The dragonpro_boot crclist carries 0xd6172b56 next to the v1.0 value, and the ROM metadata table names it Dragon Professional Boot 0.4. + Romlist dragonpro_boot tries alpha-boot-v1.0, alpha_bt_10, Dragon Data Ltd - Dragon Alpha Boot v1.0 150584, alpha-boot-v0.4, alpha_bt_04. - name: "alpha-basic.rom" system: dragon64 @@ -149,7 +151,7 @@ files: validation: [crc32] aliases: [alpha_ba, "Dragon Data Ltd - Dragon Alpga Basic"] source_ref: "src/xroar.c:661 (romlist dragonpro_basic), src/xroar.c:748 (crclist = @d64_1), src/dragon/dragonpro.c:247 (ROM0 bank 16384), src/dragon/dragonpro.c:260-263 (load), src/dragon/dragonpro.c:274 (0x84f68bf9 = Dragon 64 32K mode BASIC)" - note: "Loaded as altbas_rom into the 32K BASIC bank. Falls back to d64_1 if not found." + note: "Loaded as altbas_rom into the 32K BASIC bank. Falls back to d64_1 if not found. Romlist dragonpro_basic tries alpha-basic, alpha_ba, Dragon Data Ltd - Dragon Alpga Basic, @d64_1." # ---- Tandy CoCo (Colour BASIC) ---- @@ -172,7 +174,7 @@ files: validation: [crc32] aliases: ["Color Basic v1.2 (1982)(Tandy)"] source_ref: "src/xroar.c:663 (romlist coco), src/xroar.c:752 (crclist bas12)" - note: "Alternative to bas13. Second in fallback order." + note: "Alternative to bas13. Second in fallback order. Romlist coco tries bas13, bas12, Color Basic v1.2 (1982)(Tandy), bas11, bas10. Romlist coco2 tries bas12, @coco." - name: "bas11.rom" system: coco @@ -271,7 +273,7 @@ files: validation: [crc32] aliases: [adv070_u24] source_ref: "src/xroar.c:679 (romlist deluxecoco), src/xroar.c:763 (crclist), src/dragon/deluxecoco.c:213-216 (load)" - note: "Primary ROM for the Deluxe CoCo prototype. Loaded as extbas_rom." + note: "Primary ROM for the Deluxe CoCo prototype. Loaded as extbas_rom. Romlist deluxecoco tries adv070_u24, deluxe." - name: "adv071_u24.rom" system: coco @@ -291,6 +293,7 @@ files: required: false aliases: [adv073_u24] source_ref: "src/xroar.c:683 (romlist deluxecoco3), src/dragon/deluxecoco.c:237-240 (load)" + note: "Romlist deluxecoco3 tries adv073-2_u24, adv073_u24." # ---- Dynacom MX-1600 (Brazilian clone) ---- @@ -302,7 +305,7 @@ files: validation: [crc32] aliases: [mx1600bas_zephyr] source_ref: "src/xroar.c:685 (romlist mx1600), src/xroar.c:754 (crclist mx1600)" - note: "Required for MX-1600. Zephyr-patched variant (CRC32 d11b1c96) is an alias." + note: "Required for MX-1600. Zephyr-patched variant (CRC32 d11b1c96) is an alias. Romlist mx1600 tries mx1600bas, mx1600bas_zephyr." - name: "mx1600extbas.rom" system: coco @@ -381,7 +384,7 @@ files: crc32: "b44536f6" aliases: ["Dragon Data Ltd - DragonDOS 1.0"] source_ref: "src/xroar.c:708 (romlist dragondos), src/xroar.c:712 (romlist dragondos_compat), src/dragondos.c:173 (default @dragondos_compat), src/rom.c:131 (0xb44536f6 = DragonDOS 1.0)" - note: "Fifth name in the dragondos romlist, which @dragondos_compat tries after the dosplus and superdos lists." + note: "Fifth name in the dragondos romlist, which @dragondos_compat tries after the dosplus and superdos lists. Romlist dragondos tries ddos12a, ddos12, ddos40, ddos15, ddos10, Dragon Data Ltd - DragonDOS 1.0." - name: "dplus49b.rom" system: dragon64 @@ -400,7 +403,7 @@ files: crc32: "d4d954a0" aliases: [dosplus-4.8, DOSPLUS] source_ref: "src/xroar.c:709 (romlist dosplus), src/xroar.c:712 (romlist dragondos_compat), src/dragondos.c:173 (default @dragondos_compat), src/rom.c:137 (0xd4d954a0 = DOSplus 4.8)" - note: "Second name in the dosplus romlist, the first list @dragondos_compat tries." + note: "Second name in the dosplus romlist, the first list @dragondos_compat tries. Romlist dosplus tries dplus49b, dplus48, dosplus-4.8, DOSPLUS." - name: "sdose6.rom" system: dragon64 @@ -410,7 +413,7 @@ files: crc32: "8c1d6c45" aliases: ["PNP - SuperDOS E6"] source_ref: "src/xroar.c:710 (romlist superdos), src/rom.c:141 (0x8c1d6c45 = SuperDOS E6)" - note: "Latest SuperDOS. Older versions: sdose5, sdose4." + note: "Latest SuperDOS. Older versions: sdose5, sdose4. Romlist superdos tries sdose6, PNP - SuperDOS E6, sdose5, sdose4." - name: "cdos20.rom" system: dragon64 @@ -418,6 +421,7 @@ files: required: false aliases: [CDOS20, "CumanaDOSv2.0"] source_ref: "src/xroar.c:711 (romlist cumana)" + note: "Romlist cumana tries cdos20, CDOS20, CumanaDOSv2.0." - name: "delta2.rom" system: dragon64 @@ -427,7 +431,7 @@ files: crc32: "307fb37c" aliases: ["Premier Micros - DeltaDOS"] source_ref: "src/xroar.c:717 (romlist delta), src/deltados.c:160 (default @delta), src/rom.c:146 (0x307fb37c = Delta System 2.0)" - note: "Latest Delta version. Older versions: delta1a, delta19, delta, deltados." + note: "Latest Delta version. Older versions: delta1a, delta19, delta, deltados. Romlist delta tries delta2, delta1a, delta19, delta, deltados, Premier Micros - DeltaDOS." - name: "cp450dsk.rom" system: coco @@ -453,7 +457,7 @@ files: required: false aliases: [MCX_Basic_21] source_ref: "src/xroar.c:730 (romlist mcx128), src/mcx128.c:208 (default @mcx128)" - note: "128K memory expansion for MC-10. Adds enhanced BASIC." + note: "128K memory expansion for MC-10. Adds enhanced BASIC. Romlist mcx128 tries mcx128, MCX_Basic_21." - name: "mcx128a.rom" system: mc10 @@ -461,7 +465,7 @@ files: required: false aliases: [MCX_Basic_21_AZERTY] source_ref: "src/xroar.c:731 (romlist mcx128_azerty), src/xroar.c:638-639 (cart mcx128a)" - note: "AZERTY keyboard variant for Alice. Same MCX128 hardware." + note: "AZERTY keyboard variant for Alice. Same MCX128 hardware. Romlist mcx128_azerty tries mcx128a, MCX_Basic_21_AZERTY." - name: "yados.rom" system: coco diff --git a/tests/test_launcher_aliases.py b/tests/test_launcher_aliases.py new file mode 100644 index 00000000..cfb0abf9 --- /dev/null +++ b/tests/test_launcher_aliases.py @@ -0,0 +1,153 @@ +"""A profile alias is a name the emulator's own code opens. + +Platform launchers pick names of their own: Batocera passes cerbios.bin to +xemu, ROCKNIX and EmuDeck write Complex_4627v1.03.bin into xemu.toml. Those +names live in the platform's ``launchers:`` layer. Copied into a profile as +aliases, they claimed that xemu opens them, and xemu-libretro offers +Complex_4627_v1.03.bin, not Complex_4627v1.03.bin (ui/xemu-libretro.c:115-118). + +Two checks read the committed data: + +- an alias equal to a name a launcher of the same emulator chooses is kept + only when the entry's own note or description names it in a sentence that + speaks of no platform, next to a ``source_ref`` into the emulator's code; +- no entry cites a platform's launcher script as its source. +""" + +from __future__ import annotations + +import re +import unittest +from collections import defaultdict +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parent.parent +PLATFORMS = REPO_ROOT / "platforms" +EMULATORS = REPO_ROOT / "emulators" + +# Words that name a platform in prose. MiSTer's registry key carries its +# hardware suffix, the prose says MiSTer. +PLATFORM_WORDS = ( + "retroarch", "batocera", "recalbox", "retrobat", "emudeck", "lakka", + "retrodeck", "romm", "rocknix", "retropie", "bizhawk", "mister", +) + +# Launcher sources: configgen and emulatorLauncher generators, EmuDeck and +# RetroPie scripts, a ref annotated as a launcher. +LAUNCHER_REF = re.compile( + r"Generator\.(?:py|cs)\b|\bconfiggen\b|\bemulatorLauncher\b|" + r"\bemuDeck\w*\.sh\b|\bscriptmodules/|\(launcher\)", +) + +# Profiles whose entries still rest on a launcher, with the fact that blocks +# reading the emulator itself. +LAUNCHER_SOURCED = { + "lexaloffle": "PICO-8 and Voxatron are sold binaries, and no copy of either is held to read", +} + +_SENTENCE = re.compile(r"(?<=[.!?])\s+(?=[A-Z0-9(\"'])") + + +def _profiles() -> dict[str, dict]: + return { + path.stem: yaml.safe_load(path.read_text(encoding="utf-8")) or {} + for path in sorted(EMULATORS.glob("*.yml")) + } + + +def _launcher_names() -> dict[str, set[tuple[str, str]]]: + """Folded file name -> (platform, emulator) of each launcher choosing it.""" + names: dict[str, set[tuple[str, str]]] = defaultdict(set) + for path in sorted(PLATFORMS.glob("*.yml")): + config = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + for record in config.get("launchers") or []: + for name in record.get("names") or []: + base = str(name).rsplit("/", 1)[-1].casefold() + names[base].add((path.stem, str(record.get("emulator", "")))) + return names + + +def _text(value: object) -> str: + if isinstance(value, dict): + return " ".join(_text(v) for v in value.values()) + if isinstance(value, list): + return " ".join(_text(v) for v in value) + return str(value or "") + + +def _named_without_platform(alias: str, prose: str) -> bool: + for sentence in _SENTENCE.split(" ".join(prose.split())): + if alias not in sentence: + continue + folded = sentence.casefold() + if not any(re.search(rf"\b{word}", folded) for word in PLATFORM_WORDS): + return True + return False + + +class ProfileAliasesAreCodeNames(unittest.TestCase): + def test_alias_a_launcher_chooses_is_named_by_the_code(self) -> None: + launchers = _launcher_names() + offenders = [] + for key, profile in _profiles().items(): + emulator_names = {key} | {str(c) for c in profile.get("cores") or []} + for entry in profile.get("files") or []: + prose = _text(entry.get("note")) + " " + _text(entry.get("description")) + for alias in entry.get("aliases") or []: + alias = str(alias) + chosen_by = { + platform + for platform, emulator in launchers.get( + alias.rsplit("/", 1)[-1].casefold(), () + ) + if emulator in emulator_names + } + if not chosen_by: + continue + if entry.get("source_ref") and _named_without_platform(alias, prose): + continue + offenders.append( + f"{key}: {entry.get('name')} alias {alias} " + f"(launchers: {', '.join(sorted(chosen_by))})" + ) + self.assertEqual( + offenders, [], + "an alias a platform launcher chooses must be named by the emulator's " + "code, stated in the entry's note with its source_ref, or removed", + ) + + def test_check_catches_a_launcher_name(self) -> None: + self.assertFalse(_named_without_platform( + "cerbios.bin", "Batocera writes bios/cerbios.bin into flashrom_path.", + )) + self.assertTrue(_named_without_platform( + "cerbios.bin", "The core option offers cerbios.bin. Batocera writes it.", + )) + + +class EntriesCiteTheEmulator(unittest.TestCase): + def test_no_source_ref_is_a_launcher_script(self) -> None: + offenders = [] + for key, profile in _profiles().items(): + if key in LAUNCHER_SOURCED: + continue + for entry in profile.get("files") or []: + ref = _text(entry.get("source_ref")) + if LAUNCHER_REF.search(ref): + offenders.append(f"{key}: {entry.get('name')}: {ref}") + self.assertEqual(offenders, []) + + def test_known_exceptions_still_need_it(self) -> None: + profiles = _profiles() + for key in LAUNCHER_SOURCED: + refs = [_text(e.get("source_ref")) for e in profiles[key].get("files") or []] + self.assertTrue( + any(LAUNCHER_REF.search(r) for r in refs), + f"{key} no longer cites a launcher: drop it from LAUNCHER_SOURCED", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/wiki/testing-guide.md b/wiki/testing-guide.md index 30abcf46..0b71e40d 100644 --- a/wiki/testing-guide.md +++ b/wiki/testing-guide.md @@ -39,7 +39,7 @@ Every module in `tests/`, by the area it guards. |------|----------|---------| | Core regression suites | synthetic | `test_e2e`, `test_audit_regressions`, `test_tool_messages` | | File resolution and identity | synthetic + collection | `test_adler32_resolution`, `test_agnostic_scan`, `test_agnostic_scan_mode`, `test_agnostic_scope`, `test_agnostic_substitute`, `test_archive_choice`, `test_bios_companions`, `test_bundled_identity`, `test_casefold_cache`, `test_db_aliases`, `test_declared_names`, `test_derivations`, `test_directory_entries`, `test_extra_identity`, `test_foreign_tree`, `test_foreign_tree_names`, `test_frontend_digest`, `test_game_data_homonyms`, `test_hashing`, `test_identity`, `test_md5_pack_context`, `test_name_tiebreak`, `test_nec_filing`, `test_owner_resolution`, `test_path_suffix`, `test_resolution_evidence`, `test_shipped_content`, `test_twin_declarations`, `test_validated_choice`, `test_variant_choice`, `test_variant_identity`, `test_zipped_member` | -| Verification, cross-reference and profile checks | synthetic + profiles | `test_crypto_aes`, `test_crypto_unique_data`, `test_crypto_verify`, `test_case_insensitive_platforms`, `test_checks_have_values`, `test_core_file_fills_declaration`, `test_cross_reference_sources`, `test_file_consumers`, `test_fileless_audit`, `test_gap_settlement`, `test_hidden_extras`, `test_launcher_layer`, `test_native_mode`, `test_no_case_collisions`, `test_path_conflict_status`, `test_path_null_copies`, `test_profile_checks`, `test_romm_hash_rule`, `test_shared_groups`, `test_standalone_mode`, `test_undeclared_dedup`, `test_unplaced_extras`, `test_validation_scope`, `test_verify_all_dir`, `test_verify_emulator_evidence`, `test_verify_listing`, `test_verify_pack_choice` | +| Verification, cross-reference and profile checks | synthetic + profiles | `test_crypto_aes`, `test_crypto_unique_data`, `test_crypto_verify`, `test_case_insensitive_platforms`, `test_checks_have_values`, `test_core_file_fills_declaration`, `test_cross_reference_sources`, `test_file_consumers`, `test_fileless_audit`, `test_gap_settlement`, `test_hidden_extras`, `test_launcher_aliases`, `test_launcher_layer`, `test_native_mode`, `test_no_case_collisions`, `test_path_conflict_status`, `test_path_null_copies`, `test_profile_checks`, `test_romm_hash_rule`, `test_shared_groups`, `test_standalone_mode`, `test_undeclared_dedup`, `test_unplaced_extras`, `test_validation_scope`, `test_verify_all_dir`, `test_verify_emulator_evidence`, `test_verify_listing`, `test_verify_pack_choice` | | Packs and manifests | synthetic + real packs | `test_artifact_lock`, `test_atomic_artifacts`, `test_batocera_cores`, `test_deterministic_zip`, `test_emulator_pack_counts`, `test_finalize_pack_names`, `test_large_file_cache`, `test_libretro_layout`, `test_manifest_entry`, `test_pack_conformance_dirs`, `test_pack_core_extras`, `test_pack_counts`, `test_pack_destinations`, `test_pack_failures`, `test_pack_file_names`, `test_pack_grouping`, `test_pack_integrity`, `test_pack_member_identity`, `test_pack_readme`, `test_platform_inheritance`, `test_platform_keys`, `test_required_only_archives`, `test_required_only_twins`, `test_split_extras`, `test_split_names`, `test_split_pack`, `test_system_pack_extras`, `test_system_pack_parity`, `test_torrentzip`, `test_which_pack` | | Slots and regions | synthetic + profiles | `test_region`, `test_region_agnostic_scan`, `test_region_groups`, `test_region_held`, `test_region_platform_index`, `test_region_required_only`, `test_slot`, `test_slots`, `test_slots_shipped_file` | | Hardware targets and systems | synthetic + target files | `test_extract_targets`, `test_lakka_targets`, `test_libretro_slugs`, `test_system_ids`, `test_system_selection`, `test_target_names`, `test_target_scrapers`, `test_target_systems`, `test_target_verify` |