From 5330ed1950a9d69ba4c97031d6bf407bb4c0569a Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 10 Aug 2026 19:10:26 +0200 Subject: [PATCH] fix: keep the cache usable when a path is both --- emulators/gam4980.yml | 12 ++++++------ emulators/gsplus.yml | 4 ++-- emulators/vice.yml | 6 +++--- scripts/upstream.py | 13 +++++++++++-- tests/test_upstream.py | 11 +++++++++++ 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/emulators/gam4980.yml b/emulators/gam4980.yml index 56f9bf09..514fb07b 100644 --- a/emulators/gam4980.yml +++ b/emulators/gam4980.yml @@ -16,14 +16,14 @@ systems: # Original code from https://codeberg.org/iyzsong/gam4980, based on the # BA4988 BBK simulator by Wuyun. Uses a 6502-based CPU with banked ROM access. # -# ROM loading (src/libretro.c:692-760, sys_init): +# ROM loading (libretro.c:692-760, sys_init): # Opens romdir/8.BIN and reads 0x200000 bytes (2 MB) into sys.rom_8 (font ROM). # Opens romdir/E.BIN and reads 0x200000 bytes (2 MB) into sys.rom_e (OS ROM). # The upstream has error handling (error_msg + RETRO_ENVIRONMENT_SHUTDOWN on # missing files). The fork (ThisBoringWorld) removed these checks -- crashes # on missing files. # -# Memory map (src/libretro.c:109-110, 300-305): +# Memory map (libretro.c:109-110, 300-305): # 0x800000-0x9FFFFF rom_8 (font ROM, 8.BIN) # 0xE00000-0xFFFFFF rom_e (OS ROM, E.BIN) # @@ -42,19 +42,19 @@ files: required: true system: bbk-longman-4980 note: "Font ROM (2 MB). Loaded at 0x800000." - source_ref: "src/libretro.c:716-718" + source_ref: "libretro.c:716-718" - name: "gam4980/E.BIN" size: 2097152 required: true system: bbk-longman-4980 note: "OS ROM (2 MB). Loaded at 0xE00000." - source_ref: "src/libretro.c:720-722" + source_ref: "libretro.c:720-722" platform_details: bios_mapping: target: "0x800000-0x9FFFFF (font), 0xE00000-0xFFFFFF (OS) in banked address space" - source_ref: "src/libretro.c:109-110, 300-305" + source_ref: "libretro.c:109-110, 300-305" note: | sys.rom_8 and sys.rom_e are flat 2 MB arrays. The CPU accesses them through bank-switched memory (PA() macro resolves virtual to physical @@ -62,7 +62,7 @@ platform_details: the banking system. load_path: - source_ref: "src/libretro.c:1173-1175" + source_ref: "libretro.c:1173-1175" note: | retro_init() builds the path as {system_directory}/gam4980/ and passes it to sys_init(). Files must be named exactly 8.BIN and diff --git a/emulators/gsplus.yml b/emulators/gsplus.yml index 32a08009..9a4ec684 100644 --- a/emulators/gsplus.yml +++ b/emulators/gsplus.yml @@ -31,7 +31,7 @@ files: aliases: [ROM.01, ROM.03, APPLE2GS.ROM, APPLE2GS.ROM2, xgs.rom, XGS.ROM, Rom03gd, 342-0077-b] required: true description: "Apple IIGS system ROM" - source_ref: "config.c:131,403-406,1092-1149" + source_ref: "gsplus/src/config.c:131,403-406,1092-1149" note: "Accepted sizes: 32768 (Apple //e), 131072 (ROM 01), 262144 (ROM 03)" validation: [size] @@ -42,5 +42,5 @@ files: size: 256 description: "Disk II controller PROM (slot 6)" validation: [size] - source_ref: "config.c:414-415,1168-1220" + source_ref: "gsplus/src/config.c:414-415,1168-1220" note: "Built-in PROM generated from XOR diffs against main ROM" diff --git a/emulators/vice.yml b/emulators/vice.yml index ae82cd01..388c7438 100644 --- a/emulators/vice.yml +++ b/emulators/vice.yml @@ -475,7 +475,7 @@ files: md5: 57af4ae21d4b705c2991d98ed5c1f7b8 crc32: f833d117 note: "C64 BASIC V2 ROM (DTV copy). Embedded in core." - source_ref: "vice/data/C64DTV/" + source_ref: "vice/data/C64DTV/basic-901226-01.bin" - name: "vice/C64DTV/chargen-901225-01.bin" system: commodore-c64dtv @@ -487,7 +487,7 @@ files: md5: 12a4202f5331d45af846af6c58fba946 crc32: ec4272ee note: "C64 Character Generator (DTV copy). Embedded in core." - source_ref: "vice/data/C64DTV/" + source_ref: "vice/data/C64DTV/chargen-901225-01.bin" - name: "vice/C64DTV/kernal-901227-03.bin" system: commodore-c64dtv @@ -499,7 +499,7 @@ files: md5: 39065497630802346bce17963f13c092 crc32: dbe3e7c7 note: "C64 Kernal Rev 3 (DTV copy). Embedded in core." - source_ref: "vice/data/C64DTV/" + source_ref: "vice/data/C64DTV/kernal-901227-03.bin" # ------------------------------------------------------- # VIC-20 ROMs (vice_xvic) diff --git a/scripts/upstream.py b/scripts/upstream.py index c60a1333..f7eeee57 100644 --- a/scripts/upstream.py +++ b/scripts/upstream.py @@ -213,8 +213,17 @@ def cache_path(cache_dir: str, repo: Repo, sha: str, path: str) -> Path: def write_cache(target: Path, text: str) -> None: - """Atomic write: unique scratch in the target directory, then replace.""" - target.parent.mkdir(parents=True, exist_ok=True) + """Atomic write: unique scratch in the target directory, then replace. + + A profile can cite both a directory and files inside it, and the first + citation caches `vice/data/C64DTV` as a file, which then blocks the + directory the second one needs. The cache is derived data, so a collision + skips the write rather than failing the read. + """ + try: + target.parent.mkdir(parents=True, exist_ok=True) + except (FileExistsError, NotADirectoryError): + return fd, scratch = tempfile.mkstemp(dir=str(target.parent), suffix=".part") try: with os.fdopen(fd, "w", encoding="utf-8") as fh: diff --git a/tests/test_upstream.py b/tests/test_upstream.py index d2b8fa87..6537b88e 100644 --- a/tests/test_upstream.py +++ b/tests/test_upstream.py @@ -309,6 +309,17 @@ class TestCache(unittest.TestCase): self.assertEqual([p.name for p in siblings], ["f.txt"]) +class TestCacheCollision(unittest.TestCase): + def test_a_file_where_a_directory_is_needed_skips_the_write(self): + # A profile citing both a directory and files inside it caches the + # directory as a file first; that must not break the files. + with tempfile.TemporaryDirectory() as root: + base = Path(root) + (base / "C64DTV").write_text("", encoding="utf-8") + upstream.write_cache(base / "C64DTV" / "basic.bin", "data") + self.assertTrue((base / "C64DTV").is_file()) + + class TestRevisions(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory()