mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-27 06:07:24 -05:00
feat: read batocera md5 written as a list
batocera.linux 6b08b873 lets a biosFiles item declare its md5 as a list of strings, and checkBios accepts a file matching any of them. The scrapers join the values with commas, as for Recalbox, and record md5_list so the export writes the list back as a list, byte for byte.
This commit is contained in:
1 parent
e86defdb1e
commit
0b4504992b
9 files changed
+298
-13
No files matched your search
@@ -74,6 +74,25 @@ class ChangeSet:
|
||||
MAX_RESPONSE_SIZE = 50 * 1024 * 1024 # 50 MB
|
||||
|
||||
|
||||
def md5_field(raw: object) -> tuple[list[str], bool]:
|
||||
"""The md5 values a batocera-systems item declares, and whether it
|
||||
writes them as a list.
|
||||
|
||||
Since batocera.linux 6b08b873 an item's md5 is one string or a list of
|
||||
strings, and checkBios accepts a file whose md5 is any of them. The
|
||||
platform YAML joins the values with commas, as it does for Recalbox, and
|
||||
records the list form so the export writes a list back as a list: a
|
||||
one-item list and an empty one are not the same text as a string.
|
||||
"""
|
||||
if raw is None:
|
||||
return [], False
|
||||
if isinstance(raw, str):
|
||||
return ([raw] if raw else []), False
|
||||
if isinstance(raw, list) and all(isinstance(item, str) for item in raw):
|
||||
return [item for item in raw if item], True
|
||||
raise ValueError(f"md5 is neither a string nor a list of strings: {raw!r}")
|
||||
|
||||
|
||||
def requirement_entry(req: BiosRequirement) -> dict:
|
||||
"""Serialize a requirement to a platform YAML file entry.
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from .base_scraper import (
|
||||
BaseScraper,
|
||||
BiosRequirement,
|
||||
github_headers,
|
||||
md5_field,
|
||||
requirement_entry,
|
||||
)
|
||||
from .batocera_launchers import extract_launchers
|
||||
@@ -420,10 +421,16 @@ def requirements_from_systems(
|
||||
for bios in sys_data.get("biosFiles", []):
|
||||
declared_path = bios.get("file", "")
|
||||
file_path = declared_path
|
||||
md5 = _resolve_truncated_md5(bios.get("md5", ""), md5_index)
|
||||
values, as_list = md5_field(bios.get("md5"))
|
||||
md5 = ",".join(
|
||||
_resolve_truncated_md5(value, md5_index) for value in values
|
||||
)
|
||||
zipped_file = bios.get("zippedFile", "")
|
||||
if strip and file_path.startswith(strip):
|
||||
file_path = file_path[len(strip):]
|
||||
native: dict[str, object] = {"native_name": sys_data.get("name", "")}
|
||||
if as_list:
|
||||
native["md5_list"] = True
|
||||
requirements.append(
|
||||
BiosRequirement(
|
||||
name=file_path.split("/")[-1],
|
||||
@@ -434,7 +441,7 @@ def requirements_from_systems(
|
||||
required=True,
|
||||
zipped_file=zipped_file or None,
|
||||
native_id=sys_key,
|
||||
native={"native_name": sys_data.get("name", "")},
|
||||
native=native,
|
||||
)
|
||||
)
|
||||
return requirements
|
||||
|
||||
@@ -22,6 +22,7 @@ try:
|
||||
BaseScraper,
|
||||
BiosRequirement,
|
||||
fetch_github_latest_version,
|
||||
md5_field,
|
||||
requirement_entry,
|
||||
)
|
||||
from .rocknix_launchers import extract_launchers
|
||||
@@ -31,6 +32,7 @@ except ImportError:
|
||||
BaseScraper,
|
||||
BiosRequirement,
|
||||
fetch_github_latest_version,
|
||||
md5_field,
|
||||
requirement_entry,
|
||||
)
|
||||
from rocknix_launchers import extract_launchers
|
||||
@@ -133,6 +135,10 @@ _SYSTEM_HEADER = re.compile(
|
||||
r'\s*"biosFiles"\s*:\s*\[',
|
||||
)
|
||||
_FIELD = re.compile(r'"(\w+)"\s*:\s*"([^"]*)"')
|
||||
# A field written as a list of strings (`"md5": [ "a", "b" ]`), the form
|
||||
# batocera.linux 6b08b873 gives the md5 of a file several dumps satisfy.
|
||||
_LIST_FIELD = re.compile(r'"(\w+)"\s*:\s*\[([^\[\]]*)\]')
|
||||
_LIST_ITEM = re.compile(r'"([^"]*)"')
|
||||
|
||||
|
||||
def _slice_bios_block(source: str, start: int) -> tuple[str, int]:
|
||||
@@ -162,7 +168,9 @@ def parse_systems(source: str) -> dict[str, dict]:
|
||||
body, _ = _slice_bios_block(source, header.end() - 1)
|
||||
files = []
|
||||
for raw_entry in re.findall(r"\{[^{}]*\}", body):
|
||||
fields = dict(_FIELD.findall(raw_entry))
|
||||
fields: dict[str, str | list[str]] = dict(_FIELD.findall(raw_entry))
|
||||
for key, items in _LIST_FIELD.findall(raw_entry):
|
||||
fields[key] = _LIST_ITEM.findall(items)
|
||||
if fields.get("file"):
|
||||
files.append(fields)
|
||||
systems[header.group("key")] = {
|
||||
@@ -236,15 +244,19 @@ class Scraper(BaseScraper):
|
||||
# Declared paths are relative to the storage root: strip the
|
||||
# leading bios/ so destinations match base_destination
|
||||
destination = path[len("bios/"):] if path.startswith("bios/") else path
|
||||
values, as_list = md5_field(bios.get("md5"))
|
||||
native: dict[str, object] = {"native_name": entry["name"]}
|
||||
if as_list:
|
||||
native["md5_list"] = True
|
||||
req = BiosRequirement(
|
||||
name=destination.rsplit("/", 1)[-1],
|
||||
system=system_slug,
|
||||
md5=bios.get("md5", "") or None,
|
||||
md5=",".join(values) or None,
|
||||
alt_md5=bios.get("altmd5", "") or None,
|
||||
destination=destination,
|
||||
required=True,
|
||||
native_id=key,
|
||||
native={"native_name": entry["name"]},
|
||||
native=native,
|
||||
)
|
||||
if bios.get("zippedFile"):
|
||||
req.zipped_file = bios["zippedFile"]
|
||||
|
||||
Reference in new issue
Block a user