fix: round 2 audit fixes, updated emulator profiles

Scripts:
- fix generate_site nav regex destroying mkdocs.yml content
- fix auto_fetch comma-separated MD5 in find_missing
- fix verify print_platform_result conflating untested/missing
- fix validate_pr path traversal and symlink check
- fix batocera_scraper brace counting and escaped quotes in strings
- fix emudeck_scraper hash search crossing function boundaries
- fix pipeline.py cwd to repo root via Path(__file__)
- normalize SHA1 comparison to lowercase in generate_pack

Emulator profiles:
- emux_gb/nes/sms: reclassify from alias to standalone profiles
- ep128emu: remove .info-only files not referenced in source
- fbalpha2012 variants: full source-verified profiles
- fbneo_cps12: add new profile
This commit is contained in:
Abdessamad Derraz
2026-03-19 15:00:18 +01:00
parent 38d605c7d5
commit 257ec1a527
19 changed files with 483 additions and 82 deletions

View File

@@ -1,5 +1,5 @@
{ {
"generated_at": "2026-03-19T12:13:36Z", "generated_at": "2026-03-19T13:30:04Z",
"total_files": 5593, "total_files": 5593,
"total_size": 4909044289, "total_size": 4909044289,
"files": { "files": {

View File

@@ -1,8 +1,54 @@
emulator: "emux_gb" emulator: "emux (Game Boy)"
type: alias type: libretro
alias_of: "gambatte" source: "https://github.com/libretro/emux"
profiled_date: "2026-03-18" profiled_date: "2026-03-19"
core_version: "0.1" core_version: "0.1"
display_name: "Nintendo - Game Boy / Color (Emux GB)" display_name: "Nintendo - Game Boy / Color (Emux GB)"
note: "This core uses the same BIOS/firmware as gambatte. See emulators/gambatte.yml for details." cores:
files: [] - emux_gb
systems:
- nintendo-gb
notes: |
emux is a multi-system emulator by Sebastien Ronsse, supporting CHIP-8,
Game Boy, NES, and Sega Master System as separate libretro cores.
NOT an alias of Gambatte. Completely different codebase and BIOS requirements.
Gambatte uses gb_bios.bin/gbc_bios.bin (optional, HLE fallback).
emux_gb uses dmg_boot.bin (required, no HLE, core fails without it).
Boot ROM loading in controllers/mapper/gb_mapper.c:
static char *bootrom_path = "dmg_boot.bin"; (line 32)
gb_mapper->bootrom = file_map(PATH_SYSTEM, bootrom_path, 0, BOOTROM_SIZE);
If file_map returns NULL, gb_mapper_init returns false — core cannot start.
No HLE fallback, no skip logic.
Boot ROM is 256 bytes (BOOTROM_SIZE in gb_mapper.h:10), mapped at 0x0000-0x00FF.
When register 0xFF50 (BOOT_LOCK) is written with non-zero, the bootrom region
is removed from the memory map (lock_writeb, gb_mapper.c:130-138).
Memory map from mach/gb.c:
0x0000-0x00FF Boot ROM (overlays ROM0 until BOOT_LOCK)
0x0000-0x3FFF ROM bank 0 (16 KB)
0x4000-0x7FFF ROM bank 1 (switchable, 16 KB)
0x8000-0x9FFF VRAM (8 KB)
0xA000-0xBFFF External RAM (8 KB)
0xC000-0xDFFF WRAM (8 KB)
0xFE00-0xFE9F OAM (160 bytes)
0xFF80-0xFFFE HRAM (127 bytes)
The .info declares firmware_count=1, firmware0_opt=false.
is_experimental=true. Core does not support save states, rewind, or netplay.
system_directory obtained via RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY
(libretro/libretro.c:23-25), passed as "system-dir" config param.
files:
- name: "dmg_boot.bin"
system: nintendo-gb
description: "Game Boy (DMG) boot ROM"
required: true
size: 256
md5: "32fbbd84168d3482956eb3c5051637f5"
source_ref: "controllers/mapper/gb_mapper.c:32 (bootrom_path), gb_mapper.h:10 (BOOTROM_SIZE=256)"
note: "Mapped at 0x0000-0x00FF, overlays ROM0 until BOOT_LOCK (0xFF50) written. No HLE — core fails to init without this file."

View File

@@ -1,8 +1,51 @@
emulator: "emux_nes" emulator: "emux (NES)"
type: alias type: libretro
alias_of: "fceumm" source: "https://github.com/libretro/emux"
profiled_date: "2026-03-18" profiled_date: "2026-03-19"
core_version: "0.1" core_version: "0.1"
display_name: "Nintendo - NES / Famicom (Emux NES)" display_name: "Nintendo - NES / Famicom (Emux NES)"
note: "This core uses the same BIOS/firmware as fceumm. See emulators/fceumm.yml for details." cores:
- emux_nes
systems:
- nes
notes: |
emux is a multi-system emulator by Sebastien Ronsse, supporting CHIP-8,
Game Boy, NES, and Sega Master System as separate libretro cores.
NOT an alias of FCEUmm. Completely different codebase.
The NES core does not require any BIOS or firmware files.
The .info declares no firmware_count.
The NES has no boot ROM. CPU starts at the reset vector address
stored at 0xFFFC-0xFFFD (cpu/rp2a03.c RESET_VECTOR).
Cartridge loading in controllers/mapper/nes_mapper.c:
- iNES header parsed (magic 0x1A53454E)
- Mapper number extracted from flags6/flags7
- Supported mappers: NROM (0), MMC1 (1), MMC3 (4)
- PRG-ROM and CHR-ROM mapped via file_map(PATH_DATA, ...)
Memory map from mach/nes.c:
CPU bus:
0x0000-0x07FF WRAM (2 KB, mirrored to 0x1FFF)
0x2000-0x2007 PPU registers (mirrored to 0x3FFF)
0x4000-0x4013 APU registers
0x4014 Sprite DMA
0x4016-0x4017 Controller ports
0x6000-0x7FFF SRAM
0x8000-0xFFFF PRG-ROM
PPU bus:
0x0000-0x1FFF CHR-ROM/RAM (pattern tables)
0x2000-0x2FFF VRAM (nametables, 4 KB)
0x3F00-0x3F1F Palette RAM
system_directory obtained via RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY
(libretro/libretro.c:23-25) but not used for any file loading.
is_experimental=true. No save states, rewind, or netplay support.
Supported extensions: nes, bin, rom.
files: [] files: []
# No BIOS, firmware, or system files of any kind.

View File

@@ -1,8 +1,55 @@
emulator: "emux_sms" emulator: "emux (SMS)"
type: alias type: libretro
alias_of: "gearsystem" source: "https://github.com/libretro/emux"
profiled_date: "2026-03-18" profiled_date: "2026-03-19"
core_version: "0.1" core_version: "0.1"
display_name: "Sega - Master System (Emux SMS)" display_name: "Sega - Master System (Emux SMS)"
note: "This core uses the same BIOS/firmware as gearsystem. See emulators/gearsystem.yml for details." cores:
files: [] - emux_sms
systems:
- sms
notes: |
emux is a multi-system emulator by Sebastien Ronsse, supporting CHIP-8,
Game Boy, NES, and Sega Master System as separate libretro cores.
NOT an alias of GearSystem. Completely different codebase.
BIOS loading in controllers/mapper/sms_mapper.c:
static char *bios_path = "bios.sms"; (line 33)
#define BIOS_SIZE 0x2000 (line 7, 8192 bytes)
sms_mapper->bios = file_map(PATH_SYSTEM, bios_path, 0, BIOS_SIZE);
If file_map returns NULL, init returns false — core cannot start.
No HLE fallback, no skip logic.
BIOS enable/disable via slot control register port write (lines 74-90):
bios_disable bit in control union. Reset clears bios_disable and
adds BIOS region to memory map. Writing bios_disable=1 removes
the BIOS overlay, exposing cartridge ROM underneath.
Cartridge loading delegated to sega_mapper controller:
ROM mapped via file_map(PATH_DATA, ...), 16 KB banks (BANK_SIZE=0x4000),
3 bank slots (NUM_BANKS=3), bank select registers at 0xFFFD-0xFFFF.
Memory map from mach/sms.c:
0x0000-0xBFFF Mapper (BIOS overlay + cartridge ROM banks)
0xC000-0xDFFF RAM (8 KB)
0xE000-0xFFFF RAM mirror
The .info declares firmware_count=1, firmware0_opt=false.
is_experimental=true. No save states, rewind, or netplay support.
Supported extensions: sms, bms, bin, rom.
system_directory obtained via RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY
(libretro/libretro.c:23-25), passed as "system-dir" config param.
BIOS loaded from this directory via PATH_SYSTEM.
files:
- name: "bios.sms"
system: sms
description: "Sega Master System BIOS"
required: true
size: 8192
md5: "840481177270d5642a14ca71ee72844c"
source_ref: "controllers/mapper/sms_mapper.c:7 (BIOS_SIZE=0x2000), :33 (bios_path), :167-172 (file_map)"
note: "Mapped at 0x0000, overlays cartridge ROM until bios_disable bit set in slot control register. No HLE — core fails to init without this file."

View File

@@ -135,12 +135,14 @@ files:
- name: exdos14isdos10uk.rom - name: exdos14isdos10uk.rom
path: ep128emu/roms/exdos14isdos10uk.rom path: ep128emu/roms/exdos14isdos10uk.rom
md5: f91c4a507cc6895bdd9c43df4f021df3
required: false required: false
hle_fallback: true hle_fallback: true
system: enterprise-128 system: enterprise-128
has_builtin: true has_builtin: true
size: 32768
note: "EXDOS 1.4 + IS-DOS 1.0 (UK). Used for IS-DOS disk mode instead of exdos13.rom." note: "EXDOS 1.4 + IS-DOS 1.0 (UK). Used for IS-DOS disk mode instead of exdos13.rom."
source_ref: "core/core.cpp:295-298" source_ref: "core/core.cpp:253, roms/roms.hpp:30104 (built-in data, MD5 verified)"
# ============================================================ # ============================================================
# Enterprise 64/128 - file I/O and extensions # Enterprise 64/128 - file I/O and extensions
@@ -183,12 +185,14 @@ files:
- name: epdos16f.rom - name: epdos16f.rom
path: ep128emu/roms/epdos16f.rom path: ep128emu/roms/epdos16f.rom
md5: 6593dff00ab32a4b1fc084674ededf2b
required: false required: false
hle_fallback: true hle_fallback: true
system: enterprise-128 system: enterprise-128
has_builtin: true has_builtin: true
size: 32768
note: "EP-DOS 1.6f. Provides HFONT and CLKOFF for Hungarian locale. 32K across segments 0x06-0x07." note: "EP-DOS 1.6f. Provides HFONT and CLKOFF for Hungarian locale. 32K across segments 0x06-0x07."
source_ref: "core/core.cpp:252-255" source_ref: "core/core.cpp:236-237, roms/roms.hpp:43780 (built-in data, MD5 verified)"
- name: brd.rom - name: brd.rom
path: ep128emu/roms/brd.rom path: ep128emu/roms/brd.rom
@@ -200,25 +204,9 @@ files:
note: "German (BRD) language extension. Auto-loaded when content locale is German." note: "German (BRD) language extension. Auto-loaded when content locale is German."
source_ref: "core/core.cpp:260-261" source_ref: "core/core.cpp:260-261"
# ============================================================ # epd19hft.rom and zt19hfnt.rom: listed in .info (firmware8, firmware9) but
# Enterprise 64/128 - optional extras (in .info, not in source) # NOT referenced in core source code and NOT in builtin_rom map (roms.hpp:46516-46538).
# ============================================================ # The core never loads these files. Excluded from profile to avoid misleading users.
- name: epd19hft.rom
path: ep128emu/roms/epd19hft.rom
md5: 12cfc9c7e48c8a16c2e09edbd926d467
required: false
system: enterprise-128
note: "EP-DOS 1.9 with Hungarian font. Listed in .info file as optional firmware."
source_ref: "ep128emu_core_libretro.info:firmware8"
- name: zt19hfnt.rom
path: ep128emu/roms/zt19hfnt.rom
md5: 653daaf7b9b29c2c4e577f489580f247
required: false
system: enterprise-128
note: "ZozoTools 1.9 with Hungarian font. Listed in .info file as optional firmware."
source_ref: "ep128emu_core_libretro.info:firmware9"
# ============================================================ # ============================================================
# Videoton TVC # Videoton TVC

View File

@@ -1,8 +1,56 @@
emulator: "fbalpha2012" emulator: "FB Alpha 2012"
type: alias type: frozen_snapshot
alias_of: "fbneo" source: "https://github.com/libretro/fbalpha2012"
profiled_date: "2026-03-18" upstream: "https://www.fbalpha.com"
profiled_date: "2026-03-19"
core_version: "v0.2.97.29" core_version: "v0.2.97.29"
display_name: "Arcade (FB Alpha 2012)" display_name: "Arcade (FB Alpha 2012)"
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details." cores:
- fbalpha2012
systems:
- arcade
- neogeo
- cps1
- cps2
- cps3
- pgm
notes: |
Frozen snapshot of Final Burn Alpha v0.2.97.29 (circa 2012), full version.
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
Supports CPS-1, CPS-2, CPS-3, Neo Geo, PGM, Sega, Cave, Toaplan, and more.
Exists for RAM-constrained platforms. Most users should use FBNeo.
Multiple arcade systems have BIOS ROMs marked BRF_BIOS in source:
Neo Geo (d_neogeo.cpp:923-1011): neogeo.zip ROM set contains ~35 BIOS variants
- 68K BIOS: asia-s3.rom (default), sp-s2.sp1, sp-s.sp1, sp-u2.sp1, sp-e.sp1,
vs-bios.rom, sp-j2.sp1, sp1.jipan.1024, sp-45.sp1, japan-j3.bin, neo-po.bin,
neo-epo.bin, neodebug.bin, sp-1v1_3db8c.bin, 16 Universe BIOS variants,
neopen.sp1, plus PCB/CD/trackball BIOS variants
- Z80 BIOS: sm1.sm1 (sound program)
- Graphics: sfix.sfix (text layer tiles)
- Data: 000-lo.lo (zoom table)
- Neo Geo CD: neocd.bin
PGM (d_pgm.cpp:303-379): per-game BIOS sets in each ROM ZIP
- pgm_t01s.rom (text tiles), pgm_m01s.rom (samples),
pgm_p01s.u20/pgm_p02s.u20 (68K BIOS v1/v2),
ddp3_bios.u37, bios.u42, svg_bios.u49 (custom per-game)
CPS-3 (d_cps3.cpp): 18 SH-2 BIOS variants, 512 KB each, region-specific
(see fbalpha2012_cps3.yml for full list)
Sega System 16B (d_sys16b.cpp:8900): ism2006v00.u1
All BIOS files are part of their respective game ROM set ZIPs (arcade standard).
None are standalone system directory files. The .info note "BIOS files must be
inside the ROM directory" refers to the ROM directory containing the game ZIPs,
which already include their BIOS — no separate action needed by the user.
ROM path: g_rom_dir from content path. g_system_dir set but unused.
Extensions: iso|zip|7z. need_fullpath=true, block_extract=true.
files: [] files: []
exclusion_note: "All BIOS ROMs (Neo Geo, PGM, CPS-3, Sega) are inside game ROM set ZIPs, not standalone system files. neogeo.zip is a parent ROM set loaded automatically by the emulator when a Neo Geo game is started."

View File

@@ -1,8 +1,39 @@
emulator: "fbalpha2012_cps1" emulator: "FB Alpha 2012 CPS-1"
type: alias type: frozen_snapshot
alias_of: "fbneo" source: "https://github.com/libretro/fbalpha2012_cps1"
profiled_date: "2026-03-18" upstream: "https://www.fbalpha.com"
profiled_date: "2026-03-19"
core_version: "v0.2.97.28" core_version: "v0.2.97.28"
display_name: "Arcade (FB Alpha 2012 CPS-1)" display_name: "Arcade (FB Alpha 2012 CPS-1)"
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details." cores:
- fbalpha2012_cps1
systems:
- cps1
notes: |
Frozen snapshot of Final Burn Alpha v0.2.97.28 (circa 2012), CPS-1 only.
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
Exists for RAM-constrained platforms (3DS, embedded). Most users should use FBNeo.
CPS-1 has no system BIOS. Each game's ROM set is self-contained:
68000 program ROMs + Z80 sound program + GFX ROMs + (optional) QSound samples.
QSound games (Cadillacs & Dinosaurs, etc.) include audio samples directly in
their ROM set (cd-q1.1k through cd-q4.4k), not as a separate DSP BIOS.
Board PLDs (buf1, ioa1, prg1, rom1, sou1) are listed in ROM definitions as
BRF_OPT (optional). These are GAL/PAL dumps for hardware preservation —
never loaded by the emulator.
ROM path: ROMs are loaded from the content directory (g_rom_dir), not system
directory. system_directory is obtained (libretro.cpp:1645) but never used.
The .info note "BIOS files must be inside the ROM directory" is misleading —
there are no BIOS files for CPS-1. This note is generic across all FBA cores.
244 ROM sets defined (488 STD_ROM_PICK/FN entries = 244 games).
is_experimental=false, need_fullpath=true, block_extract=true.
files: [] files: []
# CPS-1 has no BIOS. All required data is in per-game ROM sets (ZIP archives).
exclusion_note: "CPS-1 arcade hardware has no system BIOS. Each game ROM set is self-contained."

View File

@@ -1,8 +1,42 @@
emulator: "fbalpha2012_cps2" emulator: "FB Alpha 2012 CPS-2"
type: alias type: frozen_snapshot
alias_of: "fbneo" source: "https://github.com/libretro/fbalpha2012_cps2"
profiled_date: "2026-03-18" upstream: "https://www.fbalpha.com"
profiled_date: "2026-03-19"
core_version: "v0.2.97.28" core_version: "v0.2.97.28"
display_name: "Arcade (FB Alpha 2012 CPS-2)" display_name: "Arcade (FB Alpha 2012 CPS-2)"
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details." cores:
- fbalpha2012_cps2
systems:
- cps2
notes: |
Frozen snapshot of Final Burn Alpha v0.2.97.28 (circa 2012), CPS-2 only.
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
Exists for RAM-constrained platforms (3DS, embedded). Most users should use FBNeo.
CPS-2 has no system BIOS. Each game's ROM set is self-contained:
68000 program ROMs + Z80 sound program + GFX ROMs + QSound samples.
QSound is emulated in software (qs_c.c:QscInit, panning table + DSP emulation).
No external QSound DSP ROM (dl-1425.bin) needed — unlike MAME, FBA emulates
the QSound chip directly without requiring the DSP program ROM.
Z80 sound program loaded from game ROM set into CpsZRom buffer (cps.c:451,
BurnLoadRom). QSound samples loaded into CpsQSam. All from the game ZIP.
Board PLDs (buf1, ioa1, prg2, rom1) referenced via A_BOARD_QSOUND_PLDS
macro, all marked BRF_OPT. GAL/PAL dumps for hardware preservation only.
Zero BRF_BIOS entries in entire codebase.
ROM path: ROMs loaded from content directory (g_rom_dir, libretro.cpp).
g_system_dir is set (line 988) but never used for any file loading.
The .info note "BIOS files must be inside the ROM directory" is misleading —
there are no BIOS files for CPS-2. Generic note across all FBA cores.
need_fullpath=true, block_extract=true, extensions=zip.
files: [] files: []
exclusion_note: "CPS-2 arcade hardware has no system BIOS. QSound DSP emulated in software (qs_c.c). Each game ROM set is self-contained."

View File

@@ -1,8 +1,55 @@
emulator: "fbalpha2012_cps3" emulator: "FB Alpha 2012 CPS-3"
type: alias type: frozen_snapshot
alias_of: "fbneo" source: "https://github.com/libretro/fbalpha2012_cps3"
profiled_date: "2026-03-18" upstream: "https://www.fbalpha.com"
profiled_date: "2026-03-19"
core_version: "v0.2.97.29" core_version: "v0.2.97.29"
display_name: "Arcade (FB Alpha 2012 CPS-3)" display_name: "Arcade (FB Alpha 2012 CPS-3)"
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details." cores:
- fbalpha2012_cps3
systems:
- cps3
notes: |
Frozen snapshot of Final Burn Alpha v0.2.97.29 (circa 2012), CPS-3 only.
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
Exists for RAM-constrained platforms. Most users should use FBNeo.
CPS-3 uses a Hitachi SH-2 CPU with per-game, per-region BIOS ROMs.
Each BIOS is a 512 KB (0x080000) 29F400 flash chip dump, region-locked.
BIOS ROMs are marked BRF_ESS | BRF_BIOS in the ROM definitions.
BIOS is loaded into RomBios (cps3run.cpp:1048-1064), byte-swapped to
little-endian, then decrypted via cps3_decrypt_bios() (cps3run.cpp:255).
The SH-2 boots from BIOS, which initializes hardware and loads the game
program from simulated flash/CD-ROM.
BIOS files are INSIDE the game ROM set ZIP (arcade standard), not separate
system directory files. Each game variant has its own region BIOS.
Unique BIOS ROMs across all ROM sets (CRC32 from source):
sfiii_usa.29f400.u2 0xfb172a8e SF3: New Generation (USA)
sfiii_japan.29f400.u2 0x74205250 SF3: New Generation (Japan)
sfiii_hispanic.29f400.u2 0xd2b3cd48 SF3: New Generation (Hispanic)
sfiii_asia_nocd.29f400.u2 0x73e32463 SF3: New Generation (Asia NoCD)
sfiii2_usa.29f400.u2 0x75dd72e0 SF3: 2nd Impact (USA)
sfiii2_japan.29f400.u2 0xfaea0a3e SF3: 2nd Impact (Japan)
sfiii2_asia_nocd.29f400.u2 0xfd297c0d SF3: 2nd Impact (Asia NoCD)
sfiii3_euro.29f400.u2 0x30bbf293 SF3: 3rd Strike (Euro)
sfiii3_usa.29f400.u2 0xecc545c1 SF3: 3rd Strike (USA)
sfiii3_japan_nocd.29f400.u2 0x1edc6366 SF3: 3rd Strike (Japan NoCD)
jojo_usa.29f400.u2 0x8d40f7be JoJo's Venture (USA)
jojo_japan.29f400.u2 0x02778f60 JoJo's Venture (Japan)
jojo_asia_nocd.29f400.u2 0x05b4f953 JoJo's Venture (Asia NoCD)
jojoba_japan.29f400.u2 0x3085478c JoJo's Bizarre Adventure (Japan)
jojoba_japan_nocd.29f400.u2 0x4dab19f5 JoJo's Bizarre Adventure (Japan NoCD)
jojoba_euro_nocd.29f400.u2 0x1ee2d679 JoJo's Bizarre Adventure (Euro NoCD)
redearth_euro.29f400.u2 0x02e0f336 Red Earth (Euro)
warzard_japan.29f400.u2 0xf8e2f0c6 Warzard (Japan)
18 unique BIOS ROMs, all 512 KB, all region-specific.
These are arcade ROM set components, not user-supplied BIOS files.
files: [] files: []
exclusion_note: "CPS-3 BIOS ROMs (SH-2 flash, 18 variants) are part of each game's ROM set ZIP archive, not standalone system files. Users obtain them as part of the arcade ROM set, not separately."

View File

@@ -1,8 +1,59 @@
emulator: "fbalpha2012_neogeo" emulator: "FB Alpha 2012 Neo Geo"
type: alias type: frozen_snapshot
alias_of: "fbneo" source: "https://github.com/libretro/fbalpha2012_neogeo"
profiled_date: "2026-03-18" upstream: "https://www.fbalpha.com"
profiled_date: "2026-03-19"
core_version: "v0.2.97.29" core_version: "v0.2.97.29"
display_name: "Arcade (FB Alpha 2012 Neo Geo)" display_name: "Arcade (FB Alpha 2012 Neo Geo)"
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details." cores:
- fbalpha2012_neogeo
systems:
- neogeo
notes: |
Frozen snapshot of Final Burn Alpha v0.2.97.29 (circa 2012), Neo Geo only.
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
Exists for RAM-constrained platforms. Most users should use FBNeo.
Neo Geo requires a BIOS ROM set (neogeo.zip) placed alongside game ROMs.
The BIOS set contains ~35 variants (d_neogeo.cpp:903-968), all BRF_BIOS:
68K main BIOS (BRF_SELECT, one active at a time):
asia-s3.rom 0x91b64be3 MVS Asia/Europe ver. 6 (default)
sp-s2.sp1 0x9036d879 MVS Asia/Europe ver. 5
sp-s.sp1 0xc7f2fa45 MVS Asia/Europe ver. 3 (4 slot)
sp-u2.sp1 0xe72943de MVS USA ver. 5 (2 slot)
sp-e.sp1 0x2723a5b5 MVS USA ver. 5 (6 slot)
vs-bios.rom 0xf0e8f27d MVS Japan ver. 6
sp-j2.sp1 0xacede59c MVS Japan ver. 5
sp1.jipan.1024 0x9fb0abe4 MVS Japan ver. 3 (4 slot)
sp-45.sp1 0x03cc9f6a NEO-MVH MV1C (512 KB)
japan-j3.bin 0xdff6d41f MVS Japan (J3)
neo-po.bin 0x16d0c132 AES Japan
neo-epo.bin 0xd27a71f1 AES Asia
neodebug.bin 0x698ebb7d Development Kit
sp-1v1_3db8c.bin 0x162f0ebe Deck ver. 6 (Git Ver 1.3)
uni-bios_4_0.rom 0xa7aab458 Universe BIOS 4.0
(+ Universe BIOS 1.0-3.3, 15 versions)
neopen.sp1 0xcb915e76 NeoOpen BIOS v0.1 beta
System ROMs (always required):
sm1.sm1 0x94416d67 Z80 BIOS (sound program)
sfix.sfix 0xc2ea0cfd Text layer tiles (fix layer)
000-lo.lo 0x5a86cff2 Zoom table
Special sets:
236-bios.sp1 0x853e6b96 Trackball BIOS
sp-4x.sp1 0xb4590283 PCB BIOS
spj.sp1 0x148dd727 PCB BIOS (Japan)
neocd.bin 0xdf9de490 Neo Geo CD BIOS
neogeo.zip is a parent ROM set, loaded from g_rom_dir alongside game ZIPs.
g_system_dir is set (libretro.cpp:3016) but never used for file loading.
The .info note "BIOS files must be inside the ROM directory" is accurate here:
neogeo.zip must be in the same directory as game ROM ZIPs.
files: [] files: []
exclusion_note: "Neo Geo BIOS is a parent ROM set (neogeo.zip) placed in the ROM directory alongside game ZIPs. Not a system directory file — follows arcade ROM set convention."

38
emulators/fbneo_cps12.yml Normal file
View File

@@ -0,0 +1,38 @@
emulator: "FinalBurn Neo (CPS-1/CPS-2)"
type: pure_libretro
source: "https://github.com/libretro/FBNeo"
upstream: "https://neo-source.com"
profiled_date: "2026-03-19"
core_version: "v1.0.0.03"
display_name: "Arcade (FinalBurn Neo) (CPS-1 / CPS-2)"
cores:
- fbneo_cps12
systems:
- cps1
- cps2
notes: |
CPS-1/CPS-2 subset of FinalBurn Neo, current codebase (not frozen).
Exists for platforms that cannot run the full FBNeo core.
CPS-1 and CPS-2 have no system BIOS. Each game ROM set is self-contained:
68000 program + Z80 sound + GFX + QSound samples (CPS-2).
Zero BRF_BIOS entries in d_cps1.cpp and d_cps2.cpp. QSound emulated in
software (qs_c.cpp:QscInit, panning table). No DSP ROM needed.
Board PLDs (buf1, ioa1, prg1/prg2, rom1, sou1) are BRF_OPT — GAL/PAL
dumps for hardware preservation, never loaded by the emulator.
The .info lists firmware_count=1 but it's only hiscore.dat (optional,
high score database in system/fbneo/). Not a BIOS.
ROM path: game ZIPs loaded from content directory.
system/fbneo/ used only for optional extras: hiscore.dat, samples,
cheats, blend files, IPS patches.
need_fullpath=false, extensions=zip|7z, savestate=deterministic.
files: []
exclusion_note: "CPS-1/CPS-2 arcade hardware has no system BIOS. QSound DSP emulated in software. hiscore.dat is optional high score support, not firmware."

View File

@@ -81,8 +81,10 @@ def find_missing(config: dict, db: dict) -> list[dict]:
found = False found = False
if sha1 and sha1 in db.get("files", {}): if sha1 and sha1 in db.get("files", {}):
found = True found = True
elif md5 and md5 in db.get("indexes", {}).get("by_md5", {}): elif md5:
found = True by_md5 = db.get("indexes", {}).get("by_md5", {})
md5_list = [m.strip() for m in md5.split(",") if m.strip()]
found = any(m in by_md5 for m in md5_list)
if not found: if not found:
missing.append({ missing.append({

View File

@@ -53,7 +53,7 @@ def _verify_file_hash(path: str, expected_sha1: str = "",
return True return True
hashes = compute_hashes(path) hashes = compute_hashes(path)
if expected_sha1: if expected_sha1:
return hashes["sha1"] == expected_sha1 return hashes["sha1"].lower() == expected_sha1.lower()
md5_list = [m.strip().lower() for m in expected_md5.split(",") if m.strip()] md5_list = [m.strip().lower() for m in expected_md5.split(",") if m.strip()]
return hashes["md5"].lower() in md5_list return hashes["md5"].lower() in md5_list

View File

@@ -797,7 +797,7 @@ def main():
# Replace nav section (everything from \nnav: to the next top-level key or EOF) # Replace nav section (everything from \nnav: to the next top-level key or EOF)
import re import re
if "\nnav:" in content: if "\nnav:" in content:
content = re.sub(r'\nnav:.*', '\n' + nav_yaml.rstrip(), content, count=1, flags=re.DOTALL) content = re.sub(r'\nnav:\n(?:[ \t]+.*\n?)*', '\n' + nav_yaml, content, count=1)
else: else:
content += "\n" + nav_yaml content += "\n" + nav_yaml
with open("mkdocs.yml", "w") as f: with open("mkdocs.yml", "w") as f:

View File

@@ -21,13 +21,15 @@ import json
import subprocess import subprocess
import sys import sys
import time import time
from pathlib import Path
def run(cmd: list[str], label: str) -> tuple[bool, str]: def run(cmd: list[str], label: str) -> tuple[bool, str]:
"""Run a command. Returns (success, captured_output).""" """Run a command. Returns (success, captured_output)."""
print(f"\n--- {label} ---", flush=True) print(f"\n--- {label} ---", flush=True)
start = time.monotonic() start = time.monotonic()
result = subprocess.run(cmd, capture_output=True, text=True, cwd=".") repo_root = str(Path(__file__).resolve().parent.parent)
result = subprocess.run(cmd, capture_output=True, text=True, cwd=repo_root)
elapsed = time.monotonic() - start elapsed = time.monotonic() - start
output = result.stdout output = result.stdout

View File

@@ -101,14 +101,26 @@ class Scraper(BaseScraper):
start = match.start() + raw[match.start():].index("{") start = match.start() + raw[match.start():].index("{")
depth = 0 depth = 0
i = start i = start
in_str = False
str_ch = None
while i < len(raw): while i < len(raw):
if raw[i] == "{": ch = raw[i]
if in_str:
if ch == '\\':
i += 2
continue
if ch == str_ch:
in_str = False
elif ch in ('"', "'"):
in_str = True
str_ch = ch
elif ch == "{":
depth += 1 depth += 1
elif raw[i] == "}": elif ch == "}":
depth -= 1 depth -= 1
if depth == 0: if depth == 0:
break break
elif raw[i] == "#": elif ch == "#":
while i < len(raw) and raw[i] != "\n": while i < len(raw) and raw[i] != "\n":
i += 1 i += 1
i += 1 i += 1
@@ -120,10 +132,15 @@ class Scraper(BaseScraper):
in_string = False in_string = False
string_char = None string_char = None
clean = [] clean = []
for j, ch in enumerate(line): j = 0
if ch in ('"', "'") and j > 0 and line[j - 1] == '\\': while j < len(line):
ch = line[j]
if ch == '\\' and j + 1 < len(line):
clean.append(ch) clean.append(ch)
elif ch in ('"', "'") and not in_string: clean.append(line[j + 1])
j += 2
continue
if ch in ('"', "'") and not in_string:
in_string = True in_string = True
string_char = ch string_char = ch
clean.append(ch) clean.append(ch)
@@ -134,6 +151,7 @@ class Scraper(BaseScraper):
break break
else: else:
clean.append(ch) clean.append(ch)
j += 1
lines.append("".join(clean)) lines.append("".join(clean))
clean_dict_str = "\n".join(lines) clean_dict_str = "\n".join(lines)

View File

@@ -227,8 +227,10 @@ class Scraper(BaseScraper):
continue continue
system = FUNCTION_HASH_MAP[func_name] system = FUNCTION_HASH_MAP[func_name]
func_start = func_match.start() func_start = func_match.start()
remaining = script[func_start:] next_func = _RE_FUNC.search(script, func_match.end())
local_match = _RE_LOCAL_HASHES.search(remaining) func_end = next_func.start() if next_func else len(script)
func_body = script[func_start:func_end]
local_match = _RE_LOCAL_HASHES.search(func_body)
if local_match: if local_match:
hashes_raw = local_match.group(1) hashes_raw = local_match.group(1)
hashes = [h.strip() for h in hashes_raw.split() if h.strip()] hashes = [h.strip() for h in hashes_raw.split() if h.strip()]

View File

@@ -174,14 +174,17 @@ def validate_file(
else: else:
result.add_warning("File not referenced in any platform config - needs manual review") result.add_warning("File not referenced in any platform config - needs manual review")
if filepath.startswith("bios/"): normalized = os.path.normpath(filepath)
parts = filepath.split("/") if os.path.islink(filepath):
result.add_check(False, "Symlinks are not allowed")
elif normalized.startswith("bios" + os.sep):
parts = normalized.split(os.sep)
if len(parts) >= 4: if len(parts) >= 4:
result.add_check(True, f"Correct placement: bios/{parts[1]}/{parts[2]}/") result.add_check(True, f"Correct placement: bios/{parts[1]}/{parts[2]}/")
else: else:
result.add_warning("File should be in bios/Manufacturer/Console/ structure") result.add_warning("File should be in bios/Manufacturer/Console/ structure")
else: else:
result.add_warning(f"File is not under bios/ directory") result.add_warning("File is not under bios/ directory")
if name_known and not sha1_known and not md5_known: if name_known and not sha1_known and not md5_known:
result.add_info( result.add_info(

View File

@@ -421,8 +421,9 @@ def print_platform_result(result: dict, group: list[str]) -> None:
else: else:
parts = [f"{ok_count}/{total} present"] parts = [f"{ok_count}/{total} present"]
else: else:
untested = c.get(Severity.WARNING, 0) sc = result.get("status_counts", {})
missing = c.get(Severity.CRITICAL, 0) untested = sc.get(Status.UNTESTED, 0)
missing = sc.get(Status.MISSING, 0)
parts = [f"{ok_count}/{total} OK"] parts = [f"{ok_count}/{total} OK"]
if untested: if untested:
parts.append(f"{untested} untested") parts.append(f"{untested} untested")