Demon's Crest (SNES Recomp)

Demon's Crest (SNES Recomp) is a native PC recompilation of Demon's Crest for Super Nintendo Entertainment System with adaptive widescreen, save states, and rewind.

README

Demon's Crest Recompiled

This fork ships no game code, ROM data or game screenshots. The upstream screenshots were removed on this branch.

Play on Windows (no coding needed)

  1. Download DemonsCrest-SNESRecomp-windows-x64-v1.0.0.zip from this fork's Releases page and unzip it anywhere.
  2. Open DemonsCrestSNESRecomp.exe.
  3. Click Browse For ROM and pick your own Demon's Crest (USA) ROM (.sfc/.smc, SHA-256 18d40a80...5844de).
  4. Let it build. The first time it downloads a portable compiler and builds the game on your PC, which takes a few minutes.
  5. Click Play. Next time, just open the exe.

The zip has no game code and no ROM. It holds the setup exe, its DLLs, the GPL shaders, the prebuilt snesrecomp-analyze.exe (so no Rust install is needed) and the build tools/configs. The first run downloads a portable toolchain (about 800 MB) into toolchain\ beside the exe and builds the game into build\; after that, opening the setup exe hands off to build\DemonsCrestSNESRecomp.exe. The zip root is flat with a single exe, so launchers such as Quiver Launcher pick it up automatically. Everything is built on your machine from your own cartridge dump. In game, press F12 or Escape (or the pad Guide button) to open the Options menu, where you pick a CRT/RetroArch shader and the 120 Hz motion mode. Rewind is now Shift+F12.


Native PC recompilation of Demon's Crest (USA), built on snesrecomp. The 65816 game code is translated to C and runs on the framework's authoritative interpreter, while the SNES hardware — PPU, APU/SPC700, DMA/HDMA and cartridge mapping — is emulated. Same model as N64Recomp and similar projects: recompile the CPU, emulate the silicon.

Play with adaptive widescreen, save states, rewind and the shared launcher, using your own legally dumped ROM.

Quick start | Features | Widescreen | Recording | Known issues | Build | Run

You must legally own a copy of the game. No ROM data is distributed with this project, in the repository or in any release. The recompiled C is generated locally from your own copy and is never committed.

This fork: options menu, RetroArch shaders, 120 Hz (branch shaders-120hz)

Additions on top of upstream (no game code, ROM, or generated C is committed):

  • In-game Options menu - press F12 or Escape (no modifiers) or the gamepad Guide button. Arrows / D-pad move, Left/Right change, Enter or pad A accepts, Backspace or pad B goes back, Escape / F12 / Guide close it. The game pauses while it is open.
  • Shader - every root-level RetroArch .glslp preset from libretro/glsl-shaders (commit cba01a48), applied live. Needs OutputMethod = OpenGL (now the default). Chosen only from this menu; config.ini's Shader key is ignored.
  • 120Hz motion - Off / Interpolate / Low-latency. Game logic stays at 60 Hz; on a 100 Hz+ display each in-between frame is re-rendered with interpolated scroll and sprite positions (widescreen aware). Interpolate is smoothest (blends the previous and newest frame, about half a frame of added latency); Low-latency extrapolates ahead of the newest frame instead (no added latency, may overshoot briefly when motion stops).
  • Both settings persist in dc_options.ini beside the executable.
  • Rebinds: Rewind moved from F12 to Shift+F12 (pad Select+R3 is unchanged). Escape had no binding in single-player play (the framework uses it only to leave a netplay session, which this target does not build, and to close its own save-state/rewind overlays, which still works).
  • No console window on Windows - the game exe is a GUI-subsystem app; its log goes to DemonsCrest.log beside the exe. Run it from a terminal (log prints there) or pass --console to open a log console.
Setup and build
git clone -b shaders-120hz https://github.com/rac08241988/DemonsCrestSNESRecomp
cd DemonsCrestSNESRecomp
scripts/setup.sh --rom "/path/to/Demon's Crest (USA).sfc"   # submodules, framework patch, shaders, regen
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release && cmake --build build
# (shaders/ is staged beside the exe automatically when <repo>/shaders exists)
# Windows exe from Linux:
cmake -S . -B build-win -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-w64.cmake -DCMAKE_BUILD_TYPE=Release

Release builder zip (no game code). Build the setup host from a tree with an empty src/gen/, then pack it. The packager refuses ROMs and generated C, and ships the patched sources plus shaders/, so the player's machine builds the game with the launcher's portable cmake-clang-v1 toolchain:

