From b29500846bcf51dc09bac1106e2c9ad1fce9115a Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 11 Nov 2026 11:57:12 +0100 Subject: [PATCH] feat: read retrobat's saturn and atari builds RetroBat installs its stable archives, so each config_reads entry is read at the build the binary names: Hatari 2.4.1, hatariB 0.3, Hatari2014 at ab55c3e from the buildbot, Kronos 2.7.0 and Ymir 0.3.3. SSF ships PreviewVer R28 and YabaSanshiro 1.20.32 has no public source: binary_sha256 names the file their address refs are read in. The seven RetroBat records citing an option list now hold. --- emulators/hatari.yml | 8 ++++++++ emulators/hatari2014.yml | 11 +++++++++++ emulators/hatarib.yml | 11 +++++++++++ emulators/kronos.yml | 13 +++++++++++++ emulators/ssf.yml | 11 +++++++++++ emulators/yabasanshiro.yml | 13 +++++++++++++ emulators/ymir.yml | 16 ++++++++++++++++ schemas/emulator.schema.json | 6 ++++-- scripts/profile_sync.py | 5 ++++- scripts/validate_schemas.py | 2 +- tests/test_launcher_evidence.py | 33 +++++++++++++++++++++++++++++++++ 11 files changed, 125 insertions(+), 4 deletions(-) diff --git a/emulators/hatari.yml b/emulators/hatari.yml index 940f07f95..207a7a1f1 100644 --- a/emulators/hatari.yml +++ b/emulators/hatari.yml @@ -76,6 +76,14 @@ config_reads: keys: - key: "[ROM] szTosImageFileName" source_ref: "upstream src/configuration.c:480, upstream src/tos.c:975-986" + - file: "*.cfg" + platforms: [retrobat] + revision: "4371dcd647fc85d31c0629400adaeaa4212040d9" + revision_from: "Hatari v2.4.1, compiled Aug 3 2022, in hatari.exe of RetroBat's stable hatari.7z (2026-04-04), the v2.4.1 tag" + source_ref: "upstream src/options.c:228-229, 1099-1108, upstream src/configuration.c:967-976, 1024" + keys: + - key: "[ROM] szTosImageFileName" + source_ref: "upstream src/configuration.c:413, 884, upstream src/tos.c:942-955" notes: | The main branch of libretro/hatari is the upstream Hatari tree at diff --git a/emulators/hatari2014.yml b/emulators/hatari2014.yml index f9e2e2ecf..ff7f84be6 100644 --- a/emulators/hatari2014.yml +++ b/emulators/hatari2014.yml @@ -22,6 +22,17 @@ systems: - atari-tt - atari-falcon +config_reads: + - file: "core options" + platforms: [retrobat] + revision: "ab55c3ed0e620c91e7f059a6d3fbef7acf9bfca8" + revision_from: "library_version 1.8 ab55c3e of hatari2014_libretro.dll from the libretro buildbot windows x86_64 nightly (2026-11-10), which RetroBat installs when its own repository has no copy" + source_ref: "source libretro/libretro.c:264-300, 372, 393-413, 437-440, 984-1001, 1418, source libretro/libretro_core_options.h:129-145" + keys: + - key: "hatari_tosimage" + source_ref: "source libretro/libretro.c:617, 639-649, 1612-1617, source src/main.c:913-917, source src/tos.c:575-590" + condition: "a value other than default is a file of system/hatari/tos ending in img (libretro/libretro.c:288-299)" + notes: | Hatari 2014 is the libretro port of Hatari 1.8.0 (src/includes/version.h:16), the July 2014 release, kept on the hitari2014-mercurial branch of diff --git a/emulators/hatarib.yml b/emulators/hatarib.yml index 502f6fec2..97b3fa8b9 100644 --- a/emulators/hatarib.yml +++ b/emulators/hatarib.yml @@ -20,6 +20,17 @@ systems: - atari-tt - atari-falcon +config_reads: + - file: "core options" + platforms: [retrobat] + revision: "9c5ee608504d7e174dce99ac9237151863d78347" + revision_from: "library_version v0.3 9c5ee60 of hatarib_libretro.dll in RetroBat's stable cores/hatarib_libretro.dll.zip (2026-04-04), the 0.3 tag" + source_ref: "core/core.c:963-976, core/core_config.c:961-969, 985, 1286-1318, 1408-1413, hatari/src/main.c:898, 921-925, 1019" + keys: + - key: "hatarib_tos" + source_ref: "core/core_config.c:1082-1104, core/core_file.c:256-259, 763-769, hatari/src/tos.c:996-1056" + condition: " is offered only when system/tos.img exists, a hatarib/ value only for a file with an img, rom, bin or tos extension (core/core_config.c:1293-1318, core/core_file.c:707-716)" + notes: | hatariB carries Hatari 2.6.1 in its hatari/ directory, every change fenced by __LIBRETRO__ (DEVELOP.md:7-14, DEVELOP.md:44-48), and diff --git a/emulators/kronos.yml b/emulators/kronos.yml index 16ed978ee..8cef01caf 100644 --- a/emulators/kronos.yml +++ b/emulators/kronos.yml @@ -20,6 +20,19 @@ systems: - sega-saturn - sega-stv +config_reads: + - file: "kronos.ini" + platforms: [retrobat] + revision: "6750417f3d6fef1b7be9adf52e707cc78aa41ee0" + revision_from: "Kronos 2.7.0 without a commit id in kronos.exe of RetroBat's stable kronos.7z, linked 2024-12-31: the last FCare/Kronos commit before that date, the cited files unchanged from aca9865c to the 2.7.0_official_release tag 58352d6d" + source_ref: "upstream yabause/src/port/qt/Settings.cpp:50-61, 95-103, upstream yabause/src/port/qt/Settings.h:25-26, upstream yabause/src/CMakeLists.txt:763, upstream yabause/src/port/qt/main.cpp:55-57" + keys: + - key: "[1.0] General\\EnableEmulatedBios" + source_ref: "upstream yabause/src/port/qt/YabauseThread.cpp:392-393" + - key: "[1.0] General\\Bios" + source_ref: "upstream yabause/src/port/qt/YabauseThread.cpp:394-395, upstream yabause/src/port/qt/VolatileSettings.cpp:20-27, upstream yabause/src/ctrl/src/yabause.c:497-509, 575-587, 626-630, upstream yabause/src/sys/memory/src/memory.c:1343-1346" + condition: "General\\EnableEmulatedBios false; an empty path runs the HLE BIOS, a path that fails to load stops init" + notes: | Kronos is a Sega Saturn and ST-V arcade emulator by FCare, forked from Yabause. Requires OpenGL 4.3 compute shaders. diff --git a/emulators/ssf.yml b/emulators/ssf.yml index 45f70463b..5424a8a0c 100644 --- a/emulators/ssf.yml +++ b/emulators/ssf.yml @@ -21,6 +21,17 @@ systems: - sega-stv mode: standalone +config_reads: + - file: "SSF.ini" + platforms: [retrobat] + binary_sha256: "00f43e448592e5e8359eba7da4de5b3a16e6840ad8faf9cf182f738c4e25293a" + revision_from: "SSF PreviewVer R28 (string at 0x1a46634): the 32-bit SSF.exe of RetroBat's stable ssf.7z (2026-04-04), byte-identical to the one in SSF_PreviewVer_R28.zip of release tag PreviewVer" + source_ref: "SSF.exe 0x5b0b41-0x5b0b81, SSF.exe 0x5b9785, SSF.exe 0x5ac8cc-0x5ac8e2, SSF.exe 0x5ac94c, SSF.exe 0x5953d0, SSF.exe 0x5954c3-0x5954db, SSF.exe 0xb35e20" + keys: + - key: "[Peripheral] SaturnBIOS" + source_ref: "SSF.exe 0x52bc85-0x52bcd8, SSF.exe 0x5ac9e4, SSF.exe 0x5aab87-0x5aab93, SSF.exe 0x5b2905-0x5b2936, SSF.exe 0xb36022-0xb360cf" + condition: "[Program4] NoBIOS off (0 in the embedded template) and Ctrl not held at launch, in Saturn mode (no left Shift, no zip); an empty path or a read short of 0x80000 bytes boots without BIOS" + notes: | Sega Saturn and ST-V emulator by Shima, closed source and distributed as a binary. Two artifacts are read here and they describe the same emulator: the diff --git a/emulators/yabasanshiro.yml b/emulators/yabasanshiro.yml index 3afe0efdc..513e95600 100644 --- a/emulators/yabasanshiro.yml +++ b/emulators/yabasanshiro.yml @@ -39,6 +39,19 @@ data_root: source_ref: "yabause/src/retro_arena/main.cpp:292-301" note: "Created at startup. Holds backup.bin, keymapv2.json, the per-game settings files, save states and screenshots. No firmware is read from it." +config_reads: + - file: "yabause.ini" + platforms: [retrobat] + binary_sha256: "54f89ad3d45f078422616b794e9f7d400a37af0fbb00b31d3ba5a3b66516bf27" + revision_from: "yabasanshiro.exe of RetroBat's stable yabasanshiro.7z (2026-08-08), version 1.20.32 (0x141206de8), the yabasanshiro-1.20.32-3989ac release of 2026-07-31 whose source is not public" + source_ref: "yabasanshiro.exe 0x14001ae2c-0x14001ae58, yabasanshiro.exe 0x1400cea2c-0x1400cea33, yabasanshiro.exe 0x1400ce4d5-0x1400ce59e, yabasanshiro.exe 0x1400ce5a3-0x1400ce61f, yabasanshiro.exe 0x1400ce44a-0x1400ce496, yabasanshiro.exe 0x1400ce339-0x1400ce394" + keys: + - key: "[0.9.11] General\\EnableEmulatedBios" + source_ref: "yabasanshiro.exe 0x1400d248f-0x1400d2512" + - key: "[0.9.11] General\\Bios" + source_ref: "yabasanshiro.exe 0x1400d251b-0x1400d258c, yabasanshiro.exe 0x1400cf162-0x1400cf166, yabasanshiro.exe 0x14016d957-0x14016d98f, yabasanshiro.exe 0x14016f3ad-0x14016f4b4" + condition: "General\\EnableEmulatedBios false; an empty value runs the HLE BIOS (0x14016d997-0x14016d9f7), a file that does not open stops init" + notes: | YabaSanshiro is a Sega Saturn emulator by devmiyax, forked from Yabause. Requires OpenGL 3.3 or OpenGL ES 3.0. diff --git a/emulators/ymir.yml b/emulators/ymir.yml index f1a2dad6a..07784acf8 100644 --- a/emulators/ymir.yml +++ b/emulators/ymir.yml @@ -42,6 +42,22 @@ data_root: source_ref: "apps/ymir-sdl3/src/app/profile.cpp:29-34, apps/ymir-sdl3/src/app/app.cpp:342-437, apps/ymir-sdl3/CMakeLists.txt:647-648" note: "Portable profile: the current directory, then the folder of the executable, when it holds Ymir.toml. The first launch asks between the two. -p names a folder, -u forces this one. Always this folder under Flatpak" +config_reads: + - file: "Ymir.toml" + platforms: [retrobat] + revision: "54fead6a0001d3e4b6741a8d095ee8342266c3dc" + revision_from: "version 0.3.3 of ymir-sdl3.exe in RetroBat's stable ymir.7z (2026-08-08), byte-identical to the v0.3.3 windows-x86_64-AVX2 release asset" + source_ref: "apps/ymir-sdl3/src/app/settings.hpp:40, apps/ymir-sdl3/src/app/app.cpp:340-359, 412, apps/ymir-sdl3/src/app/profile.cpp:36-62" + keys: + - key: + - "[System.IPL] Override" + - "[System.IPL] Path" + source_ref: "apps/ymir-sdl3/src/app/settings.cpp:1236-1240, 2805-2813, apps/ymir-sdl3/src/app/services/rom_service.cpp:109-114, apps/ymir-sdl3/src/util/rom_loader.cpp:9-22, apps/ymir-sdl3/src/app/app.cpp:451-462" + condition: "Override true and Path not empty: the path is opened without an existence check and must be 512 KiB" + - key: "[General.PathOverrides] IPLROMImages" + source_ref: "apps/ymir-sdl3/src/app/settings.cpp:1190-1197, apps/ymir-sdl3/src/app/profile.cpp:94-104, apps/ymir-sdl3/src/app/services/rom_service.cpp:67-73, 116-175, apps/ymir-sdl3/src/app/rom_manager.cpp:14-45" + condition: "Override false or Path empty: the folder is scanned recursively for 512 KiB files known by XXH128" + notes: | Ymir is a Sega Saturn emulator by StrikerX3. The libretro port by warmenhoven wraps the shared ymir-core library on the libretro branch of diff --git a/schemas/emulator.schema.json b/schemas/emulator.schema.json index 7bb563009..2e11e9d4e 100644 --- a/schemas/emulator.schema.json +++ b/schemas/emulator.schema.json @@ -1165,12 +1165,14 @@ "type": "object", "additionalProperties": false, "required": ["file", "source_ref", "keys"], - "dependentRequired": {"revision": ["platforms", "revision_from"]}, + "dependentRequired": {"revision": ["platforms", "revision_from"], "binary_sha256": ["platforms", "revision_from"]}, + "not": {"required": ["revision", "binary_sha256"]}, "properties": { "file": {"type": "string", "minLength": 1, "description": "The config as the code finds it: a name, a glob for any file a flag passes (*.uae), a path, the flag itself, \"command line\" or \"core options\""}, "platforms": {"type": "array", "items": {"type": "string"}, "minItems": 1, "uniqueItems": true, "description": "Registry slugs of the platforms shipping the build these refs are read at, each with a pin_build entry unless revision names the build"}, "revision": {"type": "string", "pattern": "^[0-9a-f]{40}$", "description": "The commit these refs are read at, for platforms installing a package no build file of theirs pins (RetroBat's stable archives, a Flathub app)"}, - "revision_from": {"type": "string", "minLength": 1, "description": "Where revision was read: the version string of the shipped binary, the Flathub manifest commit"}, + "binary_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$", "description": "The shipped binary these refs are read in, for a build whose source is not public (SSF, YabaSanshiro 1.20): its refs are virtual addresses of that file"}, + "revision_from": {"type": "string", "minLength": 1, "description": "Where revision or binary_sha256 was read: the version string of the shipped binary, the Flathub manifest commit, the archive the binary comes from"}, "source_ref": {"type": "string", "minLength": 1}, "keys": { "type": "array", diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index 94c663b81..99abe41d4 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -1652,7 +1652,10 @@ def _check_build_refs( results: list[dict] = [] for citation, read in deferred: for platform in read.get("platforms") or []: - revision = str(read.get("revision") or "") or build_revision(profile, str(platform)) + # A closed build is named by its binary: an address ref is + # skipped below, a source path cannot exist at a sha256. + revision = (str(read.get("revision") or read.get("binary_sha256") or "") + or build_revision(profile, str(platform))) row = {"platform": str(platform), "revision": revision or "", "ref": citation.ref, "field": citation.field} if revision is None: diff --git a/scripts/validate_schemas.py b/scripts/validate_schemas.py index dc84746b6..055495866 100644 --- a/scripts/validate_schemas.py +++ b/scripts/validate_schemas.py @@ -147,7 +147,7 @@ def _config_reads_without_build(path: Path, document: object) -> list[str]: for index, read in enumerate(document.get("config_reads") or []): if not isinstance(read, dict): continue - if read.get("revision"): + if read.get("revision") or read.get("binary_sha256"): continue for platform in read.get("platforms") or []: if str(platform) not in builds: diff --git a/tests/test_launcher_evidence.py b/tests/test_launcher_evidence.py index 5f01c2929..31d7d549f 100644 --- a/tests/test_launcher_evidence.py +++ b/tests/test_launcher_evidence.py @@ -261,6 +261,8 @@ class Invariants(unittest.TestCase): ), []) pinned = [{**reads[0], "revision": "c" * 40, "revision_from": "the exe"}] self.assertEqual(_config_reads_without_build(path, {"config_reads": pinned}), []) + closed = [{**reads[0], "binary_sha256": "d" * 64, "revision_from": "the archive"}] + self.assertEqual(_config_reads_without_build(path, {"config_reads": closed}), []) class BuildRefs(unittest.TestCase): @@ -299,6 +301,37 @@ class BuildRefs(unittest.TestCase): self.assertNotIn(("b" * 40, "ROCKNIX x.patch"), asked) + def test_a_closed_build_is_read_in_its_binary(self) -> None: + """An address ref is read in the binary binary_sha256 names, never + fetched; a source path cannot exist at a closed build.""" + read = { + "file": "SSF.ini", "platforms": ["retrobat"], "binary_sha256": "e" * 64, + "revision_from": "the archive", "source_ref": "SSF.exe 0x10-0x20", "keys": [], + } + + class View: + repo, field = "repo", "source" + + class Citation: + def __init__(self, ref: str) -> None: + self.ref, self.field = ref, "c" + + asked: list[str] = [] + + def fetch(_repo: str, _revision: str, path: str) -> list[str] | None: + asked.append(path) + return None + + rows = _check_build_refs( + {"config_reads": [read]}, + [(Citation("SSF.exe 0x10-0x20"), read), (Citation("src/a.c:3"), read)], + [View()], fetch, + ) + self.assertEqual([r["status"] for r in rows], ["BUILD_OK", "BUILD_GONE"]) + self.assertEqual({r["revision"] for r in rows}, {"e" * 64}) + self.assertEqual(asked, ["src/a.c"]) + + class RepositoryRecords(unittest.TestCase): """The placement chains the audit of 2026-11-10 found moving files."""