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()