fix: keep the text of unchanged Recalbox elements

Every <bios> element the model held was rendered again, so attribute order, alignment, a line break inside md5 and the case of a digest changed in 222 elements that stated the same thing. The diff deleted 451 lines for 136 corrections. An element whose attributes say what Recalbox wrote now keeps Recalbox's own text: 175 lines, the corrections.
This commit is contained in:
Abdessamad Derraz committed 2026-10-20 01:23:29 +02:00
1 parent cef6d943e2
commit c5fd079b78
2 files changed
+70 -4

No files matched your search

+41 -4
View File
@@ -11,6 +11,8 @@ when the platform stated it explicitly.
from __future__ import annotations
import html
import re
import sys
from pathlib import Path
from xml.etree.ElementTree import Comment, ParseError
@@ -33,6 +35,32 @@ SCHEMA_URL = (
)
_ATTRIBUTE = re.compile(r'([A-Za-z]+)\s*=\s*"([^"]*)"')
_SYSTEM_BLOCK = re.compile(r"<system\b.*?</system>", re.S)
_BIOS_ELEMENT = re.compile(r"[ \t]*<bios\b.*?/>", re.S)
def _stated(text: str) -> dict[str, str]:
"""What an element says, without its layout: attribute order, spacing,
line breaks inside a value and the case of a digest are not content."""
attrs = {k: " ".join(html.unescape(v).split()) for k, v in _ATTRIBUTE.findall(text)}
if "md5" in attrs:
attrs["md5"] = ",".join(h.strip().lower() for h in attrs["md5"].split(","))
return attrs
def _original_elements(original: str) -> list[dict[str, str]]:
"""Each system block's <bios> elements as written, by path, in order."""
blocks = []
for block in _SYSTEM_BLOCK.finditer(original):
elements: dict[str, str] = {}
for match in _BIOS_ELEMENT.finditer(block.group(0)):
raw = match.group(0)
elements.setdefault(_stated(raw).get("path", ""), raw)
blocks.append(elements)
return blocks
class Exporter(BaseExporter):
"""Write Recalbox's es_bios.xml, corrected."""
@@ -179,17 +207,26 @@ class Exporter(BaseExporter):
lines.extend(body)
lines.append(" </system>")
# Each block of theirs corrects the entries it holds.
# Each block of theirs corrects the entries it holds. An element that
# states what Recalbox wrote keeps Recalbox's text: re-rendered, 222
# unchanged elements made 180 lines of diff the report never named.
written = _original_elements(original) if original else []
blocks: list[tuple[str, str, list[str]]] = []
for platform, fullname, children in theirs:
for index, (platform, fullname, children) in enumerate(theirs):
elements = ours.get(platform, (None, {}))[1]
raw = written[index] if index < len(written) else {}
body: list[str] = []
for child in children:
if isinstance(child, str):
body.append(f" {child}")
continue
mine = elements.pop(child.get("path", ""), None)
body.append(mine[1] if mine else element(child))
path = child.get("path", "")
mine = elements.pop(path, None)
kept = raw.get(" ".join(path.split()))
if kept is not None and (mine is None or _stated(mine[1]) == _stated(kept)):
body.append(kept)
else:
body.append(mine[1] if mine else element(child))
blocks.append((platform, fullname, body))
# An addition joins the block named like its file, else the first
+29
View File
@@ -1764,5 +1764,34 @@ class BatoceraNotesStayWithTheirItem(unittest.TestCase):
self.assertEqual(trailing, {("bios/bk0010.zip", "two.rom"): "# tail"})
class RecalboxKeepsUnchangedText(unittest.TestCase):
"""Every element the model held was re-rendered: attribute order,
alignment, line breaks inside md5 and digest case changed in 222
elements that said the same thing, 180 lines of diff nobody was told of."""
def test_an_element_that_says_the_same_keeps_its_text(self):
scraped = {"systems": {"sinclair-zxspectrum": {
"native_id": "zxspectrum",
"files": [{
"name": "128p-0.rom", "destination": "fuse/128p-0.rom",
"native_system": "zxspectrum", "native_name": "Pentagon",
"native_path": "fuse/128p-0.rom", "md5": "a" * 32,
"core": "libretro/fuse", "required": True,
}],
}}}
written = f' <bios path="fuse/128p-0.rom" md5="{"A" * 32}"\n core="libretro/fuse" />'
original = (
'<?xml version="1.0" encoding="UTF-8"?>\n<biosList>\n'
' <system fullname="Pentagon" platform="zxspectrum">\n'
f"{written}\n"
" </system>\n</biosList>\n"
)
systems, report = build_native_model({"systems": {}}, scraped)
produced = discover_exporters()["recalbox"]().render(
systems, report, {"es_bios.xml": original}, None
)["es_bios.xml"]
self.assertIn(written, produced)
if __name__ == "__main__":
unittest.main()