From 06ad1d10d33ef47246c45571e36474c3633f8487 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sat, 10 Oct 2026 07:32:21 +0200 Subject: [PATCH] fix: serve armsx2 the vulkan shader it reads --- emulators/armsx2.yml | 1 + tests/test_bundled_identity.py | 142 +++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 tests/test_bundled_identity.py diff --git a/emulators/armsx2.yml b/emulators/armsx2.yml index e996e4d5..99c842e9 100644 --- a/emulators/armsx2.yml +++ b/emulators/armsx2.yml @@ -506,6 +506,7 @@ files: - name: cas.glsl path: pcsx2/resources/shaders/vulkan/cas.glsl standalone_path: resources/shaders/vulkan/cas.glsl + sha1: "005cecf1a5e20b05407d55d0aad22cf81ad66c31" required: false bundled: true description: "Vulkan contrast adaptive sharpening shader" diff --git a/tests/test_bundled_identity.py b/tests/test_bundled_identity.py new file mode 100644 index 00000000..962093d3 --- /dev/null +++ b/tests/test_bundled_identity.py @@ -0,0 +1,142 @@ +"""A bundled file is identified by its bytes, not its name. + +ZEsarUX ships ace.rom and a patched alternate_roms/ace.rom, Azahar a default +keys.txt, ARMSX2 an OpenGL and a Vulkan cas.glsl. Declared without a hash, +each resolved by its bare name to whatever shared it: the patched ROM at the +original's slot, the Wii U disc keys at the 3DS key slot, the OpenGL shader +at the Vulkan one. An entry with a hash resolves by content first. + +529 bundled entries still carry neither a hash nor `unsourceable:`. The +count per profile may only fall: a new entry is written with the hash of +the file the emulator ships. +""" + +from __future__ import annotations + +import hashlib +import sys +import unittest +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(REPO_ROOT / "scripts")) + +from common import load_database, resolve_local_file # noqa: E402 + +UNIDENTIFIED = { + "abuse": 3, + "aethersx2": 1, + "altirra": 2, + "arcadeflashweb": 8, + "armsx2": 58, + "ax360e": 2, + "bluemsx": 14, + "c64-emu": 8, + "cdogs": 16, + "citra-mmj": 9, + "drastic": 10, + "eka2l1": 89, + "emucorev": 1, + "emucorex": 23, + "epsxe": 2, + "fpse": 7, + "fpse-ng": 7, + "future-pinball": 18, + "future-pinball-fploader": 54, + "gba-emu": 1, + "hakux": 1, + "hcl": 3, + "hurrican": 1, + "idtech4a++": 1, + "ikemen": 32, + "ines": 2, + "j2me-loader": 14, + "jl-mod": 22, + "kemulator": 4, + "mastergear": 2, + "mfme": 58, + "msx-emu": 10, + "my-boy": 1, + "neo-emu": 3, + "nethersx2": 2, + "nethersx2-turnip": 2, + "nethersx2-turnip-classic": 2, + "nuance": 1, + "pcee2": 1, + "pcsx2": 1, + "project64": 9, + "sixtyforce": 5, + "skyline": 9, + "ssf": 1, + "supermodel-dojo": 4, + "x1-box": 1, + "xendroid": 1, + "xm6pro68k": 2, + "yaps2": 1, +} + + +def _unidentified() -> dict[str, int]: + counts: dict[str, int] = {} + for path in sorted((REPO_ROOT / "emulators").glob("*.yml")): + document = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + for entry in document.get("files") or []: + if not isinstance(entry, dict) or not entry.get("bundled"): + continue + if entry.get("unsourceable") or entry.get("type") == "directory": + continue + if any(entry.get(key) for key in ("sha1", "md5", "sha256", "crc32")): + continue + counts[path.stem] = counts.get(path.stem, 0) + 1 + return counts + + +class BundledFilesCarryTheirIdentity(unittest.TestCase): + def test_no_profile_adds_an_unidentified_bundled_file(self): + grown = { + name: (count, UNIDENTIFIED.get(name, 0)) + for name, count in _unidentified().items() + if count > UNIDENTIFIED.get(name, 0) + } + self.assertEqual(grown, {}, "bundled entries need the shipped file's hash") + + +class TheFixedEntriesGetTheirOwnBytes(unittest.TestCase): + CASES = ( + ("azahar", "sysdata/keys.txt"), + ("azaharplus", "sysdata/keys.txt"), + ("zesarux", "ace.rom"), + ("zesarux", "alternate_roms/ace.rom"), + ("zesarux", "coleco.rom"), + ("zesarux", "trdos.rom"), + ("armsx2", "pcsx2/resources/shaders/vulkan/cas.glsl"), + ) + + def test_each_resolves_to_the_declared_hash(self): + db_path = REPO_ROOT / "database.json" + if not db_path.exists(): + self.skipTest("database.json is not built") + db = load_database(str(db_path)) + for profile, destination in self.CASES: + with self.subTest(profile=profile, destination=destination): + document = yaml.safe_load( + (REPO_ROOT / "emulators" / f"{profile}.yml").read_text(encoding="utf-8") + ) + entry = next( + e for e in document["files"] + if (e.get("path") or e["name"]) == destination + ) + local, _status = resolve_local_file( + {**entry, "source_profile": profile}, db, dest_hint=destination + ) + self.assertIsNotNone(local) + self.assertEqual( + hashlib.sha1((REPO_ROOT / local).read_bytes()).hexdigest(), + entry["sha1"], + ) + + +if __name__ == "__main__": + unittest.main()