From 604ab3371b8dc8615494f62353d9df444fc6e9fb Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 21 Oct 2026 10:09:51 +0200 Subject: [PATCH] fix: give a suffixed release its emulator's tree _foreign_tree compared owner tokens by equality, so cxbx-reloaded-2023 read bios/Other/cxbx-reloaded/ as another emulator's tree and the case-folded fallback served xemu's encrypted EEPROM. The leading whole words of a profile key now name its own tree too. Across every profile entry this moves cxbx-reloaded-2023's EEPROM.bin to the decrypted dump, turns eight name matches on the same files into path matches, and gives solarus-2.0.0 the Solarus tree's gamecontrollerdb.txt. --- scripts/common.py | 12 +++++++++- tests/test_foreign_tree.py | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/scripts/common.py b/scripts/common.py index 35b53465..89f8daef 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -934,11 +934,21 @@ def _affinity_tokens(text: str) -> set[str]: def _owner_tokens(file_entry: dict) -> set[str]: - """Tokens naming the profile that asks for the file.""" + """Tokens naming the profile that asks for the file. + + A profile named after another with a suffix (cxbx-reloaded-2023, + mame4droid-2024) is a release of that emulator, and the tree named by + the shorter name is its own: cxbx-reloaded-2023 was served xemu's + encrypted EEPROM because bios/Other/cxbx-reloaded/ read as foreign. + Only whole leading words count, so dolphin-mmjr2 is not dolphin-mmjr. + """ tokens: set[str] = set() for owner in (file_entry.get("source_profile"), file_entry.get("source_emulator")): if owner: tokens |= _affinity_tokens(str(owner).replace(" ", "")) + profile = str(file_entry.get("source_profile") or "").casefold() + words = [word for word in re.split(r"[^0-9a-z]+", profile) if word] + tokens |= {"".join(words[:end]) for end in range(1, len(words))} return tokens diff --git a/tests/test_foreign_tree.py b/tests/test_foreign_tree.py index e4530590..4fba40a8 100644 --- a/tests/test_foreign_tree.py +++ b/tests/test_foreign_tree.py @@ -68,5 +68,50 @@ class ForeignTree(unittest.TestCase): self.assertNotEqual(status, "path_exact") +class AReleaseNamedAfterTheTree(unittest.TestCase): + """cxbx-reloaded-2023 asks for EEPROM.bin. Its emulator's tree holds the + decrypted dump Cxbx reads, xemu's aliases name the encrypted retail one + eeprom.bin. The tree read as foreign to the suffixed profile, so the + case-folded fallback served the encrypted dump.""" + + def setUp(self): + self._tmp = tempfile.TemporaryDirectory() + self._cwd = os.getcwd() + os.chdir(self._tmp.name) + files = {} + for rel, payload in ( + ("bios/Other/cxbx-reloaded/EEPROM.bin", b"decrypted"), + ("bios/Microsoft/Xbox/eeprom.bin", b"encrypted"), + ): + path = Path(rel) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(payload) + sha1 = hashlib.sha1(payload).hexdigest() + files[sha1] = {"path": rel, "name": path.name, "size": len(payload), + "sha1": sha1, "md5": hashlib.md5(payload).hexdigest(), + "sha256": hashlib.sha256(payload).hexdigest(), + "crc32": f"{len(files) + 1:08x}"} + self.db = {"files": files, "indexes": generate_db.build_indexes(files, {})} + + def tearDown(self): + os.chdir(self._cwd) + self._tmp.cleanup() + + def _resolve(self, owner: str, name: str): + entry = {"name": name, "source_profile": owner} + return resolve_local_file(entry, self.db, {}) + + def test_the_suffixed_release_reads_its_emulator_tree(self): + local, _status = self._resolve("cxbx-reloaded-2023", "EEPROM.bin") + self.assertEqual(local, "bios/Other/cxbx-reloaded/EEPROM.bin") + + def test_only_whole_leading_words_name_the_tree(self): + """dolphin-mmjr2 is not a release of dolphin-mmjr: mmjr2 is one word.""" + from common import _owner_tokens + + self.assertNotIn("dolphinmmjr", _owner_tokens({"source_profile": "dolphin-mmjr2"})) + self.assertIn("cxbxreloaded", _owner_tokens({"source_profile": "cxbx-reloaded-2023"})) + + if __name__ == "__main__": unittest.main()