scripts/setup.sh                              # no --rom: patch + shaders only
cmake -S . -B build-setup -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-w64.cmake \
      -DCMAKE_BUILD_TYPE=Release -DSNESRECOMP_SETUP_HOST=ON
cmake --build build-setup
scripts/package_release.sh build-setup windows-x64   # dist/demonscrest-<ver>-windows-x64.zip

setup.sh checks your ROM against rom_identity.txt (sha256 18d40a80...5844de, a 512-byte copier header is stripped) before generating code. Fork note: upstream's snesrecomp gitlink a085bfc is not published on magnopaz/snesrecomp, so this branch pins 88d24ef (demons-crest/host-hooks). Test hooks: DC_SHADER_PRESET=<label>, DC_MOTION=off|interpolate|lowlatency, DC_INTERP_HZ=<hz>. Licensing of the additions: see NOTICE.md.

Status

The port boots and plays: the Capcom logo, intro, stage gameplay and boss fights all run from the generated C. The native Widescreen mod is implemented and validated through its final phase — background margins, objects and sprites, HUD anchoring, the Mode 7 overworld, the top-band flicker and save-state round-trips — with the engineering notes and validation summary in docs/PORTING_NOTES.md. See Known issues for what playtesting has since turned up, including the save-state CPU caveat.

Playtesting continues; general gameplay bugs and broader coverage are the remaining work, so treat a full-game playthrough as not yet certified.

What has been verified by playing it

These were confirmed by actually playing (and scripting) the game, not only by frame-accuracy checks:

  • Boot → play: logo, intro, title, attract and a new game all run.
  • Stage gameplay and bosses: the ice cavern, mountains and castle stages run with their bosses, HUD, crest icons and forms intact.
  • Widescreen: the widened field really renders stages, margins, HUD anchoring and the Mode 7 overworld as described below.
  • Forms: switching gargoyle forms works and each form's sprites play.
  • Scripted input: the debug server can drive the game from reset (set_controller, run_to_frame), which is how the boss and door tests in the companion port are run.
  • Recording (new): input can be recorded and replayed frame-exactly, and frames or a rolling buffer can be written to disk — see docs/RECORDING.md.
Known issues

Found while using the runner as a reference for a native port. None of them stop normal play, but they matter to anyone scripting or capturing the game.

  1. Save states do not restore the CPU. Loading a state brings back WRAM/VRAM/CGRAM/OAM, but the 65816 starts from reset: the screen shows the Capcom copyright while the memory still holds the saved scene. A state made during a boss fight therefore cannot be resumed or screenshotted — the game plays its boot sequence instead. Legacy and current states both behave this way when the ROM's own save is not part of the state.
  2. screenshot / dump_frame_raw capture the current frame, and frames only advance through run_to_frame. A paused runner that is asked for a screenshot without advancing returns the same (stale) picture, which looks like a drifting capture. Pair every capture with a frame advance.
  3. run_to_frame returns before the target frame is reached. Clients that read memory immediately after the command see the old frame; poll frame until it reports the target.
  4. Widescreen edge columns can show art that was never authored for them. In the mountain stage the far-right margin can show a strip of unrelated tiles (a hazard of widening a 256-pixel game); the castle stage shows black there. Cosmetic only, and only with the widescreen mod enabled.
  5. Boss sprite composition is only correct in the runner. The native port built from these captures had to reassemble boss OAM parts itself and got them wrong for a while; that is a port problem, recorded here so the distinction is clear when comparing screenshots.

Features

AvailableFeatureWhat it adds
☑Adaptive widescreenNative widened field with adaptive, 16:9, 21:9 and 32:9 views. Disabled by default; with it off the game renders at the original 256-pixel width.
☑Save statesTen slots (F1–F10 load, Shift+F1–Shift+F10 save) plus the save-state browser (F11). States load in a fresh process, but they do not restore the CPU — see Known issues.
☑RewindRewind history (F12) over the shared framework's snapshot ring.
☑RecordingAlt+R starts/stops a take: one BMP per presented frame plus a replayable input_log.jsonl, in captures/rec_<time>/. Needs no debug server; the protocol can additionally replay the pad stream frame-exactly or keep a rolling capture. See docs/RECORDING.md.
☑Launcherrecomp-ui ROM picker with a verification badge, display/audio/input settings and the Mods screen.
☑ROM verificationThe ROM is checked against the project digests before anything boots; a mismatched dump is refused instead of mis-executing.
☑Authentic 4:3The original 256-pixel field and layer layout remain the default when the widescreen mod is off.

