mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-16 16:33:27 -05:00
fix: hash recalbox archives by their members
This commit is contained in:
1 parent
3e14c9df1f
commit
346950156c
4 files changed
+92
-16
No files matched your search
+14
-14
@@ -238,9 +238,9 @@
|
||||
}
|
||||
],
|
||||
"total_files": 3378,
|
||||
"total_size": 4262186697,
|
||||
"total_size": 4263070113,
|
||||
"pack_files": 3380,
|
||||
"pack_size": 4262186697,
|
||||
"pack_size": 4263070113,
|
||||
"total_omitted": 1,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -5318,10 +5318,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "dc/hod2bios.zip",
|
||||
"sha1": "0b5cc968644c936d4819c1eb5f623f5f3f83eda1",
|
||||
"sha256": "8ae5a99e4ffc15b92e4d590c9e14558743e8f2068947a23e942dfc038ca326c6",
|
||||
"size": 1465991,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/hod2bios.zip.0b5cc968",
|
||||
"sha1": "9b9c9f0af3de914de677ba8e19f4190673142c1b",
|
||||
"sha256": "c90c754f36d4ab0c927da1f79883cb322a4f94b7cebe9cc7f401151a1b8f1a5a",
|
||||
"size": 1057881,
|
||||
"repo_path": "bios/Arcade/Arcade/hod2bios.zip",
|
||||
"cores": null,
|
||||
"systems": [
|
||||
"sega-dreamcast-arcade"
|
||||
@@ -5340,10 +5340,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "dc/naomigd.zip",
|
||||
"sha1": "f9ad4a4c6b0bbbe39ba358690a48f763ecbd98f0",
|
||||
"sha256": "1280f4185ac4f7352ab85e49d71db1ca0b3e679c6430bd2c8545088042117eb1",
|
||||
"size": 3371765,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/naomigd.zip",
|
||||
"sha1": "4b92936f396c8199f745d3ed0ff588a324b8dd70",
|
||||
"sha256": "793db2a859be2d814d334ab511811c42c6727a16841e7627fa7c33171c3de441",
|
||||
"size": 4972898,
|
||||
"repo_path": "bios/Arcade/Arcade/naomigd.zip",
|
||||
"cores": null,
|
||||
"systems": [
|
||||
"sega-dreamcast-arcade"
|
||||
@@ -5892,10 +5892,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "neocdz.zip",
|
||||
"sha1": "7bb26d1e5d1e930515219cb18bcde5b7b23e2eda",
|
||||
"sha256": "2e93af5848080ea04d17a7841b742f009330d30e4ff40c3410547581d921c892",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/SNK/Neo Geo CD/neocd.bin",
|
||||
"sha1": "4f28af31ca0defdd73d80edec2fa296908e624dc",
|
||||
"sha256": "6aa507a5dc33c45d2592bf2bc77eb3e497762221ddd9225f18583a5c55d38667",
|
||||
"size": 214681,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.4f28af31",
|
||||
"cores": null,
|
||||
"systems": [
|
||||
"snk-neogeo-cd"
|
||||
|
||||
@@ -415,6 +415,8 @@ platforms:
|
||||
source_url: https://gitlab.com/recalbox/recalbox/-/raw/master/board/recalbox/fsoverlay/recalbox/share_init/system/.emulationstation/es_bios.xml
|
||||
source_format: xml
|
||||
hash_type: md5
|
||||
# Bios.cpp:64-68 hashes a .zip with Zip::Md5Composite, never its bytes
|
||||
zip_digest: composite
|
||||
schedule: monthly
|
||||
target_scraper: null
|
||||
target_source: null
|
||||
|
||||
+25
-2
@@ -197,6 +197,22 @@ def load_platform_config(platform_name: str, platforms_dir: str = "platforms") -
|
||||
if key != "cores" and key not in config:
|
||||
config[key] = val
|
||||
|
||||
# Recalbox hashes a .zip by its members, so a plain md5 never answers one
|
||||
# of its archive declarations: the resolver reads the mark per entry.
|
||||
if config.get("zip_digest") == "composite":
|
||||
config["systems"] = {
|
||||
sys_id: {
|
||||
**system,
|
||||
"files": [
|
||||
{**f, "zip_digest": "composite"}
|
||||
if str(f.get("name", "")).lower().endswith(".zip")
|
||||
else f
|
||||
for f in system.get("files", [])
|
||||
],
|
||||
}
|
||||
for sys_id, system in config.get("systems", {}).items()
|
||||
}
|
||||
|
||||
_platform_config_cache[cache_key] = config
|
||||
return config
|
||||
|
||||
@@ -848,7 +864,12 @@ def resolve_local_file(
|
||||
# .variants/ pattern: filename like "neogeo.zip.fc398ab4"
|
||||
return any(bn.startswith(n + ".") for n in _name_set)
|
||||
|
||||
if md5_list and not zipped_file:
|
||||
# A frontend that hashes an archive by its members (Recalbox) never reads
|
||||
# the container's own md5: neocd.bin's md5 is also the composite of a
|
||||
# one-member neocdz.zip, and the raw ROM shipped under the archive's name.
|
||||
composite_only = file_entry.get("zip_digest") == "composite"
|
||||
|
||||
if md5_list and not zipped_file and not composite_only:
|
||||
for md5_candidate in md5_list:
|
||||
sha1_match = by_md5.get(md5_candidate)
|
||||
if sha1_match and sha1_match in files_db:
|
||||
@@ -1014,6 +1035,8 @@ def resolve_local_file(
|
||||
|
||||
if candidates:
|
||||
if md5_set and not (sha1_candidates or sha256_candidates or crc_candidates):
|
||||
if composite_only:
|
||||
candidates.sort(key=lambda c: "/.variants/" in c[0])
|
||||
for path, db_md5, _size in candidates:
|
||||
if ".zip" in os.path.basename(path):
|
||||
try:
|
||||
@@ -1022,7 +1045,7 @@ def resolve_local_file(
|
||||
return path, "md5_composite_exact"
|
||||
except (zipfile.BadZipFile, OSError):
|
||||
pass
|
||||
if db_md5.lower() in md5_set:
|
||||
if db_md5.lower() in md5_set and not composite_only:
|
||||
return path, "md5_exact"
|
||||
# When zipped_file is set, only accept candidates that contain it
|
||||
if zipped_file:
|
||||
|
||||
@@ -218,5 +218,56 @@ class TheArchiveHoldingTheCoreMembers(unittest.TestCase):
|
||||
self.assertNotIn('{"name": archive', source)
|
||||
|
||||
|
||||
class AnArchiveHashedByItsMembers(unittest.TestCase):
|
||||
"""Recalbox hashes a .zip with Zip::Md5Composite (Bios.cpp:64-68). Its
|
||||
neocdz.zip pin is neocd.bin's md5, which is also the composite of a
|
||||
one-member archive, and the md5 step served the raw ROM under the
|
||||
archive's name: Recalbox cannot open it and reports the BIOS red."""
|
||||
|
||||
def setUp(self):
|
||||
directory = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
|
||||
self.addCleanup(shutil.rmtree, directory, True)
|
||||
rom = b"bios" * 8
|
||||
raw = directory / "bios/neocd.bin"
|
||||
archive = directory / "bios/.variants/neocdz.zip.4f28af31"
|
||||
archive.parent.mkdir(parents=True)
|
||||
raw.write_bytes(rom)
|
||||
with zipfile.ZipFile(archive, "w") as zf:
|
||||
zf.writestr("neocd.bin", rom)
|
||||
files, by_md5, by_name = {}, {}, {}
|
||||
for path, name in ((raw, "neocd.bin"), (archive, "neocdz.zip")):
|
||||
data = path.read_bytes()
|
||||
sha1, md5 = hashlib.sha1(data).hexdigest(), hashlib.md5(data).hexdigest()
|
||||
files[sha1] = {"path": str(path), "name": name, "sha1": sha1, "md5": md5}
|
||||
by_md5[md5] = sha1
|
||||
by_name.setdefault(name, []).append(sha1)
|
||||
self.db = {"files": files, "indexes": {"by_name": by_name, "by_md5": by_md5,
|
||||
"by_path_suffix": {}, "by_crc32": {}}}
|
||||
self.entry = {"name": "neocdz.zip", "md5": hashlib.md5(rom).hexdigest()}
|
||||
self.archive = str(archive)
|
||||
|
||||
def test_the_archive_answers_a_composite_frontend(self):
|
||||
from common import resolve_local_file
|
||||
|
||||
path, status = resolve_local_file({**self.entry, "zip_digest": "composite"}, self.db)
|
||||
self.assertEqual((path, status), (self.archive, "md5_composite_exact"))
|
||||
|
||||
def test_a_plain_digest_still_takes_the_md5_match(self):
|
||||
from common import resolve_local_file
|
||||
|
||||
path, _status = resolve_local_file(self.entry, self.db)
|
||||
self.assertTrue(path.endswith("neocd.bin"))
|
||||
|
||||
def test_the_registry_marks_recalbox_archives(self):
|
||||
from common import load_platform_config
|
||||
|
||||
config = load_platform_config("recalbox", str(REPO_ROOT / "platforms"))
|
||||
marks = {
|
||||
(f["name"].lower().endswith(".zip"), f.get("zip_digest"))
|
||||
for system in config["systems"].values() for f in system["files"]
|
||||
}
|
||||
self.assertEqual(marks, {(True, "composite"), (False, None)})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user