From 29940abed694cb96ad5792f8d45a0bb7f9ac8488 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:00:13 +0200 Subject: [PATCH] fix: keep unsourceable entries out of the asset store --- scripts/packresolve.py | 5 +++++ tests/test_game_data_homonyms.py | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/scripts/packresolve.py b/scripts/packresolve.py index 5cedb844..d1c6a4d2 100644 --- a/scripts/packresolve.py +++ b/scripts/packresolve.py @@ -155,6 +155,11 @@ def resolve_file( first_sha1 = (sha1[0] if sha1 else "") if isinstance(sha1, list) else (sha1 or "") md5_list = parse_md5_list(file_entry.get("md5")) first_md5 = md5_list[0] if md5_list else "" + if file_entry.get("unsourceable") and not (first_sha1 or first_md5): + # The asset store is addressed by file name: with no hash to check, + # it would hand back the same-named file of another game, which is + # exactly what the flag forbids. + return (path, status) if path else (None, "not_found") cached = fetch_large_file( name, expected_sha1=first_sha1, diff --git a/tests/test_game_data_homonyms.py b/tests/test_game_data_homonyms.py index 58bd00f6..bb5ea96b 100644 --- a/tests/test_game_data_homonyms.py +++ b/tests/test_game_data_homonyms.py @@ -170,6 +170,30 @@ class SecondPassKeepsIdentity(unittest.TestCase): self.assertEqual(extras["maintt/pak1.pk3"].get("sha1"), "c" * 40) +class ReleaseAssetIsNotANameMatch(unittest.TestCase): + """The release fallback keeps the flag the local resolver honours. + + ioquake3's baseq3/pak0.pk3 is flagged unsourceable; the local resolver + returned not_found, then the release fallback looked the asset up by its + bare name and handed Return to Castle Wolfenstein's demo pak to three + platform manifests. + """ + + def test_an_unsourceable_entry_without_hash_never_reaches_the_asset_store(self): + from unittest import mock + + import packresolve + + entry = {"name": "pak0.pk3", "unsourceable": "retail"} + db = {"files": {}, "indexes": {"by_name": {}, "by_md5": {}, "by_path_suffix": {}}} + with mock.patch.object(packresolve, "fetch_large_file", return_value="/x/pak0.pk3") as fetch: + self.assertEqual( + packresolve.resolve_file(entry, db, "bios", {}, offline=True), + (None, "not_found"), + ) + fetch.assert_not_called() + + class CollectionCarriesNoGameDataHomonym(unittest.TestCase): def test_the_profiles_resolve_no_game_data_to_another_game(self): database = REPO_ROOT / "database.json"