Widescreen

Enable Widescreen in the launcher's Mods screen and pick a view: Adaptive, 16:9 (342 px), 21:9 (448 px) or 32:9 (682 px). The mod is off by default.

What it does:

  • renders the game directly into the widened field (native_widescreen), so the scrolling backgrounds continue into symmetric margins;
  • gates widening to stage gameplay ($8D in 1..0x7F, or the Mode 7 overworld map with its alternate-HUD variant): menus, cutscenes, password and transitional screens stay pillarboxed;
  • widens the object-loop cull and the OAM emitter so enemies and effects just outside the native view keep updating and appear in the margins, and widens the attack projectile's on-screen test so shots fly across the whole field;
  • anchors the BG3 status bar to the widened left edge and leaves the freed columns transparent, showing the scenery where the bar used to sit; BG3 is widened only on the bar's scanlines, so a dialogue box on the same layer keeps its native columns;
  • keeps the Start/status menu pillarboxed ($00FB marks it): it is a full-screen native UI, and widening it would show the stage tilemap in the margins;
  • drops the game's forced-blank upload cover from the raster replay in every scene (a field that stays blank keeps its black), which removes the black band that flickered at the top and lets HDMA tables start at the top of the field, keeping window effects aligned with their targets;
  • keeps dialogue boxes and edge windows intact: the fixed-color box cannot leak a strip into the top-left corner, and a parked one-pixel layer window moves with the widened edge instead of cutting a one-pixel seam at the margin boundary;
  • opens the Mode 7 overworld map to the widened field; the terrain continues into the margins with no renderer change.

The generated-code hooks are applied by tools/dc_apply_widescreen_hooks.py, an idempotent injector run by tools/regen.sh and by the build. At margin 0 every injected expression evaluates to the authentic constant, so the object and sprite hooks add nothing to the 4:3 path.

Diagnostic kill-switches for A/B captures: SNESRECOMP_WS_OAM=0 (object/sprite widening), SNESRECOMP_WS_HUD=0 (status-bar anchoring), SNESRECOMP_WS_NOBLANK=0 (upload-cover policy), SNESRECOMP_WS_CULL=<px> (cap the margin object window; 0 = authentic cull) and SNESRECOMP_WS_EXTRA=<px> (fixed margin).

Quick start (build from source)

  1. Clone with the framework and launcher submodules:

    git clone --recurse-submodules https://github.com/magnopaz/DemonsCrestSNESRecomp.git
    cd DemonsCrestSNESRecomp
    
  2. Have your own Demon's Crest (USA) ROM available. Keeping it outside the repository is the better habit; SNESRECOMP_ROM sets the path once for a shell.

  3. Generate the recompiled C from the ROM:

    bash tools/regen.sh --rom "/path/to/Demon's Crest (USA).sfc"
    
  4. Configure and build:

    cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
    cmake --build build -j
    
  5. Run:

    ./build/DemonsCrestSNESRecomp
    

    The launcher asks for the ROM on the first run and caches the choice.

On Windows with the MinGW toolchain, tools/play.ps1 wraps regeneration, configuration, build and launch in one step.

Build

The default desktop backend is SDL3 (fetched and built from source when no package is installed). To build against an installed SDL2 development package instead — for example the MinGW one at M:\SDL on Windows:

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
  -DSNESRECOMP_SDL_BACKEND=SDL2 \
  -DSDL2_DIR="M:/SDL/SDL2-2.32.6/x86_64-w64-mingw32/lib/cmake/SDL2"

tools/play.ps1 automates regen + configure + build + launch for the Windows MinGW toolchain (CMake, Ninja, GCC, Rust GNU, SDL2 under M:\SDL). It refuses to build while a game instance is running (a forced kill would end that session with no exit report); pass -KillRunning to terminate it explicitly.

The ROM does not have to live in the repository — SNESRECOMP_ROM sets the path for a shell. Without it, tools/regen.sh looks for Demon's Crest (USA).sfc at the repo root, then for the path the setup wizard recorded in rom.cfg; .gitignore blocks both from ever being committed.

