Static recompilation of Astral Chain (Nintendo Switch, 01007300020FA000) into a native
Linux x86_64 executable.
The game's AArch64 code is translated ahead of time into C and compiled for the host. The
translated code runs on a vendored runtime derived from suyu: HLE kernel and services,
video_core (Vulkan) and audio_core. When the static pass misses an address, dynarmic is
used as a JIT fallback.
No game code or assets are part of this repository. To build a playable binary you need
your own copy of the game with update 1.0.1 applied: either an extracted exefs/ + romfs/
folder, or the NSPs plus your own console keys.
- M1 (foundation): done.
- M2 (pipeline): done.
tools/acre.pygoes from your own dump to a native binary that runs fully ahead-of-time compiled. - M3 (playable at 30 fps): done.
astralchain.iniholds the controls, display and audio settings, and saves live in$XDG_DATA_HOME/astralchain/. The play checklist (docs/checklist.md) passed with 0 fallbacks. - M4 (60 fps and internal resolution): done.
fps60 = trueruns the game at 60 fps with cutscenes and gameplay at the right speed;resolution_scaleraises the internal resolution. The 60 fps rows of the play checklist passed. - M5 (in-game overlay): done. Insert on the keyboard or L3+R3 on a controller opens an
overlay with live performance, the
astralchain.inisettings, AOT counters and the log. - Next: a tuning milestone (steady 60 fps, then 1440p and 4K).
See docs/superpowers/specs/2026-10-14-astralchainre-design.md for the design and roadmap.
- Linux x86_64 on an x86-64-v3 CPU (AVX2 and FMA; any Ryzen, Intel since Haswell). The
lifted guest code is built for that level; configure with
-DACRE_RECOMP_ARCH=x86-64for an older CPU, at a cost in speed. - clang 22 for the lifted C (
acre.py builduses it) and GCC 16 or clang 22 for C++ - CMake ≥ 3.22 and Ninja
- SDL3, Vulkan loader, FFmpeg, Boost, OpenSSL (
openssl-develis also needed by the lifter tests)
Other dependencies are fetched by CPM at configure time. Their cache lives in .cache/cpm.
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DVulkanHeaders_FORCE_BUNDLED=ON \
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=c++
cmake --build buildThis builds build/bin/astralchain in its JIT-only form.
To build the statically recompiled variant from an existing export, add
-DASTRALCHAIN_RECOMP_DIR=<export dir> and build the astralchain-static target.
You need one of:
- a game folder (main path): the extracted
exefs/andromfs/of the game with update 1.0.1 applied.exefs/holdsmain,sdk,rtldandmain.npdm. No keys needed; - the NSPs (alternative): the base game (
[01007300020FA000][v0]), update 1.0.1 ([01007300020FA800][v65536]) and your console keys, a directory withprod.keys(default~/.local/share/suyu/keys).tools/acre.py extractturns these into a game folder.
Other game versions are refused.
Build the JIT frontend once, as described in Build. Then:
Settings live in ~/.config/astralchain/astralchain.ini (created on the first run).
Saves live in ~/.local/share/astralchain/profile.
tools/acre.py setup --game-dir GAME_DIR # the folder with exefs/ and romfs/ (main path)
# or, from NSPs (needs keys):
tools/acre.py setup --base "BASE.nsp" --update "UPDATE.nsp" [--keys DIR]
tools/acre.py export # C from the dumped modules; incremental
tools/acre.py build -j16 # astralchain-static (the first build takes a few minutes)
tools/acre.py run # play
tools/acre.py run --validate # 180 s headless, strict AOT: PASS/FAILWhat each command does:
setup --game-dirdumps the game's modules from the folder as the emulator loads them (the loader mountsromfs/next toexefs/), and checks their hashes againstgame/version.json. A modded or older dump is refused.setup --base/--updatedoes the same from NSPs: it installs the update into an isolated profile (work/profile) first.extract --base BASE --update UPDATE [--keys DIR]writes the 1.0.1 game folder (work/dump-1.0.1) from the NSPs, for people who have those and keys but no folder. Then runsetup --game-diron it.- If a run reports fallback sites,
tools/acre.py roots --from work/logs/validate-*.jsonadds them togame/roots/, and the nextexportcovers them. - On Linux x86-64,
buildinlines the lifted code's guest memory accesses (FM2), which ran a city scene about 20% faster.build --no-fastmemleaves them out, andfastmem = falsein the ini, orACRE_NO_FASTMEM=1, turns them off at run time. fps60 = truein the ini turns on the 60 fps patch (game/patches/fps60.patches); it takes effect on the next start. Combat does not hold 60 everywhere yet.resolution_scalesets the internal resolution as a multiple of the docked 1080p: 0.5, 0.75, 1, 1.5, 2 (2160p), 3 or 4.scaling_filterpicks how the image fits the window.reactive_flushingis off by default. Turning it on makes the game wait for the GPU before reading memory the GPU wrote, which costs about 10% in combat; try it if a picture or effect looks stale.- In game, Insert or L3+R3 opens the overlay; the game keeps running and does not
see your input while it is open. Its tabs (L/R on a controller) show performance, every
ini setting (most apply at once, the rest say "restart required"; "Save to ini" keeps
the file's comments), the AOT fallbacks and the log.
[overlay]in the ini changes the key and the button chord and turns on a small fps counter. - Everything derived from your dump stays in
work/, which git ignores.
Exit codes:
| Code | Meaning |
|---|---|
| 2 | usage |
| 10 | keys missing |
| 11 | game file missing |
| 12 | frontend not built |
| 13 | update install failed |
| 14 | dump failed |
| 15 | wrong game version |
| 19 | run setup first |
| 20 | run export first |
| 21 | run build first |
| 22 | cmake/ninja missing |
| 23 | export failed |
| 24 | build failed |
| 25 | validation failed |
ctest --test-dir build -L lifter # lifter: generated C per instruction family
ctest --test-dir build -L acre # pipeline unit tests (Python, stdlib only)Each lifter test generates a C program that exercises one family of AArch64 instructions, then compiles and runs it.
| Path | Contents |
|---|---|
runtime/ |
Vendored suyu modules (src/ upstream), with history |
frontend/ |
SDL3 frontend (astralchain executable) |
externals/, CMakeModules/, cpmfile.json |
Build infrastructure inherited from suyu |
tools/tests/ |
Lifter tests |
tools/acre.py, tools/acre/ |
Pipeline |
recompiler/ |
export_module generator |
game/ |
Supported version hashes, extra roots |
tools/import-runtime.sh |
Script that produced the vendored runtime |
work/ |
Local exports and builds (ignored by git) |
This project ships tools and a runtime, never the game.
- The repository contains no game code, assets, keys or saves. What it holds about the game
is metadata needed to check and patch your own copy: module hashes (
game/version.json), code entry addresses (game/roots/), and patch offsets with the few instruction words they replace (game/patches/). - You need a copy of the game and console keys that you dumped yourself from your own Switch. This project does not explain how to get either, and issues asking for them are closed.
- A built
astralchain-staticcontains the game's code, translated from your dump. Do not share builds, exports or anything underwork/. Share source changes only. - Astral Chain is a trademark of its owners. This project is not affiliated with or endorsed by Nintendo or PlatinumGames.
GPL-2.0-or-later, inherited from suyu and yuzu. See LICENSE.txt and LICENSES/.