mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-10 03:53:56 -06:00
feat: parse gamel machines and computed sizes
This commit is contained in:
1 parent
ac9ce4d2cc
commit
b86a34933c
2 files changed
+61
-12
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
"""Parser for MAME C source files.
|
"""Parser for MAME C source files.
|
||||||
|
|
||||||
Extracts BIOS root sets and ROM definitions from MAME driver sources.
|
Extracts BIOS root sets and ROM definitions from MAME driver sources.
|
||||||
Handles GAME/SYST/COMP/CONS macros with MACHINE_IS_BIOS_ROOT flag,
|
Handles GAME/GAMEL/SYST/COMP/CONS macros with MACHINE_IS_BIOS_ROOT flag,
|
||||||
ROM_START/ROM_END blocks, ROM_LOAD variants, ROM_REGION, ROM_SYSTEM_BIOS,
|
ROM_START/ROM_END blocks, ROM_LOAD variants, ROM_REGION, ROM_SYSTEM_BIOS,
|
||||||
NO_DUMP filtering, and BAD_DUMP flagging.
|
NO_DUMP filtering, and BAD_DUMP flagging.
|
||||||
"""
|
"""
|
||||||
@@ -12,9 +12,11 @@ import os
|
|||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Macros that declare a machine entry
|
# Macros that declare a machine entry. GAMEL is GAME plus a layout and is
|
||||||
|
# the only suffixed form MAME declares (src/emu/gamedrv.h): a BIOS root that
|
||||||
|
# ships a layout, such as gq863 or aristmk5, is written with it.
|
||||||
_MACHINE_MACROS = re.compile(
|
_MACHINE_MACROS = re.compile(
|
||||||
r"\b(GAME|SYST|COMP|CONS)\s*\(",
|
r"\b(GAMEL|GAME|SYST|COMP|CONS)\s*\(",
|
||||||
re.MULTILINE,
|
re.MULTILINE,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,10 +24,13 @@ _MACHINE_MACROS = re.compile(
|
|||||||
_ROM_START = re.compile(r"ROM_START\s*\(\s*(\w+)\s*\)")
|
_ROM_START = re.compile(r"ROM_START\s*\(\s*(\w+)\s*\)")
|
||||||
_ROM_END = re.compile(r"ROM_END")
|
_ROM_END = re.compile(r"ROM_END")
|
||||||
|
|
||||||
|
# Offsets and sizes are literals or arithmetic over them, as in 128*1024.
|
||||||
|
_NUMBER = r"(?:0x[\da-fA-F]+|\d+)(?:\s*[*+]\s*(?:0x[\da-fA-F]+|\d+))*"
|
||||||
|
|
||||||
# ROM_REGION variants: ROM_REGION, ROM_REGION16_BE, ROM_REGION16_LE, ROM_REGION32_LE, etc.
|
# ROM_REGION variants: ROM_REGION, ROM_REGION16_BE, ROM_REGION16_LE, ROM_REGION32_LE, etc.
|
||||||
_ROM_REGION = re.compile(
|
_ROM_REGION = re.compile(
|
||||||
r"ROM_REGION\w*\s*\("
|
r"ROM_REGION\w*\s*\("
|
||||||
r"\s*(0x[\da-fA-F]+|\d+)\s*," # size
|
rf"\s*({_NUMBER})\s*," # size
|
||||||
r'\s*"([^"]+)"\s*,', # tag
|
r'\s*"([^"]+)"\s*,', # tag
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,8 +52,8 @@ _ROM_LOAD = re.compile(
|
|||||||
r"\b\w*ROMX?_LOAD\w*\s*\("
|
r"\b\w*ROMX?_LOAD\w*\s*\("
|
||||||
r'[^"]*' # skip any args before the filename (e.g., bios index)
|
r'[^"]*' # skip any args before the filename (e.g., bios index)
|
||||||
r'"([^"]+)"\s*,' # name (first quoted string)
|
r'"([^"]+)"\s*,' # name (first quoted string)
|
||||||
r"\s*(0x[\da-fA-F]+|\d+)\s*," # offset
|
rf"\s*({_NUMBER})\s*," # offset
|
||||||
r"\s*(0x[\da-fA-F]+|\d+)\s*,", # size
|
rf"\s*({_NUMBER})\s*,", # size
|
||||||
)
|
)
|
||||||
|
|
||||||
# CRC32 and SHA1 within a ROM_LOAD line
|
# CRC32 and SHA1 within a ROM_LOAD line
|
||||||
@@ -114,7 +119,7 @@ def strip_comments(source: str) -> str:
|
|||||||
def find_bios_root_sets(source: str, filename: str) -> dict[str, dict]:
|
def find_bios_root_sets(source: str, filename: str) -> dict[str, dict]:
|
||||||
"""Find machine entries flagged as BIOS root sets.
|
"""Find machine entries flagged as BIOS root sets.
|
||||||
|
|
||||||
Scans for GAME/SYST/COMP/CONS macros where the args include
|
Scans for GAME/GAMEL/SYST/COMP/CONS macros where the args include
|
||||||
MACHINE_IS_BIOS_ROOT, returns set names with source location.
|
MACHINE_IS_BIOS_ROOT, returns set names with source location.
|
||||||
"""
|
"""
|
||||||
results: dict[str, dict] = {}
|
results: dict[str, dict] = {}
|
||||||
@@ -137,6 +142,7 @@ def find_bios_root_sets(source: str, filename: str) -> dict[str, dict]:
|
|||||||
|
|
||||||
# The set name position varies by macro type
|
# The set name position varies by macro type
|
||||||
# GAME(year, setname, parent, machine, input, init, monitor, company, fullname, flags)
|
# GAME(year, setname, parent, machine, input, init, monitor, company, fullname, flags)
|
||||||
|
# GAMEL(year, setname, parent, machine, input, init, monitor, company, fullname, flags, layout)
|
||||||
# CONS(year, setname, parent, compat, machine, input, init, company, fullname, flags)
|
# CONS(year, setname, parent, compat, machine, input, init, company, fullname, flags)
|
||||||
# COMP(year, setname, parent, compat, machine, input, init, company, fullname, flags)
|
# COMP(year, setname, parent, compat, machine, input, init, company, fullname, flags)
|
||||||
# SYST(year, setname, parent, compat, machine, input, init, company, fullname, flags)
|
# SYST(year, setname, parent, compat, machine, input, init, company, fullname, flags)
|
||||||
@@ -437,8 +443,12 @@ def _parse_rom_entries(block: str) -> list[dict]:
|
|||||||
|
|
||||||
|
|
||||||
def _parse_int(value: str) -> int:
|
def _parse_int(value: str) -> int:
|
||||||
"""Parse an integer that may be hex (0x...) or decimal."""
|
"""Parse a hex or decimal literal, or arithmetic over them as in 128*1024."""
|
||||||
value = value.strip()
|
total = 0
|
||||||
if value.startswith("0x") or value.startswith("0X"):
|
for term in value.split("+"):
|
||||||
return int(value, 16)
|
product = 1
|
||||||
return int(value)
|
for factor in term.split("*"):
|
||||||
|
factor = factor.strip()
|
||||||
|
product *= int(factor, 16 if factor[:2].lower() == "0x" else 10)
|
||||||
|
total += product
|
||||||
|
return total
|
||||||
@@ -71,6 +71,16 @@ ROM_END
|
|||||||
CONS( 1988, megadriv, 0, 0, megadriv, megadriv, md_state, init_megadriv, "Sega", "Mega Drive", MACHINE_IS_BIOS_ROOT )
|
CONS( 1988, megadriv, 0, 0, megadriv, megadriv, md_state, init_megadriv, "Sega", "Mega Drive", MACHINE_IS_BIOS_ROOT )
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# GAMEL macro: GAME plus a layout, the form gq863 and aristmk5 are written with
|
||||||
|
GAMEL_FIXTURE = """\
|
||||||
|
ROM_START( gq863 )
|
||||||
|
ROM_REGION( 0x080000, "audiocpu", 0 )
|
||||||
|
ROM_LOAD( "863a03.7b", 0x000000, 0x080000, CRC(81498f73) SHA1(3599b40a5872eab3a00d345287635355fcb25a71) )
|
||||||
|
ROM_END
|
||||||
|
|
||||||
|
GAMEL( 1999, gq863, 0, twinkle, twinkle, twinkle_state, empty_init, ROT0, "Konami", "Twinkle System", MACHINE_IS_BIOS_ROOT, layout_bmiidx )
|
||||||
|
"""
|
||||||
|
|
||||||
# GAME macro WITHOUT MACHINE_IS_BIOS_ROOT (should NOT be detected)
|
# GAME macro WITHOUT MACHINE_IS_BIOS_ROOT (should NOT be detected)
|
||||||
NON_BIOS_FIXTURE = """\
|
NON_BIOS_FIXTURE = """\
|
||||||
ROM_START( pacman )
|
ROM_START( pacman )
|
||||||
@@ -118,6 +128,17 @@ ROM_END
|
|||||||
GAME( 1989, rcgp, 0, rcgp, rcgp, test_state, init_test, ROT0, "Namco", "R/C Grand Prix", MACHINE_IS_BIOS_ROOT )
|
GAME( 1989, rcgp, 0, rcgp, rcgp, test_state, init_test, ROT0, "Namco", "R/C Grand Prix", MACHINE_IS_BIOS_ROOT )
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Sizes written as arithmetic, as konamigx.c does for its GX_BIOS macro
|
||||||
|
ARITHMETIC_SIZE_FIXTURE = """\
|
||||||
|
ROM_START( konamigx )
|
||||||
|
ROM_REGION( 8*1024*1024, "maincpu", ROMREGION_ERASEFF )
|
||||||
|
ROM_LOAD( "300a01.34k", 0x000000, 128*1024, CRC(d5fa95f5) SHA1(c483aa98ff8ef40cdac359c19ad23fea5ecc1906) )
|
||||||
|
ROM_LOAD( "300a02.31k", 0x020000, 0x10000+0x800, CRC(d5fa95f6) SHA1(c483aa98ff8ef40cdac359c19ad23fea5ecc1907) )
|
||||||
|
ROM_END
|
||||||
|
|
||||||
|
GAME( 1994, konamigx, 0, konamigx, konamigx, gx_state, init_gx, ROT0, "Konami", "System GX", MACHINE_IS_BIOS_ROOT )
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TestFindBiosRootSets(unittest.TestCase):
|
class TestFindBiosRootSets(unittest.TestCase):
|
||||||
"""Tests for find_bios_root_sets."""
|
"""Tests for find_bios_root_sets."""
|
||||||
@@ -136,6 +157,14 @@ class TestFindBiosRootSets(unittest.TestCase):
|
|||||||
result = find_bios_root_sets(CONS_FIXTURE, "src/mame/sega/megadriv.cpp")
|
result = find_bios_root_sets(CONS_FIXTURE, "src/mame/sega/megadriv.cpp")
|
||||||
self.assertIn("megadriv", result)
|
self.assertIn("megadriv", result)
|
||||||
|
|
||||||
|
def test_detects_from_gamel_macro(self) -> None:
|
||||||
|
result = find_bios_root_sets(GAMEL_FIXTURE, "src/mame/konami/twinkle.cpp")
|
||||||
|
self.assertIn("gq863", result)
|
||||||
|
|
||||||
|
def test_gamel_rom_block_is_parsed(self) -> None:
|
||||||
|
roms = parse_rom_block(GAMEL_FIXTURE, "gq863")
|
||||||
|
self.assertEqual([r["name"] for r in roms], ["863a03.7b"])
|
||||||
|
|
||||||
def test_ignores_non_bios_games(self) -> None:
|
def test_ignores_non_bios_games(self) -> None:
|
||||||
result = find_bios_root_sets(NON_BIOS_FIXTURE, "src/mame/pacman/pacman.cpp")
|
result = find_bios_root_sets(NON_BIOS_FIXTURE, "src/mame/pacman/pacman.cpp")
|
||||||
self.assertEqual(result, {})
|
self.assertEqual(result, {})
|
||||||
@@ -228,6 +257,16 @@ class TestParseRomBlock(unittest.TestCase):
|
|||||||
roms = parse_rom_block(NEOGEO_FIXTURE, "nonexistent")
|
roms = parse_rom_block(NEOGEO_FIXTURE, "nonexistent")
|
||||||
self.assertEqual(roms, [])
|
self.assertEqual(roms, [])
|
||||||
|
|
||||||
|
def test_evaluates_arithmetic_sizes(self) -> None:
|
||||||
|
roms = parse_rom_block(ARITHMETIC_SIZE_FIXTURE, "konamigx")
|
||||||
|
self.assertEqual([r["name"] for r in roms], ["300a01.34k", "300a02.31k"])
|
||||||
|
self.assertEqual(roms[0]["size"], 131072)
|
||||||
|
self.assertEqual(roms[1]["size"], 0x10800)
|
||||||
|
|
||||||
|
def test_tracks_region_with_arithmetic_size(self) -> None:
|
||||||
|
roms = parse_rom_block(ARITHMETIC_SIZE_FIXTURE, "konamigx")
|
||||||
|
self.assertEqual(roms[0]["region"], "maincpu")
|
||||||
|
|
||||||
def test_good_rom_not_flagged_bad_dump(self) -> None:
|
def test_good_rom_not_flagged_bad_dump(self) -> None:
|
||||||
roms = parse_rom_block(NODUMP_FIXTURE, "testnd")
|
roms = parse_rom_block(NODUMP_FIXTURE, "testnd")
|
||||||
good = next(r for r in roms if r["name"] == "good.rom")
|
good = next(r for r in roms if r["name"] == "good.rom")
|
||||||
|
|||||||
Reference in new issue
Block a user