tools/regen.sh verifies the ROM, generates src/gen/*.c, and re-syncs recomp/funcs.h. Re-run it whenever you change anything under recomp/.

Run

./build/DemonsCrestSNESRecomp
# or name the ROM and skip the launcher
./build/DemonsCrestSNESRecomp "/path/to/Demon's Crest (USA).sfc"

With no ROM on the command line the build opens the recomp-ui launcher: a ROM picker with a verification badge, display/audio/input settings and the Mods screen. Your choice is cached in rom.cfg beside the executable, so the next launch opens on it and "Skip launcher on boot" makes it immediate. If the launcher is not built in (--no-recomp-ui), the host falls back to a native file picker.

Either way the ROM is checked against the digests below before anything boots — a dump that could not have produced this build is refused at the door rather than mis-executing ten frames in.

During play: F11 opens the save-state browser and F12 opens rewind; both pause gameplay while you choose. Alt+R starts/stops a recording (frames plus a replayable input log, saved under captures/). Hotkeys, controller mappings and pixel/aspect options live in the launcher; Alt+R can be moved with Record = <key> in config.ini's [KeyMap].

Recording

The debug server can capture a session and reproduce it, which is how the screenshots above and the port's reference captures are made:

# keep the last ~5 seconds in memory and write them when the window closes
SNESREC_RING_DIR=captures/somulo ./build/DemonsCrestSNESRecomp "Demon's Crest (USA).sfc"

# or, with SNESRECOMP_DEBUG_PORT set, from a debug client
record_input_start
... play the fight ...
record_input_stop captures/fight.jsonl
replay_input captures/fight.jsonl      # reproduce it frame-exactly
record_frames captures/somulo 600 1     # one BMP per frame
python tools/bmp_sequence.py captures/somulo --gif somulo.gif

Input is sampled where the runner reads the pad, so file frame N is the input frame N ran with. See docs/RECORDING.md for every command and its limits.

ROM identity

FileDemon's Crest (USA).sfc
PublisherCapcom
DeveloperCapcom
Year1994
Mappinglorom
RegionUSA (North America)
Coprocessornone
CRC32e8236ad2
SHA-25618d40a807d5f88c5b6a1ad849eec7e0f189225d9a1586037c850f6680b5844de

tools/regen.sh refuses to run against anything else, so a mismatched revision fails immediately instead of producing subtly wrong output.

How the recompilation works

The 65816 CPU code from the ROM is statically translated to C — every function the analysis can prove is a real generated C function in src/gen/. Execution is LLE-first: an authoritative 65816 interpreter is the correctness floor, and the statically compiled bodies are exact, proven materializations on top of it — anything the static pass cannot prove keeps running through the interpreter, loudly. The rest of the SNES is not recompiled: PPU rendering, the APU/SPC700, DMA/HDMA, hardware register I/O and bank mapping run through snesrecomp's runner implementations.

Rules for contributors:

  1. Never edit src/gen/ by hand and never synthesise a result to get past uncovered code. Fix the config or the framework and regenerate.
  2. Name routines in recomp/symbols.toml, then re-run tools/regen.sh. Set emit = true to promote a routine into ahead-of-time analysis; leave it false to keep it interpreted. Only proven variants become AOT code. See recomp/README.md.
  3. Deal with unresolved indirect dispatch targets before anything else — they are the reason a port diverges.

Layout

PathWhat lives there
recomp/Analysis input: bank*.cfg, symbols.toml, generated funcs.h
rom_identity.txtROM digests — read by the build, tools/regen.sh and CI
src/Host code you own: main.c, game_rtl.c, widescreen policy
src/gen/Generated C. Never committed — regenerate locally
docs/PORTING_NOTES.md — verified state, fixes and roadmap
mods/Preloaded mod catalog (the widescreen package)
tools/regen.sh, play.ps1 and the widescreen code injector
snesrecomp/Framework submodule (owns lib/recomp-net, lib/retcomm-rbengine)
recomp-ui/Shared launcher UI submodule
scripts/package_release.sh — player-facing zip
framework_pins.txtExact framework commits this project was scaffolded against

Reporting problems

Open an issue with the build, the enabled mods and the steps to reproduce the problem. For gameplay bugs, attach the save state (build/saves/saveN.sav) and describe the inputs that trigger it. If the game closes unexpectedly, also attach last_run_report.prev.json (the previous run's report, preserved on the next launch) and any crash_report_*.json / crash_minidump_*.dmp from the build directory.

github.com/magnopaz/DemonsCrestSNESRecomp/issues

License

This project's own source is under the MIT license in LICENSE. The framework and launcher carry their own terms — see snesrecomp/LICENSE, snesrecomp/THIRD_PARTY_ATTRIBUTION.md and recomp-ui/LICENSE. Neither covers the game data, which