From f47c8b8e5a5a746a98ec6ea9464aa092ced39a83 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 18:40:33 +0100 Subject: [PATCH] fix: profile voxatron apart from pico-8 lexaloffle documented two programs, so every platform naming PICO-8 received Voxatron's files: ROCKNIX, RetroDECK and RetroBat run PICO-8 only. Voxatron is its own profile, named vox_official. A platform also runs the emulators its standalone_cores list names: Batocera and KNULLI list lexaloffle there and nowhere else. --- emulators/lexaloffle.yml | 99 +++-------------------------- emulators/voxatron.yml | 104 +++++++++++++++++++++++++++++++ scripts/common.py | 7 ++- tests/test_launcher_aliases.py | 3 +- tests/test_launcher_placement.py | 28 +++++++++ 5 files changed, 149 insertions(+), 92 deletions(-) create mode 100644 emulators/voxatron.yml diff --git a/emulators/lexaloffle.yml b/emulators/lexaloffle.yml index 341a1e4a9..1b28f72d4 100644 --- a/emulators/lexaloffle.yml +++ b/emulators/lexaloffle.yml @@ -12,30 +12,30 @@ version_basis: version_source: url: "https://www.lexaloffle.com/dl/docs/pico-8_changelog.txt" pattern: '^\s*v(0\.\d+\.\d+[a-z]?)\s*$' -display_name: "Lexaloffle - PICO-8 / Voxatron" +display_name: "Lexaloffle - PICO-8" cores: - lexaloffle - "pico-8" - - vox_official - "pico8" - "Pico-8" - "pico_standalone" - "float-scaled" systems: - pico8 - - voxatron standalone_directories: - "executable directory" notes: | - Commercial fantasy consoles by Lexaloffle Games LLP, sold as ZIP archives - per platform. Nothing in either program checks a hash of its own files: - the hashes below are those of the files in the official 0.2.7 archives. + Commercial fantasy console by Lexaloffle Games LLP, sold as ZIP archives + per platform. Nothing in the program checks a hash of its own files: the + hashes below are those of the files in the official 0.2.7 archives. + Voxatron, the same publisher's other program, is profiled apart + (voxatron.yml). - Each program finds its own folder through /proc/self/exe and reads its + The program finds its own folder through /proc/self/exe and reads its .dat beside itself. The paths below are the layout of the archives, whose - top folders are pico-8/ and voxatron/: a launcher that runs one of these - files from a folder places the program folder there. + top folder is pico-8/: a launcher that runs one of these files from a + folder places the program folder there. PICO-8 0.2.7 ships five archives. pico-8_0.2.7_amd64.zip holds pico8 (x86_64, SDL2 linked in) and pico8_dyn (x86_64, system libSDL2). @@ -83,11 +83,6 @@ notes: | and three other home folders on linuxarm, pico8 on linux and unix, and pico8.exe on Windows. - Voxatron: Batocera's generator runs bios/voxatron/vox with - LD_LIBRARY_PATH set to bios/voxatron. x86_64 only. vox_dyn is the - variant linked against the system SDL2. libHoloPlayCore.so is the Looking - Glass SDK the binary links against. - files: # ------------------------------------------------------- # PICO-8 0.2.7 @@ -234,79 +229,3 @@ files: description: "SPLORE favourites list" source_ref: "pico-8 0.2.7 amd64 pico8, string favourites.txt at 0x242346" note: "Read from the -home folder. The bbs/.pip.png images beside it are downloaded from the BBS." - - # ------------------------------------------------------- - # Voxatron - # ------------------------------------------------------- - - name: vox - path: voxatron/vox - system: voxatron - required: true - description: "Voxatron main executable" - source_ref: "batocera lexaloffleGenerator.py:20, 42, 48-52" - note: "The Voxatron program vox, an x86_64 ELF with SDL2 linked in. Batocera's generator runs bios/voxatron/vox and raises when it is missing or not executable." - - - name: vox_dyn - path: voxatron/vox_dyn - system: voxatron - required: false - description: "Voxatron dynamically linked executable" - source_ref: "voxatron vox_dyn, NEEDED libSDL2-2.0.so.0, batocera lexaloffleGenerator.py:20, 42" - note: "The same program linked against the system SDL2 (its only extra NEEDED library). Batocera's generator never runs it." - - - name: vox.dat - path: voxatron/vox.dat - system: voxatron - required: true - description: "Voxatron data file" - source_ref: "voxatron vox, string vox.dat at 0x2a58f, voxatron vox_dyn, string vox.dat at 0x1f1af" - note: "Opened from the folder of the binary that runs. Absent, vox stops with \"FATAL ERROR: could not load vox.dat\"." - - - name: libHoloPlayCore.so - path: voxatron/libHoloPlayCore.so - system: voxatron - required: false - description: "Looking Glass holographic display support library" - source_ref: "voxatron vox, voxatron vox_dyn, batocera lexaloffleGenerator.py:41, 87-90" - note: "Neither binary links it or names it, and neither opens it in a run (strace, with and without the file). Batocera's generator sets LD_LIBRARY_PATH to bios/voxatron, where it keeps the library." - - - name: config.txt - unsourceable: "written by the emulator on exit, no canonical file" - system: voxatron - required: false - description: "Voxatron settings" - source_ref: "voxatron vox, string config.txt at 0x27bea0" - note: "Read beside the binary, then from ~/.lexaloffle/Voxatron, and written there when absent." - - - name: sdl_controllers.txt - unsourceable: "user supplied, no canonical file" - system: voxatron - required: false - description: "Custom SDL controller mappings" - source_ref: "voxatron vox, string sdl_controllers.txt at 0x2847f7" - note: "Read beside the binary, then from ~/.lexaloffle/Voxatron, where Voxatron writes it when absent." - - - name: favourites.txt - unsourceable: "written by the emulator, no canonical file" - system: voxatron - required: false - description: "Cart browser favourites list" - source_ref: "voxatron vox, string favourites.txt at 0x27a39e" - note: "Read from ~/.lexaloffle/Voxatron." - - - name: internal_carts.txt - unsourceable: "written by the emulator, no canonical file" - system: voxatron - required: false - description: "Voxatron internal cart list" - source_ref: "voxatron vox, string internal_carts.txt at 0x27a446" - note: "Read from ~/.lexaloffle/Voxatron and written there at startup." - - - name: player_1.vpp - path: player/player_1.vpp - unsourceable: "written by the emulator, no canonical file" - system: voxatron - required: false - description: "Voxatron player profile" - source_ref: "voxatron vox, string player/player_%d.vpp at 0x273f90" - note: "Read from ~/.lexaloffle/Voxatron/player, and written there when absent." diff --git a/emulators/voxatron.yml b/emulators/voxatron.yml new file mode 100644 index 000000000..bc3350742 --- /dev/null +++ b/emulators/voxatron.yml @@ -0,0 +1,104 @@ +emulator: Voxatron +type: standalone +core_classification: other +upstream: "https://www.lexaloffle.com/voxatron.php" +profiled_date: "2026-11-09" +core_version: "0.3.5b" +version_basis: + kind: unverified + why: no_artifact +display_name: "Lexaloffle - Voxatron" +cores: + - vox_official +systems: + - voxatron +standalone_directories: + - "executable directory" + +notes: | + Commercial voxel fantasy console by Lexaloffle Games LLP, sold as ZIP + archives. The held binaries report voxatron 0.3.5b. Nothing in the + program checks a hash of its own files. + + The program finds its own folder through /proc/self/exe and reads vox.dat + beside itself. The paths below put the program in voxatron/: a launcher + that runs vox from a folder places that folder there. + + Batocera's generator runs bios/voxatron/vox with LD_LIBRARY_PATH set to + bios/voxatron. x86_64 only. vox_dyn is the variant linked against the + system SDL2. libHoloPlayCore.so is the Looking Glass SDK the binary links + against. + +files: + - name: vox + path: voxatron/vox + system: voxatron + required: true + description: "Voxatron main executable" + source_ref: "batocera lexaloffleGenerator.py:20, 42, 48-52" + note: "The Voxatron program vox, an x86_64 ELF with SDL2 linked in. Batocera's generator runs bios/voxatron/vox and raises when it is missing or not executable." + + - name: vox_dyn + path: voxatron/vox_dyn + system: voxatron + required: false + description: "Voxatron dynamically linked executable" + source_ref: "voxatron vox_dyn, NEEDED libSDL2-2.0.so.0, batocera lexaloffleGenerator.py:20, 42" + note: "The same program linked against the system SDL2 (its only extra NEEDED library). Batocera's generator never runs it." + + - name: vox.dat + path: voxatron/vox.dat + system: voxatron + required: true + description: "Voxatron data file" + source_ref: "voxatron vox, string vox.dat at 0x2a58f, voxatron vox_dyn, string vox.dat at 0x1f1af" + note: "Opened from the folder of the binary that runs. Absent, vox stops with \"FATAL ERROR: could not load vox.dat\"." + + - name: libHoloPlayCore.so + path: voxatron/libHoloPlayCore.so + system: voxatron + required: false + description: "Looking Glass holographic display support library" + source_ref: "voxatron vox, voxatron vox_dyn, batocera lexaloffleGenerator.py:41, 87-90" + note: "Neither binary links it or names it, and neither opens it in a run (strace, with and without the file). Batocera's generator sets LD_LIBRARY_PATH to bios/voxatron, where it keeps the library." + + - name: config.txt + unsourceable: "written by the emulator on exit, no canonical file" + system: voxatron + required: false + description: "Voxatron settings" + source_ref: "voxatron vox, string config.txt at 0x27bea0" + note: "Read beside the binary, then from ~/.lexaloffle/Voxatron, and written there when absent." + + - name: sdl_controllers.txt + unsourceable: "user supplied, no canonical file" + system: voxatron + required: false + description: "Custom SDL controller mappings" + source_ref: "voxatron vox, string sdl_controllers.txt at 0x2847f7" + note: "Read beside the binary, then from ~/.lexaloffle/Voxatron, where Voxatron writes it when absent." + + - name: favourites.txt + unsourceable: "written by the emulator, no canonical file" + system: voxatron + required: false + description: "Cart browser favourites list" + source_ref: "voxatron vox, string favourites.txt at 0x27a39e" + note: "Read from ~/.lexaloffle/Voxatron." + + - name: internal_carts.txt + unsourceable: "written by the emulator, no canonical file" + system: voxatron + required: false + description: "Voxatron internal cart list" + source_ref: "voxatron vox, string internal_carts.txt at 0x27a446" + note: "Read from ~/.lexaloffle/Voxatron and written there at startup." + + - name: player_1.vpp + path: player/player_1.vpp + unsourceable: "written by the emulator, no canonical file" + system: voxatron + required: false + description: "Voxatron player profile" + source_ref: "voxatron vox, string player/player_%d.vpp at 0x273f90" + note: "Read from ~/.lexaloffle/Voxatron/player, and written there when absent." diff --git a/scripts/common.py b/scripts/common.py index 665799b2c..94f5fbbdc 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -2997,7 +2997,12 @@ def resolve_platform_cores( if cores_config == "all_libretro": result = every_libretro() elif isinstance(cores_config, list): - core_set = {str(c) for c in cores_config} + # An emulator the platform launches standalone runs there even when + # its own list names only the cores: Batocera's es_systems.yml files + # lexaloffle and gsplus as standalone emulators and nowhere else. + core_set = {str(c) for c in cores_config} | { + str(c) for c in config.get("standalone_cores") or [] + } index = upstream_profile_index(profiles) platform = str(config.get("_key") or "") diff --git a/tests/test_launcher_aliases.py b/tests/test_launcher_aliases.py index b5a3ee7e9..76d1456ff 100644 --- a/tests/test_launcher_aliases.py +++ b/tests/test_launcher_aliases.py @@ -57,7 +57,8 @@ NAMED_FOR_A_LAUNCHER = { # Profiles whose entries still rest on a launcher, with the fact that blocks # reading the emulator itself. LAUNCHER_SOURCED = { - "lexaloffle": "PICO-8 and Voxatron are sold binaries, and no copy of either is held to read", + "lexaloffle": "PICO-8 is a sold binary, and no copy is held to read", + "voxatron": "Voxatron is a sold binary, and no copy is held to read", } _SENTENCE = re.compile(r"(?<=[.!?])\s+(?=[A-Z0-9(\"'])") diff --git a/tests/test_launcher_placement.py b/tests/test_launcher_placement.py index 94466b5ec..4d1ff831e 100644 --- a/tests/test_launcher_placement.py +++ b/tests/test_launcher_placement.py @@ -364,5 +364,33 @@ class AProgramRunFromItsFolder(unittest.TestCase): install._check_roms_directory({"files": files}) +class EachProgramItsOwnProfile(unittest.TestCase): + """PICO-8 and Voxatron are two programs: a platform running one gets its files only.""" + + def test_only_the_platforms_that_run_voxatron_get_it(self) -> None: + from common import list_registered_platforms, resolve_platform_cores + + profiles = load_emulator_profiles(str(REPO_ROOT / "emulators")) + runs = { + name: {k for k in ("lexaloffle", "voxatron") + if k in resolve_platform_cores( + load_platform_config(name, str(REPO_ROOT / "platforms")), profiles)} + for name in list_registered_platforms(str(REPO_ROOT / "platforms")) + } + self.assertEqual( + {name for name, found in runs.items() if "voxatron" in found}, {"batocera", "knulli"} + ) + self.assertTrue(all("lexaloffle" in runs[name] for name in ( + "batocera", "knulli", "rocknix", "retrodeck", "retrobat", "recalbox"))) + + def test_a_standalone_emulator_is_run(self) -> None: + from common import resolve_platform_cores + + profiles = {"emu": {"emulator": "Emu", "type": "standalone", "cores": ["emu"], + "systems": ["sys"], "files": []}} + config = {"cores": ["other"], "standalone_cores": ["emu"], "systems": {}} + self.assertEqual(resolve_platform_cores(config, profiles), {"emu"}) + + if __name__ == "__main__": unittest.main()