fix: accept every version a profile lists by name

A profile lists each version its code takes under one name. The core
extra carries the first; a held copy matching another entry of the same
profile is no longer reported as a hash discrepancy.
This commit is contained in:
Abdessamad Derraz committed 2026-10-17 18:11:38 +02:00
1 parent 490fefac97
commit 2e52f06896
2 files changed
+75 -1

No files matched your search

+30 -1
View File
@@ -538,6 +538,33 @@ def _platform_pack_name(
return f"{stem}{tags}_BIOS_Pack{_system_tag(system_filter)}.zip" return f"{stem}{tags}_BIOS_Pack{_system_tag(system_filter)}.zip"
def _matches_an_alternative(fe: dict, local_path: str, profiles: dict) -> bool:
"""Whether the copy is another version the same profile accepts there.
A profile lists every version its code takes under one name (each GRP
of Raze's table, eduke32's demo and retail NWINTER.GRP); the extra
carries the first, and the held copy matching a sibling is no mismatch.
"""
profile = profiles.get(fe.get("source_profile") or "", {})
name = fe.get("source_name") or fe.get("name")
path = fe.get("source_path") or name
siblings = [
entry for entry in profile.get("files") or []
if isinstance(entry, dict) and entry.get("name") == name
and (entry.get("path") or name) == path
]
if len(siblings) < 2:
return False
actual = compute_hashes(local_path, frozenset({"sha1", "md5", "crc32"}))
for entry in siblings:
for algorithm in ("sha1", "md5", "crc32"):
declared = entry.get(algorithm)
values = declared if isinstance(declared, list) else [declared]
if any(v and str(v).lower() == actual.get(algorithm, "") for v in values):
return True
return False
def generate_pack( def generate_pack(
platform_name: str, platform_name: str,
platforms_dir: str, platforms_dir: str,
@@ -996,7 +1023,9 @@ def generate_pack(
) )
if disagreement: if disagreement:
core_discrepancies.append(f"{full_dest} -{disagreement}") core_discrepancies.append(f"{full_dest} -{disagreement}")
if status == "hash_mismatch": if status == "hash_mismatch" and not _matches_an_alternative(
fe, local_path, emu_profiles or {}
):
# The core's declared hash comes from its source, the local dump # The core's declared hash comes from its source, the local dump
# is what the collection holds. Shipping it keeps the emulator # is what the collection holds. Shipping it keeps the emulator
# working when the code never reads the hash; the divergence is # working when the code never reads the hash; the divergence is
+45
View File
@@ -15,6 +15,7 @@ import sys
import tempfile import tempfile
import unittest import unittest
import zipfile import zipfile
import zlib
from pathlib import Path from pathlib import Path
from unittest import mock from unittest import mock
@@ -2861,3 +2862,47 @@ class ADuplicateYamlKeyIsRefused(unittest.TestCase):
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
class AnAcceptedVersionIsNoCoreDiscrepancy(unittest.TestCase):
"""eduke32 lists the retail and demo NWINTER.GRP under one name: the extra
carries the first, and a held demo copy is a version the code takes."""
PROFILE = {"files": [
{"name": "NWINTER.GRP", "size": 9, "crc32": "00000000",
"validation": ["size", "crc32"]},
{"name": "NWINTER.GRP", "size": 4, "crc32": "",
"validation": ["size", "crc32"]},
]}
def _extra(self) -> dict:
# The shape find_undeclared_files gives a core extra: source_path
# defaults to the name when the profile declares no path.
return {"name": "NWINTER.GRP", "source_name": "NWINTER.GRP",
"source_path": "NWINTER.GRP", "source_profile": "eduke32"}
def test_a_sibling_version_matches(self):
from scripts.generate_pack import _matches_an_alternative
with tempfile.TemporaryDirectory(dir=TMP_ROOT) as tmp:
held = Path(tmp) / "NWINTER.GRP"
held.write_bytes(b"demo")
profile = json.loads(json.dumps(self.PROFILE))
profile["files"][1]["crc32"] = f"{zlib.crc32(b'demo'):08x}"
self.assertTrue(_matches_an_alternative(
self._extra(), str(held), {"eduke32": profile}))
def test_a_copy_no_entry_declares_still_disagrees(self):
from scripts.generate_pack import _matches_an_alternative
with tempfile.TemporaryDirectory(dir=TMP_ROOT) as tmp:
held = Path(tmp) / "NWINTER.GRP"
held.write_bytes(b"other")
profile = json.loads(json.dumps(self.PROFILE))
profile["files"][1]["crc32"] = f"{zlib.crc32(b'demo'):08x}"
self.assertFalse(_matches_an_alternative(
self._extra(), str(held), {"eduke32": profile}))
def test_the_pack_consults_the_alternatives(self):
source = (ROOT / "scripts" / "generate_pack.py").read_text()
self.assertRegex(source, r'status == "hash_mismatch" and not _matches_an_alternative\(')