SRB2 Legacy as a Chimera core
  • C 94%
  • Shell 2.5%
  • Python 1.2%
  • Makefile 1.2%
  • C++ 0.8%
  • Other 0.3%
Find a file
PAS d2d7db76b7 text: settings, firmware, file slots and Game State properties are described in plain language
What changed
- Ported from the SRB2 core. The 18 settings, 7 firmware files, 2 file
  slots and 29 Game State properties have new descriptions. The facts are
  the same. Console variables are named as "SRB2's console variable
  cam_speed".
- AGENTS.md has a "write in plain language" rule, and commit messages now
  say what changed, why, and how it was tested.
- The property table's buffer grew from 8 to 16 KiB, because the longer
  descriptions came close to 8 KiB.
- docs/PLAN.md records the decision.

Why
- Chimera decided on plain language everywhere (chimera#239), and its owner
  asked for every core's texts to be rewritten (chimera#241).

Tested
- A script compared the declarations and the property table with the
  previous commit, with every description blanked out: they are identical
  (apart from the buffer size).
- The test script (run-gate.sh) passes, 50 of 50, including the tests through
  Chimera 804e6b4e's engine and on the GPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 22:54:08 -04:00
.github/workflows settings: Score Display (scorepos), Modern by default; the TAS script is tasinfo-srb2-legacy.lua 2026-10-15 15:48:36 -04:00
docs text: settings, firmware, file slots and Game State properties are described in plain language 2026-10-16 22:54:08 -04:00
extern fork: SRB2 Legacy 2.1.30, an unofficial third-party core forked from the SRB2 core 2026-10-15 15:04:46 -04:00
lua settings: Score Display (scorepos), Modern by default; the TAS script is tasinfo-srb2-legacy.lua 2026-10-15 15:48:36 -04:00
patches fork: SRB2 Legacy 2.1.30, an unofficial third-party core forked from the SRB2 core 2026-10-15 15:04:46 -04:00
waterbox text: settings, firmware, file slots and Game State properties are described in plain language 2026-10-16 22:54:08 -04:00
.gitignore Milestone 0: SRB2 headless native build 2026-10-04 14:22:41 -04:00
.gitmodules fork: SRB2 Legacy 2.1.30, an unofficial third-party core forked from the SRB2 core 2026-10-15 15:04:46 -04:00
AGENTS.md text: settings, firmware, file slots and Game State properties are described in plain language 2026-10-16 22:54:08 -04:00
LICENSE Milestone 0: SRB2 headless native build 2026-10-04 14:22:41 -04:00
README.md settings: Score Display (scorepos), Modern by default; the TAS script is tasinfo-srb2-legacy.lua 2026-10-15 15:48:36 -04:00

chimera-core-srb2-legacy

SRB2 Legacy (srb2-preservation's updated fork of Sonic Robo Blast 2 2.1.25, Sonic Team Junior's 3D Sonic fangame, from Doom Legacy) as a Chimera game core ("kind": "game", see Chimera's docs/game-cores.md). It is an unofficial third-party core: its author, P-AS, writes and publishes it from this repository. It is a fork of the SRB2 (2.2) core, chimera-core-srb2, and keeps its shape: the platform layer, the mixer, the controller, the gate. It needs no change to Chimera. See docs/PLAN.md for the fork, the milestones and the decisions.

The engine is upstream SRB2 Legacy (extern/SRB2, the SRB2_release_2.1.30 tag), compiled from source with a platform layer of the core's own (waterbox/platform/) in place of SDL: no window, no audio device (the core mixes the sound itself, in the machine), no network, no threads. The game's data - srb2.srb, zones.dta, player.dta, rings.dta, patch.dta, music.dta and legacy.pk3 - is firmware: the core carries none of it.

Using it in Chimera

Chimera ships no cores and downloads nothing. Download the core's package from this repository's Releases page (a rolling dev build, dated nightly-YYYY-MM-DD builds), or build it, and put it in the Cores folder beside Chimera.exe (or the folder chosen in File > Core Manager > Change folder...).

Each release has two packages, one per CPU: srb2legacy-<version>-x64.chimeraCore and srb2legacy-<version>-arm64.chimeraCore. You most likely want the x64 one. Take the arm64 one only if you know you are using an arm64 device. Put only one of them in the Cores folder: two packages of one core list it twice. The same file works on Linux and on Windows. None of the game's data is in the package: a project brings SRB2 Legacy 2.1.30's seven data files as firmware (as its SRB2-Legacy-v2130-Full.zip and srb2-preservation's assets repository have them; legacy.pk3 included, which upstream calls optional).

Building

git submodule update --init
export MINIBOX_DIR=<a Chimera checkout>/extern/chimera-common-minibox
make -C waterbox -f native.mk -j$(nproc)    # the native reference and the harnesses: build/native/
make -C waterbox -f guest.mk -j$(nproc)     # the core: build/guest/core.wbx

miniBox must be built first, with its C++ guest toolchain (GME is C++), in its checkout:

meson setup build/meson-cpp -Dguest_cpp=true && ninja -C build/meson-cpp
ninja -C build/meson-cpp libstdcxx-installed.stamp source/guest/emulibc.c.o source/guest/cxxglue.c.o

The patches go onto extern/SRB2 and extern/openmpt on the first build (waterbox/apply-patches.sh [openmpt], each series all or nothing). extern/openmpt is shallow: git submodule update --init fetches only its pinned commit.

Data for the gate

waterbox/fetch-data.sh [<folder>] takes the seven data files from srb2-preservation's assets repository on Codeberg (git lfs, at a pinned commit), checked against the declared firmware (default build/srb2-legacy-data); run-gate.sh -d <folder> runs on them. CI (.github/workflows/chimera.yml) does this, then the gate, the package and Chimera's contract tests, on x64 and on arm64, and publishes dev and nightly releases with both packages.

The package

waterbox/build-package.sh [-r <Chimera bundle or checkout>]   # build/package/srb2legacy.chimeraCore, or into its Cores

-r <chimera> writes the package into a Chimera source checkout's build/Cores (a bundle's Cores). The whole build, as CI does it, is in docs/BUILDING.md; AGENTS.md is the guide for an AI coding agent.

Running the native reference

mkdir -p build/work && ln -s /usr/share/games/srb2-legacy/* build/work/
echo '{"warp": "1"}' > build/work/settings          # optional: start in Greenflower Zone Act 1
build/native/run-native build/work -n 400 --ppm frame.ppm
build/native/run-wbx build/guest/core.wbx build/work -n 400   # the same, in the sandbox

Both run -n steps (a step is a tic, on the machine's own clock), print the picture's hash, the tic and the clock every -p steps and a hash of the whole run at the end, and --ppm writes the last picture. Their lines diff directly.

The gate

./waterbox/run-gate.sh [-d <SRB2 Legacy data folder>] [-m <miniBox>]: the legs, each saying what it compared, each with teeth. equivalence: native == sandbox, step for step, on the intro and Greenflower Zone Act 1. steps: a step is a tic, a wipe's frames are lag steps, play is a tic a step. savestates: a save and load before every step, and a new host mid-wipe, change nothing. declaration: the package's controller is the core's. settings: each setting reaches the engine. opengl: with no GPU bridge the OpenGL renderer warns in the log and the run is the software renderer's. engine (with -c) also runs opengl-hw through the GPU bridge where the machine has a context: the character select drawn as in software, and the picture right again after a rewind and a reopen (within a few seam pixels: SRB2 Legacy's renderer draws coplanar polygons in an order set by the driver's texture names). properties: the Game State domain, read by its property table, holds what Greenflower's movie did, the timers tests/timers.lua sets, a spindash's charge (tests/spindash.txt), and a boss tests/boss.lua spawns and hits. input: movies through the menus and in Greenflower, the same every way. files: what the game writes is kept in the machine and exported identically. time: a host stall mid-run changes nothing. audio: the music and sounds are heard (a GME song, a tracker module, and the game's MIDI songs with Music Preference MIDI among them), and every leg's run line carries the sound's hash too.

The harnesses' --wav FILE writes a run's sound.

Settings

Declared in waterbox/waterbox.config, so Chimera shows them as the core's options; each is SRB2 Legacy's own option, given to the engine at start: Analog Control (Off: 2.1's useranalog), Camera Speed (1.0), Camera Distance (192, as in SRB2 2.2; Legacy's own is 160), Camera Height (40, as in 2.2; Legacy's own is 25), Field of View (90; 60 to 179, the picture's alone - leave it at 90 for normal levels, 120 is recommended for NiGHTS levels), Score/Time/Rings (Mania), Score Display (Modern: the score's number lined up with the time; Normal is 2.1's place and Legacy's default), Flip Camera with Gravity (Yes), Unlock Record Attack and NiGHTS Mode, Unlock All Maps in Record Attack and NiGHTS Mode, Unlock All Secrets (each Off), Music Preference (Digital: the recorded songs; MIDI: the game's MIDI songs on six emulated Sega Genesis/Mega Drive YM2612 sound chips, MAME's - the sound's alone), Renderer (software; opengl-hw: SRB2's OpenGL renderer on this machine's GPU through Chimera's GPU bridge - fast, its picture the driver's; where there is no bridge it draws in software, which plays differently, and the log warns; the renderer is part of the game, so a movie is its renderer's), OpenGL Shaders (On; the picture's alone), Resolution (1280x800: SRB2's video modes, and 2560x1440 and 3840x2160, shown with square pixels; the game plays the same at every one), Start Map (empty: the intro and the title), Start Map Character (a skin name, e.g. knuckles; empty: Sonic).

The SRB2 core's Play Style, Automatic Braking, Tutorial Prompt and Unlock All Characters are not here: 2.1 has no play styles but Analog Control, no automatic braking, no tutorial, and no locked characters; and SRB2 Legacy always offers Marathon Run.

The controller

SRB2 Legacy's keyboard as buttons (Forward, Jump, Spin - 2.1's "use" - ... the menus' Enter and Escape, which answer a prompt too), pressed as keys, so menus and play work as on a keyboard (the ring-slinger controls too - Fire, Fire Normal, Toss Flag, Weapon Next/Prev - but not the number row's weapon slots); plus axes for exact values: Forward Move, Side Move, Turn, Aim. The harnesses take a movie as text, --input FILE with FROM-TO: Button; Axis=value lines (waterbox/tests/).

Game State and the TAS info script

The core exposes one memory domain, Game State, and a property table naming its fields, so RAM Watch (Watches > Add Game Properties), RAM Search and Lua's game.get see them by name. The core copies them from the game after every step; they are read-only. Game.Tic, Game.Level Time, Game.State, Game.Map; Player.In Level, Player.X/Y/Z, Player.Momentum X/Y/Z, Player.Conveyor Momentum X/Y (player->cmomx/cmomy: what a conveyor or a moving platform adds), Player.Platform Momentum Z (mo->pmomz: the moving floor's) and Player.Speed (16.16 fixed point: 65536 is one unit; speed is the game's own player->speed), Player.Dash Speed, Player.Min Dash, Player.Max Dash (the spindash's charge and its bounds, 16.16), Player.Flags (pflags) and Player.Charging Spindash (its PF_STARTDASH bit), Player.Normal Speed (the player's top running speed); Boss.Active, Boss.Health, Boss.Max Health, Boss.Flashing (the first MF_BOSS object, as the SRB2 TAS build finds the boss: its health, its type's starting health, and MF2_FRET); Player.Angle (2^32 a turn); Timers.Speed Shoes, Timers.Invincibility, Timers.Space, Timers.Air (tics left). Out of a level the player's fields are 0.

lua/tasinfo-srb2-legacy.lua (also an asset of every dev and nightly release, beside the package) draws them in the bottom right corner of the game, clear of Chimera's own HUD: open it in Tools > Lua Console with an SRB2 Legacy project loaded. It shows speed, the angle in hex and in degrees (each to 4 decimal places, on lines of their own), the position and the momentum, the conveyor and platform momentum, the spindash's revs, the four timers in tics and seconds, and while a boss is in the level its health (left / at the start) and whether it is flashing. SRB2 has no discrete revs: a spindash charges 1.0 of speed a tic while Spin is held, from Min Dash to Max Dash, so the script counts a rev a tic, as revs so far / revs to full charge. The conveyor and platform momentum, the revs and the timers are shown only while they are not 0.

The harnesses' --game-state FILE writes every step's block, and the table beside it in FILE.json.

Licence

GPL-2.0, as SRB2 and SRB2 Legacy are (LICENSE). zlib 1.2.6 and libpng 1.2.46 are compiled from the copies in SRB2 Legacy's libs/, under their own licences; libco (miniBox's extern/libco) is public domain; libogg and libvorbis (extern/ogg, extern/vorbis) are BSD-3-Clause; Game_Music_Emu (extern/gme) is LGPL-2.1-or-later (its Nuked YM2612 too); libopenmpt (extern/openmpt) is BSD-3-Clause. The MIDI player (waterbox/platform/midi_fm.cpp) is the core's own, GPL-2.0, because libOPNMIDI's player is GPL-3.0-or-later and cannot be part of a GPL-2.0 program; it is built on the parts of libOPNMIDI (extern/opnmidi) whose terms allow it: its MIDI sequencer, WOPN bank loader, XG bank and volume and frequency models (MIT), its chip interface (LGPL-2.1-or-later) and MAME's YM2612 (GPL-2.0-or-later). Sonic the Hedgehog and related characters are trademarks of SEGA; this repository carries none of the game's data.