fix: quote rom and core names in yaml

YAML 1.1 reads an unquoted 01 as the integer 1 and 81 as 81, so the
manager set stopped naming the file its driver opens and the API
published "cores": [2048] as a number where consumers match strings.

Ground truth from MAME 0.289 at the revision the profile cites:
src/mame/vtech/crvision.cpp:957 loads "01" and "23".

The root cause was the scraper: _hash_merge wrote these names through an
f-string, so quoting the profiles alone would be undone on the next
refresh. _yaml_scalar now quotes every name, archive and description it
writes.
This commit is contained in:
Abdessamad Derraz committed 2026-08-10 13:35:42 +02:00
1 parent 5d417c8229
commit 597a8ff919
7 files changed
+66 -11

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ profiled_date: "2026-03-22"
source_commit: "e70c3f82d2b861c64943aaff7fcc29a63013997d" source_commit: "e70c3f82d2b861c64943aaff7fcc29a63013997d"
core_version: "1.0" core_version: "1.0"
display_name: "2048" display_name: "2048"
cores: [2048] cores: ["2048"]
systems: [] systems: []
files: [] files: []
notes: > notes: >
+1 -1
View File
@@ -7,7 +7,7 @@ profiled_date: "2026-03-22"
source_commit: "fa7094910d040baa5fd8b11dbf6a1a618330ecd9" source_commit: "fa7094910d040baa5fd8b11dbf6a1a618330ecd9"
core_version: "1.0a" core_version: "1.0a"
display_name: "Sinclair - ZX 81 (EightyOne)" display_name: "Sinclair - ZX 81 (EightyOne)"
cores: [81] cores: ["81"]
systems: systems:
- sinclair-zx81 - sinclair-zx81
- sinclair-zx80 - sinclair-zx80
+2 -2
View File
@@ -3543,11 +3543,11 @@ files:
source_ref: "src/mame/vtech/crvision.cpp:955-959" source_ref: "src/mame/vtech/crvision.cpp:955-959"
note: "Salora Manager (Finnish variant)" note: "Salora Manager (Finnish variant)"
contents: contents:
- name: 01 - name: "01"
description: ROM bank 0-1 description: ROM bank 0-1
size: 8192 size: 8192
crc32: "702f4cf5" crc32: "702f4cf5"
- name: 23 - name: "23"
description: ROM bank 2-3 description: ROM bank 2-3
size: 8192 size: 8192
crc32: "46489d88" crc32: "46489d88"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"windows": [ "windows": [
81, "81",
"a5200", "a5200",
"abuse", "abuse",
"arduous", "arduous",
+1 -1
View File
@@ -4,7 +4,7 @@ scraped_at: "2026-03-26T00:00:00Z"
targets: targets:
windows: windows:
architecture: x86_64 architecture: x86_64
cores: [81, a5200, abuse, arduous, atari800, azahar, bennugd, bk, bluemsx, bsnes, cores: ['81', a5200, abuse, arduous, atari800, azahar, bennugd, bk, bluemsx, bsnes,
bstone, cannonball, cap32, catacombgl, cdogs, cemu, cgenius, citron, clk, corsixth, bstone, cannonball, cap32, catacombgl, cdogs, cemu, cgenius, citron, clk, corsixth,
demul, devilutionx, dhewm3, dice, dolphin, dosbox_pure, dxx-rebirth, easyrpg, demul, devilutionx, dhewm3, dice, dolphin, dosbox_pure, dxx-rebirth, easyrpg,
ecwolf, eduke32, eka2l1, emuscv, etlegacy, fake08, fallout1-ce, fallout2-ce, ecwolf, eduke32, eka2l1, emuscv, etlegacy, fake08, fallout1-ce, fallout2-ce,
+14 -5
View File
@@ -507,7 +507,7 @@ def _append_new_entries(text: str, files: list[dict], original: str) -> str:
lines = [] lines = []
for fe in new_entries: for fe in new_entries:
lines.append(f"\n - name: {fe['name']}") lines.append(f"\n - name: {_yaml_scalar(fe['name'])}")
lines.append(f" required: {str(fe['required']).lower()}") lines.append(f" required: {str(fe['required']).lower()}")
lines.append(" category: bios_zip") lines.append(" category: bios_zip")
if fe.get("source_ref"): if fe.get("source_ref"):
@@ -521,13 +521,22 @@ def _append_new_entries(text: str, files: list[dict], original: str) -> str:
return text return text
def _yaml_scalar(value) -> str:
"""Quote a scalar so YAML reads back exactly what the source said.
A MAME ROM named ``01`` written bare re-parses as the integer 1, and the
set stops naming the file the driver loads. Quoting is not cosmetic here.
"""
return json.dumps(str(value), ensure_ascii=False)
def _format_contents(contents: list[dict]) -> str: def _format_contents(contents: list[dict]) -> str:
"""Format a contents list as YAML text.""" """Format a contents list as YAML text."""
lines = [" contents:"] lines = [" contents:"]
for rom in contents: for rom in contents:
lines.append(f" - name: {rom['name']}") lines.append(f" - name: {_yaml_scalar(rom['name'])}")
if rom.get("description"): if rom.get("description"):
lines.append(f" description: {rom['description']}") lines.append(f" description: {_yaml_scalar(rom['description'])}")
if rom.get("size"): if rom.get("size"):
lines.append(f" size: {rom['size']}") lines.append(f" size: {rom['size']}")
if rom.get("crc32"): if rom.get("crc32"):
@@ -569,8 +578,8 @@ def _backup_and_write_fbneo(path: str, data: dict, hashes: dict) -> None:
if new_roms: if new_roms:
lines = [] lines = []
for fe in new_roms: for fe in new_roms:
lines.append(f' - name: "{fe["name"]}"') lines.append(f" - name: {_yaml_scalar(fe['name'])}")
lines.append(f" archive: {fe['archive']}") lines.append(f" archive: {_yaml_scalar(fe['archive'])}")
lines.append(f" required: {str(fe.get('required', True)).lower()}") lines.append(f" required: {str(fe.get('required', True)).lower()}")
if fe.get("size"): if fe.get("size"):
lines.append(f" size: {fe['size']}") lines.append(f" size: {fe['size']}")
+46
View File
@@ -196,6 +196,52 @@ class TestMameMerge(unittest.TestCase):
self.assertTrue(pgm["required"]) self.assertTrue(pgm["required"])
self.assertEqual(pgm["category"], "bios_zip") self.assertEqual(pgm["category"], "bios_zip")
def test_numeric_looking_rom_name_survives_the_write(self) -> None:
"""MAME ships ROMs named 01 and 23. Bare YAML turns them into numbers.
The manager set loads "01"; written unquoted it re-parses as the
integer 1 and the profile stops naming the file the driver opens.
"""
from scripts.scraper._hash_merge import (
_append_new_entries,
_format_contents,
)
contents = [
{"name": "01", "description": "ROM bank 0-1", "size": 8192,
"crc32": "702f4cf5"},
{"name": "23", "description": "ROM bank 2-3", "size": 8192,
"crc32": "46489d88"},
]
document = yaml.safe_load(
"files:\n - name: manager.zip\n" + _format_contents(contents)
)
names = [rom["name"] for rom in document["files"][0]["contents"]]
self.assertEqual(names, ["01", "23"])
for name in names:
self.assertIsInstance(name, str)
original = "emulator: MAME\nfiles:\n - name: neogeo.zip\n"
appended = _append_new_entries(
original,
[{
"name": "manager.zip",
"required": False,
"category": "bios_zip",
"source_ref": "src/mame/vtech/crvision.cpp:955-959",
"contents": contents,
}],
original,
)
entry = next(
f
for f in yaml.safe_load(appended)["files"]
if f["name"] == "manager.zip"
)
self.assertEqual(
[rom["name"] for rom in entry["contents"]], ["01", "23"]
)
def test_merge_preserves_non_bios_files(self) -> None: def test_merge_preserves_non_bios_files(self) -> None:
profile = _make_mame_profile() profile = _make_mame_profile()
profile["files"].append({"name": "hiscore.dat", "required": False}) profile["files"].append({"name": "hiscore.dat", "required": False})