A static recompilation of the Sega 32X game Knuckles' Chaotix into native C++. This is not an emulator: the 68000 and both SH-2 CPUs' game code is translated ahead of time into C++ that is compiled for the host. A small hardware compatibility layer (32X and Mega Drive registers, both VDPs, interrupts, timers, sound chips) plus an SDL3 frontend make it an ordinary native program on each platform.
How it was made, step by step, with the numbers behind each step: the Recompiled Handbook (English and Traditional Chinese; its source is site/index.html).
Much of it comes from the community's code, reports and ideas: see Credits for everyone who helped.
No game data is included. You supply your own legally obtained ROM of
Knuckles' Chaotix (Japan, USA) — SHA-1
0c2fff7bc79ed26507c08ac47464c3af19f7ced7. No 32X BIOS is needed. Read
LEGAL.md before building or sharing anything.
Boots to the title screen; menus, character select and the stage-select hall work; the levels are playable, with sound (Z80 driver, YM2612, PSG and PWM).
True widescreen in levels: the game's own level renderer is extended to draw real tiles, objects and sprites in the extra area, rather than stretching or cropping a 4:3 image. Anything from 1.24:1 (320×240) to 2:1 (480×224) is filled — 426×224 at 16:9 — and the centre 320 pixels stay bit-identical to the unmodified game, which is checked by a test. The title screen's horizon fills the sides too (issue #77); other scenes outside levels stay 4:3.
| Platform | State |
|---|---|
| Windows | built and played (MSYS2 GCC, and MSVC) |
| Linux | build configuration present, not yet run by the author |
| macOS | builds and runs on Apple Silicon (an M1; every test passes, lockstep bit-identical) |
| Android | builds and runs (verified on an API 36 emulator); OPTIONS > VIDEO > SHOW FPS shows how fast it runs (performance notes) |
| iOS / iPadOS | builds and runs in the iOS Simulator; an .ipa for sideloading, not yet tried on a device |
docs/MILESTONES.md tracks the detail; ARCHITECTURE.md explains how it is put together. (Those two documents are written in Traditional Chinese; the rest of the documentation is in English.)
Requirements: CMake ≥ 3.20, a C++17 compiler, and SDL3 plus SDL_ttf for the playable frontend. GCC, Clang and MSVC all build the tree warning-free.
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCHAOTIX_ROM=/path/to/chaotix.32x
cmake --build buildThe build runs chaotix_recomp over your ROM (guided by the execution traces
in coverage/) and compiles the resulting C++ from generated/. Without
CHAOTIX_ROM you get an interpreter-only runtime and the unit tests — which
is what CI builds, since CI has no ROM.
Windows (MSYS2 MinGW64): pacman -S mingw-w64-x86_64-{gcc,cmake,ninja,sdl3,sdl3-ttf},
and pass a Windows-style ROM path (cygpath -m).
Where SDL is not packaged (Android, iOS, CI), point the build at source
checkouts instead: -DCHAOTIX_SDL3_SOURCE_DIR=... and
-DCHAOTIX_SDL3_TTF_SOURCE_DIR=... (the latter needs its vendored
submodules, so clone it with --recurse-submodules).
Presets: cmake --preset windows-msvc | windows-ninja | linux | macos-universal | macos-xcode | ios | android-arm64, with the CHAOTIX_ROM
environment variable set.
Cross builds (Android, iOS) do not run the recompiler: build once on a host
with CHAOTIX_ROM so generated/ exists, then configure the target build,
which picks it up. See platforms/android/README.md,
and docs/BUILDING_APPLE.md for the macOS app and the
iOS .ipa (scripts/package_apple.sh macos|ios VERSION on a Mac).
build/ChaotixRecompiledOn the first launch a setup page asks for your ROM. It lists the ROMs it can
find (Downloads, Documents, an __ROM__ folder next to the executable, the
app's own folder on Android, the app's Documents on iOS, where the Files app
can put it); you can also drag a file onto the window, press
O to browse, or tap the on-screen buttons. The file is verified by SHA-1 and
copied into the app's own folder, so later launches go straight into the game.
A verified ROM given on the command line (including dragging it onto the
Windows executable) replaces the installed copy, so later launches do not need
the original file. Unrecognised images are rejected without replacing an existing
installation; the error shows the selected path and both SHA-1 values. If an
older version installed an unrecognised image, startup returns to setup so you
can choose a verified ROM with Browse.
Useful flags: --user-dir DIR keeps settings, saves and the installed ROM in
DIR (a portable install), --install FILE installs a ROM without showing
the setup page (for packaging scripts), and --menu main|options|awards
opens a menu page at startup (so it can be captured without a keyboard;
timeattack is one of them).
Settings live in the user data directory (Config/chaotix.ini), saves in
SaveData/, the installed ROM in Game/. That directory is
%APPDATA%\ChaotixRecompiled\ChaotixRecompiled on Windows,
~/.local/share/ChaotixRecompiled/ChaotixRecompiled on Linux,
~/Library/Application Support/ChaotixRecompiled/ChaotixRecompiled on macOS
and the app's Documents on iOS (the Files app's On My iPhone > Chaotix).
| Key | Action |
|---|---|
| Arrows | D-pad |
| Z / X / C | A / B / C |
| A / S / D | X / Y / Z |
| Enter | Start |
| Esc | Menu (on a pad, the button set under CONTROLS - left stick click to start with) |
| F1 / F5 | Debug overlay / overlay page |
| F2 | Aspect ratio (Auto, 4:3, 16:9, 16:10, 21:9) |
| F3 / F4 | Filter / scaling mode |
| F6 | True widescreen on/off (applies from the next level load; resizing the window applies at once) |
| F7 | Achievements (the menu's Awards page) |
| F11, Alt+Enter | Borderless fullscreen |
| Tab (hold) | Fast-forward (on a pad, the right trigger; see Fast-forward below) |
| F8 / F9 | Save state / load state, in the current slot (see Save states and rewind below) |
| F10 | Next save state slot (1-4) |
| Backspace (hold) | Rewind (on a pad, the left trigger) |
| F12 | Screenshot |
| Home / End | Start / reset a speedrun timer run (see Speedrun tools below) |
The menu is the front end: it is up as soon as the program starts, drawn on the game's own title screen, which fills the window as the game does in play. The entries — START GAME, TIME ATTACK, LEVEL SELECT, OPTIONS, CONTROLS, MUSIC PLAYER, AWARDS, NETPLAY, QUIT — step through the box where the title shows PUSH START, the chosen one on a purple plaque between two spinning rings, the others under it (on a portrait screen, under the picture) (issue #3; docs/MENU_ART.md). The title is the game drawing its own screen, from your ROM, so nothing is extracted or shipped. While the menu is open the title stays, music and all, rather than giving way to the attract demo; before PUSH START is up (the intro) only a hint shows, and the first press skips to the title.
OPTIONS is split into VIDEO (widescreen, aspect ratio, scaling, screen, menu size), SOUND (the volumes), INPUT (touch controls, fast-forward, 6-button pad), EXTRAS (cheats, lobby shortcuts, the menu's look, awards, and MODS, the mod packs) and GAMEPLAY (the ring tether).
The menu is written in the game's own font -- the white digits and shaded
yellow capitals of its TRAINING and DATA LOAD screens -- and shows the game's
zones behind itself, a different one each time it opens, under a translucent
coat of that zone's colour. Both are made from your ROM: the font is read
from it at start-up, and each zone's picture is rendered once by the game
itself, on a second machine in the background while a menu is open -- it is
paused whenever you are playing -- (a couple of seconds each on a PC, longer
on a phone), then kept in the user data folder under
Cache/Backdrops. Until a picture exists the menu is plain. OPTIONS > EXTRAS
MENU BACKGROUND picks CYCLE (every zone in turn, the default), one zone for good (WORLD ENTRANCE, ISOLATED ISLAND, BOTANIC BASE, MARINA MADNESS, SPEED SLIDER, AMAZING ARENA or TECHNO TOWER; only that picture is made), or PLAIN. MENU FONT (GAME / PIXEL) and MENU BACKGROUND on PLAIN,
MenuFontandMenuBackgroundin[Video], bring back the earlier look. How it works: docs/MENU_ART.md.
OPTIONS > VIDEO > MENU SIZE makes the menu's rows, letters and panels -- and
so the area a finger can tap -- larger: NORMAL, LARGE (half as large again)
or EXTRA LARGE (twice NORMAL). AUTO, the default, is LARGE on phones and
tablets, where a landscape screen is only a few centimetres tall and
NORMAL's rows come out a few millimetres high, and NORMAL elsewhere. The
letters stay on whole pixels: each size takes the game's font up by at least
a whole step. A page with more rows than fit shows a window of them that
follows the selection, with a marker where there are more; the pause menu's
row carries on onto a second line instead of running off the screen. It is
MenuSize (Auto, Normal, Large, ExtraLarge) in [Video].
START GAME hands the game control, and it proceeds into its own menus exactly as it would have on the console, so nothing the original offers is out of reach. Escape (or the pad's menu button) then brings up a pause menu, which adds BACK TO TITLE. The game has no way back to its title screen, so the title is copied when control is handed over and put back on the way out; anything chosen inside the game's menus is discarded, as it would be by turning the console off. The save is kept, as the cartridge's battery keeps it: progress the game has saved to a DATA slot is there when you come back. Progress made with NO SAVE (DATA LOAD's first choice, and the one selected at first) only ever lives in memory, so going back to the title loses it, as resetting the console would.
TIME ATTACK drops you straight into any level. Knuckles' Chaotix carries a complete stage-select screen that the shipped game never reaches (game mode 0x30), and the page offers exactly what that screen does, in its order:
| Field | What it chooses |
|---|---|
| PLACE | Botanic Base, Speed Slider, Amazing Arena, Techno Tower, Marina Madness, Training, Introduction |
| LEVEL | which of that place's levels — the attractions have 1-5, Training 0-4 |
| AT-TIME | Morning, Day, Sunset, Night |
| PLAYER | the character you play: Mighty, Knuckles, Charmy Bee, Vector, Bomb, Heavy or Espio |
| COMBI | the partner on the other end of the tether, from the same seven |
| PLAYERS | 1 PLAYER, or 2 PLAYERS to give the partner to the second controller |
The names, the levels each place has, and the two-player switch are all read out of the game's own tables rather than invented here, and START uses the game's own route into a level, so a run behaves exactly as the console would. START goes straight there, in about a second: none of the game's own screens are passed through, only its level entry (the ball wipe and the BOTANIC BASE ticket). A run is played with NO SAVE, as the stage-select screen sets it, so it never touches your DATA slots.
Every run is recorded, frame by frame, to SaveData/last_run.txt, and
chaotix_headless --replay-input plays one back exactly. It is there because
what the game does when a level is finished at the goal is not something a
script can reach, so the only way to study it is to record somebody reaching
it. A whole run is about 150 KB and holds nothing but pad readings.
BEST TIME shows the record for whichever stage is selected. When a run ends the game would send you to its lobby, so the front end takes the time and brings you back to this page instead, with what you managed. A run that used up the level's own ten minutes is not a time. The clock stops where the game stops it -- at the goal, not at the end of the twenty-odd seconds of tally afterwards -- so a record reads the same as the time you watched when you finished. Two players needs a device for player 2 under CONTROLS, which starts as NONE.
GHOST races a translucent copy of a run you made before: BEST (the stage's
fastest kept, whoever ran it), LAST (the stage's last finished run) or OFF.
Every finished run without cheats is kept, as a small file in Ghosts/ in
the user data folder, and on the next START of the stage the ghost's
characters run alongside you, half see-through and tinted, from the moment
the clock starts until the ghost reaches the goal, when its time is shown.
The game is deterministic, so the ghost is the recorded run's pads replayed
on a second machine from the moment the stage started; it never changes your
run. Copy the files to export or import ghosts. It starts as BEST on
computers, where it costs no frame rate, and OFF on phones, which may not
have the time for a second game. See docs/GHOSTS.md.
LEVEL SELECT starts any level the same way, as practice: no clock, no record,
no ghost, no speedrun split, and no awards, and save states, rewind and
fast-forward all work. Beside TIME ATTACK's places it offers WORLD ENTRANCE
(the lobby with all five attractions open), the six SPECIAL STAGEs and the
BONUS STAGE, with the same AT-TIME, PLAYER, COMBI and PLAYERS. It is NO SAVE
too, so finishing a level writes nothing to your DATA slots. When the level
is over -- the game sends you to its lobby, or a special stage ends -- you are
brought back to the LEVEL SELECT page; in WORLD ENTRANCE you play on until
you go BACK TO TITLE. Loading a save state from story mode (one with a DATA
slot) during it lets that game go on as it was. With 1 PLAYER, in the five
attractions, PARTNER also offers NONE: the level is played alone, as with
GAMEPLAY > PARTNER SOLO (below), for that level only. The choice is kept
apart from TIME ATTACK's, in [LevelSelect]. See
docs/LEVEL_SELECT.md.
CONTROLS gives each player a device of its own: AUTO (the keyboard and any gamepad, which is the default for player 1), the keyboard alone, one gamepad by name, or nothing. Each player has its own keyboard set and its own gamepad set, so two people can share one keyboard (player 2 starts on the keypad) or two controllers can be laid out differently. Choose a button and press Enter, then press the key or the gamepad button to bind it; Escape leaves it alone. Each row also says what that button does in this game: C jumps, B holds your partner still (the game answers with its own HOLD! bubble), A pulls the partner in for ten rings, Start pauses. X, Y, Z and Mode say NOT USED BY THIS GAME, because they are: pressing them changes neither the picture nor the game's state, on the title screen or in a level.
MENU BUTTON is Escape's counterpart on a controller: the emulated 6-button pad takes every face and shoulder button, so the choice is among the ones it leaves alone - left stick click (the default), right stick click, Guide/Home/PS, the touchpad click, or Share/Capture. The same button backs out of the menu again.
FAST FORWARD KEY (Tab to begin with; press Enter, then the key) and FAST FORWARD PAD (the right trigger to begin with; also the left trigger, either stick click, Guide/Home/PS, the touchpad click, Share/Capture or NONE -- never one the emulated pad uses, nor the MENU BUTTON) are on the CONTROLS page. OPTIONS > INPUT has the rest:
- TOUCH FAST FORWARD (OFF by default) adds a small fast-forward button to the
touch controls, in the top-left corner, which both touch layouts leave
empty. Its image is
ff.bmpinMods/Touch. - FAST FORWARD: HOLD (on while held, the default) or TOGGLE (each press turns it on or off; the touch button stays lit while it is on).
- FAST FORWARD SPEED: 2X and 4X run exactly two or four frames of the game for every frame at the normal pace, and keep the sound -- all of it, played that many times faster, so it is higher but unbroken. MAX (the default, and what Tab always did) runs as fast as the machine allows, up to eight frames for every picture shown, and is silent because it has no steady speed to play the sound at.
Fast-forward only runs more frames of the same game, so the game behaves
exactly as it would have. It does nothing during a TIME ATTACK run (the touch
button is not shown either), so every record is set at the game's own speed,
nor while the menu is open. In the settings file: FastForwardKey,
FastForwardButton, TouchFastForward, FastForwardMode (Hold | Toggle)
and FastForwardSpeed (2x | 4x | Max) in [Input].
The pause menu's STATES page saves and loads the game in one of four slots
(SLOT), and turns rewind on or off. F8 saves, F9 loads and F10 picks the next
slot; holding Backspace (or the left trigger) steps back through the last
minute of play, at twice the game's speed and without sound, and letting go
plays on from there. CONTROLS > STATES AND REWIND rebinds the keys, gives
saving and loading a pad button if wanted (none to begin with) and turns on
an on-screen rewind button (TOUCH REWIND, off to begin with; rew.bmp in
Mods/Touch). Rewind is on to begin with on computers and off on phones and
tablets.
A state is the whole machine, so loading one carries on exactly as the game
would have from that moment; the cartridge save (the DATA slots) is never put
back by a load or by rewinding. States are files in States/ in the user data
folder and survive restarting the program, but only load with the same ROM,
ROM patches and version of this program. None of it works during a TIME
ATTACK run, so a record is always one unbroken run. See
docs/SAVE_STATES.md.
OPTIONS > EXTRAS has RESET AWARDS, which asks twice before locking everything again.
On-screen touch controls appear once the screen is touched (TouchControls = Auto), always (On), or never (Off). Gamepads work through SDL
(Xbox, PlayStation and Nintendo layouts): West/South/East = A/B/C,
LB/North/RB = X/Y/Z, Back = Mode (the game itself only uses three of those --
see CONTROLS). Every other button belongs to the emulated
6-button pad, so the menu is on one of the spare buttons (see MENU BUTTON
above); East/B backs out of it too. On touch devices virtual controls appear
over the game, with a menu button (three bars) in the top-left corner that
opens the menu -- RESUME, OPTIONS, BACK TO TITLE and the rest -- as Escape
does (issue #72); a phone's back button or gesture does the same, and steps
back out of it. The unlock counter in the other top corner opens AWARDS.
A small local achievement system in the spirit of RetroAchievements: the
conditions in assets/achievements.ini are checked
against the game's memory every frame, unlocks pop up in game and are stored
in SaveData/achievements.ini. Nothing is downloaded or uploaded and no
account is involved. F7 shows the list; see
docs/ACHIEVEMENTS.md to write your own.
Twenty-eight of them, 665 points, and none for just playing on: rings carried and kept, clears (quick, clean, at night or in the morning, with 100 rings), each attraction finished, the Chaos Rings, the final battle and the ending, and sets built up over many sessions (a level cleared in every attraction, as every leader, with every partner), which the list shows the progress of. Each was checked by driving the game to it rather than by reading the conditions.
OPTIONS > EXTRAS > CHEATS has 99 RINGS and SPEED SHOES, and [Cheats] Codes in the
config takes Action Replay codes (FFE009:63). Game Genie codes are not
supported: they patch the ROM's instructions, which the recompiled code does
not read. Achievements and time attack records pause while any cheat is on.
See docs/CHEATS.md.
In story mode the game picks your partner (the Combi Catcher in WORLD ENTRANCE) and the time of day (it moves on every second level). OPTIONS > EXTRAS > LOBBY SHORTCUTS lets you pick them instead, also from the pause menu while you walk the lobby:
| Field | What it does |
|---|---|
| SHORTCUTS | OFF (the default) is the game as it was |
| PARTNER | CATCHER, or Mighty, Knuckles, Charmy Bee, Vector, Bomb, Heavy or Espio, or NONE |
| TIME OF DAY | GAME, or Morning, Day, Sunset, Night |
The choice is put into the game each time it starts the lobby or one of the five attractions, so the next one you enter has them. Both are things the game itself produces, and your progress saves into your DATA slot as usual. Choosing your own character as the partner keeps the catcher's. NONE plays the attractions alone, as GAMEPLAY > PARTNER SOLO does (below), whatever GAMEPLAY says; with any other PARTNER, GAMEPLAY's setting applies. TIME ATTACK has its own choices and ignores these. See docs/LOBBY_SHORTCUTS.md.
OPTIONS > EXTRAS > SPEEDRUN has an in-game timer, splits and an input display, all off to begin with:
| Field | What it does |
|---|---|
| TIMER | real time (RTA) and game time (IGT, the level clocks added up) of the run |
| SPLITS | each level finished, with its delta against your best (kept in Splits/) |
| COMPARE | whether splits and the best go by REAL TIME or GAME TIME |
| INPUT DISPLAY | the pads' buttons, lit while held (both players with two) |
| TIMER CORNER, INPUT CORNER, OPACITY | where and how they are drawn |
| START RUN, RESET RUN, and their keys | by hand (Home / End to begin with) |
A run starts by itself when START on DATA LOAD hands over to the game, splits
on the frame each level's clock stops at the goal, and resets when the game
goes back to its title screen. tools/livesplit/ChaotixRecompiled.asl is a
LiveSplit auto-splitter that follows the same timer and gives LiveSplit the
game time. See docs/SPEEDRUN.md for the definitions, the
memory block auto-splitters read and the game's RAM addresses.
The Combi Ring that joins the two characters can be changed in OPTIONS > GAMEPLAY (also from the pause menu), in the five attractions' levels:
| Field | What it does |
|---|---|
| TETHER | NORMAL (the game as it is), WEAK (half the pull) or STRONG (half as much again) |
| PARTNER | NORMAL; FOLLOW: the ring never holds you back -- it only pulls your partner after you, unless you hold B (HOLD), so the slingshot still works; SOLO: no partner. One player only |
SOLO sends your partner away the way the game does when you are hit without rings, and keeps it away: no ring, the camera on you alone, and a hit without rings ends the run, as for Sonic. The lifts that wait for both characters take you alone. If anything else needs two, hold CALL (A) for half a second: your partner comes back for the rest of that life.
NONE as the partner on LEVEL SELECT or in the lobby shortcuts is the same SOLO. TIME ATTACK always plays the original tether, and achievements are not earned while any of these is changed (NONE included). See docs/RING_TETHER.md for how the tether works.
NETPLAY on the front menu plays the game's two-player mode with a friend on
another machine, with rollback netcode: your buttons act at once (after an
input delay of 0-4 frames, 2 to begin with), theirs are predicted until they
arrive, and a wrong guess is put right by running the last few frames again.
One player chooses HOST A GAME (they are player 1), the other types the
host's address and chooses JOIN A GAME (player 2); the host's game, saves
included, is sent over and both play on from there, choosing PLAY STYLE 2P
in the game. The two games are compared every second, and a difference stops
the session with DESYNC (both states are written to Netplay/).
Both players need the same release, ROM and game-changing mods (patches, character stats, replacement music); the joiner takes the host's widescreen, pad, lobby and ring tether settings for the session. There is no NAT traversal: the host forwards UDP port 7845, or both use a VPN such as Tailscale or ZeroTier. Save states, rewind, fast-forward, TIME ATTACK and cheats are off during a session. See docs/NETPLAY.md.
OPTIONS > SOUND has VOLUME (everything), MUSIC and EFFECTS. The sound driver's sound
effects take over some of the music's channels while they play, and those
channels follow EFFECTS for that time; see src/audio/volumes.h.
Touch controls can be reskinned: BMP images named after the buttons
(dpad.bmp, a.bmp ... start.bmp, ff.bmp, menu.bmp, button.bmp for all face buttons,
<name>_pressed.bmp while held) in Mods/Touch. The folder holds a
README.txt that lists them.
OPTIONS > INPUT > TOUCH STICK turns the touch d-pad into a joystick
(TouchStick = true in [Input]): the thumb that lands on it keeps it until
lifted, and steers even after sliding off the base. Its images are
stick.bmp (the base) and knob.bmp, each with an optional _pressed
version.
TOUCH LAYOUT (OPTIONS > INPUT, TouchLayout in [Input]) has CLASSIC,
every button of the pad, and SONIC CD, the layout of Sonic CD's 2013 mobile
version: the directions on the left and one large jump button on the right,
with HOLD (B) and CALL (A) small above it -- the only buttons this game
uses -- and anywhere else on the lower right of the screen jumping too.
TOUCH OPACITY and PRESSED OPACITY (OPTIONS > INPUT; TouchOpacity and
TouchPressedOpacity in [Input]) set how opaque the touch controls are at
rest and while held, custom images included: 65% and 100% by default.
The Mods folder is in the user data folder. On Android it is
ChaotixRecompiled/Mods in the phone's storage once you allow it (OPTIONS >
EXTRAS > MODS > MODS FOLDER opens Android's "All files access" page), and
Android/data/org.chaotixrecompiled/files/Mods until then. On iOS it is
Mods in the app's Documents, which the Files app shows.
In widescreen levels the HUD keeps to the left edge of the picture; OPTIONS > VIDEO > WIDE HUD puts it back at its 4:3 place.
Every kind of mod below can come as a pack: a folder of its own in Mods,
with a mod.ini (name, author, version, description, link, preview image)
and inside it the same Textures, Characters, Music, Patches and
Touch folders the Mods folder has:
Mods/
ChineseText/
mod.ini name = Traditional Chinese / author = ... / version = 1.0.1
preview.png
Textures/ mdtext_*.txt/.png, 32x_*.png, ...
FasterKnuckles/
mod.ini
Characters/knuckles.ini
Textures/ your own loose files, as before
OPTIONS > EXTRAS > MODS lists the packs with their versions: left/right turns
one on or off, Enter shows its description and preview with MOVE UP and MOVE
DOWN. Where two enabled packs replace the same thing (a tile, a sprite frame,
a text pattern, a character's file, a piece of music, a touch button) the
higher one wins; patches of every enabled pack are applied, the lowest
first. Loose files in Mods/Textures, Mods/Music, ... still work, above
every pack. Graphics, music and touch images change as soon as you leave the
page (or press RELOAD MODS); patches and character stats the next time the
game starts. The choices are kept in [Mods] in Config/chaotix.ini.
Details, and how to publish a pack on GameBanana:
docs/MOD_PACKS.md.
.ips and .bps files in Mods/Patches are applied, in name order, to the
ROM in memory as the game starts: graphics, palettes and other ROM hacks
without rebuilding, and without changing your ROM file. A patch that changes
the game's code works too; that code then runs in the interpreter. Details:
docs/ROM_PATCHES.md.
Both layers' graphics can be replaced with PNG files: the Mega Drive layer's
tiles (backgrounds and level art) and the 32X layer's sprites (the
characters, the HUD, objects, the title). OPTIONS > EXTRAS > DUMP GRAPHICS
saves each graphic the game draws to Mods/Textures/Dump, as md_<hash>.png
or 32x_<bank>_<frame>.png; an edited copy in Mods/Textures is drawn
instead, in the game's palette, wherever the game uses it. Only the picture
changes: the game runs exactly as without the mod. Details:
docs/TEXTURE_MODS.md.
To redraw a whole character, OPTIONS > EXTRAS > EXPORT SPRITES writes every
frame of each character's sprite banks, without playing, as one sheet per
bank to Mods/Textures/Sheets: 32x_<bank>.png, the frames side by side in
a grid, and 32x_<bank>.txt, where each frame is. A sheet and its .txt
copied into Mods/Textures replace every frame of that bank at once; a
frame's own 32x_<bank>_<frame>.png still wins over its cell
(chaotix_headless --export-sheets DIR does the same from the command line).
Mega Drive text built from letter tiles (the title menu, OPTIONS, PLAYER
SELECT, the DATA LOAD banners) can be replaced by sequence rather than tile
by tile, for translations: mdtext_<name>.txt names a run of tiles, such as
the seven of "OPTIONS", and mdtext_<name>.png is drawn over that run
wherever a plane shows it, and nowhere else. With DUMP GRAPHICS on, the runs
of tiles on each screen are listed in Mods/Textures/Dump/Text, with a
numbered screenshot, ready to copy. Details in
docs/TEXTURE_MODS.md.
Text drawn with Mega Drive sprites (the menu banners' letters, PLAY STYLE,
SOUND TEST, the 8x16 font's names) is replaced the same way by
mdsprite_<name>.txt + .png, naming the group of sprites that shows it;
the dump lists the sprites on screen in groups as Dump/Text/sprites_<n>.txt
(details).
Mods/Characters/<name>.ini (mighty, knuckles, charmy, vector,
bomb, heavy, espio) gives a character new colours ([Palette], its 18
colours as #RRGGBB; Espio's shimmer in [Cycle]) and new handling
([Stats]: acceleration, top speed, gravity, jump and more, by name).
Colours change only the picture; stats change the game, and take effect the
next time it starts. With new sprites in Mods/Textures, Mighty can become
someone else entirely. Details: docs/CHARACTER_MODS.md.
Put WAV files in Mods/Music (see above for where Mods is),
named after the game's music id in hex: 05.wav replaces BOTANIC BASE's
music. The game's sound effects still play. Loops come from the WAV's sampler
chunk or a 05.loop text file, and ModMusicVolume in [Audio] sets their
level. Every music request is logged with its id, so playing the game shows
which file to make; see docs/MUSIC_MODS.md. Only music
you have the right to use, please.
MUSIC PLAYER (on the front end, and under OPTIONS > SOUND, so from the pause menu too) is the game's sound test with its songs' names: the 41 tracks the game's own SOUND TEST lists, DOOR INTO SUMMER to DECISION 2ND, read from the ROM. PLAY, STOP, NEXT, PREVIOUS and FADE OUT; REPEAT loops the track, plays it once or plays the whole list, and FADE OUT AFTER fades out a track that would otherwise loop for ever. The music is the game's own FM, PSG and PWM, played by a second copy of the game booted to a quiet screen; a track a music mod replaces says REPLACED BY MOD and plays the mod's file. While the page is open your game is held, untouched, and it carries on when you leave. See docs/MUSIC_PLAYER.md.
rom_analyzer --rom <rom> [--coverage f.cov] --out dir [--refs-to ADDR] [--disasm ADDR[,COUNT]]— verification and reports (code spaces, functions, xrefs, hardware register usage).--refs-toprints every instruction that touches one address, with the code around it, which is how a variable is followed back into the game.--disasmlists a routine from one address, decoding straight from the ROM, so code the traces never reached can still be read.chaotix_recomp --rom <rom> --coverage f.cov --out generated— the recompiler.chaotix_headless --rom <rom> --frames N [--wide E] [--wide-bottom R] [--lockstep] [--compare-native] [--shot F] [--press F:btn:dur] [--coverage f] [--stage F:PLACE:LEVEL:TIME:PLAYER:COMBI:PLAYERS] [--replay-input f] [--trace ...]— deterministic headless execution and validation.--stageasks the game to start a level the way the front end's TIME ATTACK does, and--replay-inputplays back a session the program recorded with--record-input, so that something only a person can do — finishing a level, say — can be studied here as often as needed.--level-selectmakes the requests LEVEL SELECT ones and prints the scene reached and when the level is over (--level-select-none: with PARTNER NONE).--lobby-partner ID|noneand--lobby-time 0-3stand in for the lobby shortcuts' choices.chaotix_headless --ghost-record FILEwrites a--stageor replayed run as a TIME ATTACK ghost when it ends, and--ghost FILEraces one, drawn into the shots (--ghost-log Nprints where it is; docs/GHOSTS.md).chaotix_headless --jukebox TRACK:FRAMES --wav FILErenders a track with the music player (--jukebox-listlists the tracks;--jukebox-at FRAME:TRACK:FRAMESopens it in the middle of a run; docs/MUSIC_PLAYER.md).chaotix_headless --speedrunfollows the session with the speedrun timer (--speedrun-start FRAMEstarts a run by hand) and prints its starts, splits and resets;--input-displaydraws the input display into the shots (docs/SPEEDRUN.md).chaotix_headless --save-state FRAME:FILEwrites a save state after that frame and--load-state FILEstarts from one (the run then goes on to--frames);--state-hash-every Nprints a fingerprint of the whole machine and--rewind INTERVAL[:MB]times the rewind buffer and checks every snapshot it holds (docs/SAVE_STATES.md).chaotix_headless --netplay-loopback LATENCY:JITTER:LOSSplays both sides of a netplay session in one process over a simulated network with fuzzed pads, and checks both end identical to a single machine fed the confirmed pads;--netplay-host PORT/--netplay-join ADDRESSdo it over real UDP, one side per process (docs/NETPLAY.md).
cd build && ctest --output-on-failureUnit tests always run. With CHAOTIX_ROM set, the integration tests compare
the recompiled build against the reference interpreter frame by frame, check
golden frame hashes for 4:3, widescreen and 16:9, and verify that widescreen
leaves the centre 320 pixels untouched. -DCHAOTIX_LONG_TESTS=ON adds
ctest -L long: a 36,000-frame lockstep run and 720 golden render hashes, a
few minutes.
Coverage traces for the recompiler are recorded with
python tools/coverage/record_sessions.py --rom <rom> (writes coverage/*.cov).
The screens the program draws itself (first-run setup, achievements) use Inter, which is compiled into the executable so they work before anything has been installed. See THIRD_PARTY.md.
Knuckles' Chaotix Recompiled is made by YuutaTsubasa, with the help of the people below. Thank you all: every report here led to a fix or a feature.
- shinmagus: the OPTIONS > SCALING label (#14), non-ASCII file and folder names on Windows (#15), closing the front menu handing the game over (#16), and install error messages (#17).
- The app icon is the game's logo (Sega / Sonic Team), from Wikimedia Commons.
- Up to 0.6.1 the icon was pixel art of Knuckles by Cotton_Candy_2C, from SteamGridDB, suggested by Abdouxgo (#22).
- Nickwastakenwastaken: ROM loading (#2).
- sloppydoppies: the front end, achievements, mod support and audio, which led to music replacement, and separate volumes (#3, #24).
- Pokefan2098: ROM verification and rendering bugs (#4, #5).
- blueeeLove: widescreen special stages (#6, #18).
- SegaSaturnFan: widescreen dropping to 4:3 (#7) and Marina Madness's clouds and sea in widescreen (#36).
- datadrivezz: the touch menus, touch control opacity, ROM patches and suggestions for the mobile build (#8, #9, #33, #35).
- mechasalesman: widescreen in the lobby, and cheats (#13, #20).
- Abdouxgo: 32-bit Android, and the icon suggestion (#19, #22).
- ZacOnGH: the Linux build (#23).
- CooliKnucx: touch control images, the menu size and the level select (#25, #49, #51).
- rsomethin: settings lost on mobile (#26).
- HickySnow: special stage progress in widescreen (#27).
- lemanrush: the HUD at the widescreen edge, choosing the menu background, and widescreen bonus stages and special stage results (#28, #37, #38, #39).
- DoodleDiskette: a reachable Mods folder on Android (#29).
- stuffinix: the touch joystick (#30).
- DanKorell: the lines in the menu text (#5).
- Heartic12: the special stage lag on a slow PC (#31).
- blueeeLove: the Sonic CD touch layout (#34).
- TramplinStu: choosing no partner (#55).
- iisonic707ii-spec: the special stage's black lines and its widescreen in play, and bonus stages entered from a level staying 4:3, and the final boss widened by WIDE LOBBY (#21, #52, #56).
- ToastedSquidBread: a video review that found the HUD jumping at the start of a level, black bars on the way into the special stage, and Knuckles dropping out of a glide when turning round on a keyboard (#91, #92, #93).
- UsagiJojo's World Updates: pointed us to that review.
- Sonic Retro, whose list of cheat codes the built-in cheats come from.
- PicoDrive, ares, MAME and Genesis Plus GX, used as references while
checking the 32X and Mega Drive behaviour (no code taken; see the notes in
docs/). - Third-party libraries and fonts: see THIRD_PARTY.md.
Knuckles' Chaotix is a trademark of Sega; this project is not affiliated with Sega. See LEGAL.md.
Contributed and not listed, or want a different name or link? Open an issue.
CONTRIBUTING.md — in short: never post game data, and a change to emulated behaviour is reviewable only with a lockstep run behind it.
This repository contains only original code. It does not contain, and must not be used to distribute, Sega's copyrighted ROM, BIOS or assets.
The releases ship a built binary containing the recompiled code, as other static recompilation projects do. That binary holds a translation of the game's instructions and none of its data — no graphics, music or level data — and it stops at the setup screen until you supply your own ROM. That position is not settled law; LEGAL.md sets out what is shipped, what is not, and the risk.
Not affiliated with or endorsed by Sega.
MIT, for the original code in this repository.
That covers this project's own work only. The bundled Inter font is under the SIL Open Font License, and SDL3 and SDL_ttf under Zlib; see THIRD_PARTY.md. It does not grant you any rights to Knuckles' Chaotix itself, which remains Sega's (LEGAL.md).