fix: open no System.dat section libretro lacks

libretro titles each section with the system as it spells it,
"Commodore - Amiga". An addition for a system it has no section for
opened one under our slug: 21 sections came out as commodore-c64,
quake, laseractive or xrick, which no libretro DAT carries. An addition
now lands only in a section the original holds and is otherwise counted
as left out, so the export keeps the original's 66 sections. A platform
entry is always written, under its own section.
This commit is contained in:
Abdessamad Derraz committed 2026-10-20 17:21:16 +02:00
1 parent a8226dd367
commit f957b5f893
4 files changed
+61 -6

No files matched your search

+21 -2
View File
@@ -150,6 +150,7 @@ class Exporter(BaseExporter):
quoted = frozenset(_QUOTED_NAME.findall(original))
ours: dict[str, dict[str, str]] = {}
added: dict[str, set[str]] = {}
for system, files in self.exportable(systems):
rendered: dict[str, str] = {}
for fe in files:
@@ -170,6 +171,8 @@ class Exporter(BaseExporter):
if sha1:
parts.append(f"sha1 {sha1}")
rendered.setdefault(name, f"\trom ( {' '.join(parts)} )")
if fe.platform is None:
added.setdefault(system.native_id, set()).add(name)
if rendered:
ours[system.native_id] = rendered
@@ -194,13 +197,29 @@ class Exporter(BaseExporter):
body.append(line)
body.extend(rendered.values())
section(comment, body)
# A section is titled with the system as libretro spells it, and we
# know no title for one it does not have: 20 sections came out under
# our slugs (commodore-c64, quake, laseractive, xrick).
self._untitled = {
id(fe)
for system, files in self.exportable(systems)
if system.native_id in ours
for fe in files
if fe.platform is None
}
for comment in sorted(ours):
section(comment, list(ours[comment].values()))
own = [line for name, line in ours[comment].items()
if name not in added.get(comment, set())]
if own:
section(comment, own)
lines.append(")")
lines.append("")
return {self.native_filename(): "\n".join(lines)}
def lands(self, fe: NativeFile) -> bool:
return self.writable(fe) and id(fe) not in getattr(self, "_untitled", set())
def validate(
self,
systems: dict[str, NativeSystem],
@@ -213,7 +232,7 @@ class Exporter(BaseExporter):
issues: list[str] = []
for system in systems.values():
for fe in system.files:
if not any(fe.hash(h) for h in ("crc32", "md5", "sha1")):
if not self.lands(fe) or not any(fe.hash(h) for h in ("crc32", "md5", "sha1")):
continue
if self._rom_name(fe) not in exported:
issues.append(f"absent from the DAT: {system.native_id}/{fe.name}")
+5 -1
View File
@@ -4226,7 +4226,11 @@ class TestE2E(unittest.TestCase):
from exporter.baseline import build_native_model
systems, report = build_native_model(truth, {"systems": {}})
produced = SystemDatExporter().render(systems, report, {}, {"systems": {}})
original = ('clrmamepro (\n\tname "System"\n)\n\ngame (\n\tname "System"\n'
'\tcomment "commodore-plus4"\n)\n')
produced = SystemDatExporter().render(
systems, report, {"System.dat": original}, {"systems": {}}
)
self.assertIn("crc 70295038", produced["System.dat"])
# ---------------------------------------------------------------
+3 -1
View File
@@ -275,7 +275,9 @@ class PureDataExportsKeepWhatWeDoNotModel(unittest.TestCase):
for api, digest in (("vulkan", "a"), ("opengl", "b"))
]
systems = {"Sony - PlayStation 2": NativeSystem("Sony - PlayStation 2", files=shaders)}
produced = SystemDat().render(systems, Report(), {})["System.dat"]
original = ('clrmamepro (\n\tname "System"\n)\n\ngame (\n\tname "System"\n'
'\tcomment "Sony - PlayStation 2"\n)\n')
produced = SystemDat().render(systems, Report(), {"System.dat": original})["System.dat"]
self.assertIn("name pcsx2/resources/shaders/vulkan/convert.glsl md5 " + "a" * 32, produced)
self.assertIn("name pcsx2/resources/shaders/opengl/convert.glsl md5 " + "b" * 32, produced)
self.assertEqual(SystemDat().validate(systems, {"System.dat": produced}), [])
+32 -2
View File
@@ -1206,6 +1206,38 @@ class RommKeepsItsFixture(unittest.TestCase):
self.assertEqual(len(changed), 2, changed)
class SystemDatNamesNoSectionItself(unittest.TestCase):
"""libretro titles each section with the system as it spells it,
"Commodore - Amiga". An addition for a system it has no section for
opened one under our slug: 20 such sections, commodore-c64, quake,
laseractive."""
def test_an_addition_without_a_libretro_section_is_left_out(self):
amiga = "Commodore - Amiga"
original = (
'clrmamepro (\n\tname "System"\n)\n\ngame (\n\tname "System"\n'
f'\tcomment "{amiga}"\n'
f"\trom ( name kick34005.A500 size 262144 md5 {'2' * 32} )\n"
")\n"
)
scraped = {"systems": {"commodore-amiga": {"native_id": amiga, "files": [
{"name": "kick34005.A500", "destination": "kick34005.A500",
"native_system": amiga, "md5": "2" * 32, "size": 262144}]}}}
truth = {"systems": {
"commodore-amiga": {"files": [{"name": "kick40068.A1200", "md5": "3" * 32}]},
"laseractive": {"files": [{"name": "pac-n1.bin", "crc32": "a8cb694c"}]},
}}
systems, report = build_native_model(truth, scraped)
exporter = discover_exporters()["retroarch"]()
produced = exporter.render(systems, report, {"System.dat": original}, None)["System.dat"]
self.assertNotIn('comment "laseractive"', produced)
self.assertNotIn("pac-n1.bin", produced)
self.assertIn("kick40068.A1200", produced)
self.assertEqual(exporter.validate(systems, {"System.dat": produced}), [])
added = systems["laseractive"].files[0]
self.assertFalse(exporter.lands(added))
class SystemDatKeepsSpaceIndentedLines(unittest.TestCase):
"""libretro indents 39 rom lines with four spaces. Read with a tab only,
they left the base and came back last in their section, re-indented."""
@@ -1508,8 +1540,6 @@ class BatoceraExport(unittest.TestCase):
exporter = discover_exporters()[platform]
self.assertFalse(exporter().writable(foreign))
self.assertTrue(exporter().writable(own))
# A format that finds files by path has no system key to miss.
self.assertTrue(discover_exporters()["retroarch"]().writable(foreign))
produced = discover_exporters()["batocera"]().render(
systems, report, {"batocera-systems": self.SKELETON}, None
)