https://github.com/ecliptik/doskutsu
A port of Cave Story to DOS using SDL
https://github.com/ecliptik/doskutsu
cave-story dosbox msdos retrocomputing retrogaming sdl
Last synced: 8 days ago
JSON representation
A port of Cave Story to DOS using SDL
- Host: GitHub
- URL: https://github.com/ecliptik/doskutsu
- Owner: ecliptik
- License: other
- Created: 2026-04-26T00:50:37.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-25T16:51:23.000Z (21 days ago)
- Last Synced: 2026-05-25T18:30:39.073Z (21 days ago)
- Topics: cave-story, dosbox, msdos, retrocomputing, retrogaming, sdl
- Language: Shell
- Homepage:
- Size: 1.85 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# DOSKUTSU
DOSKUTSU is a faithful port of [Cave Story](https://www.cavestory.org/) (Doukutsu Monogatari) to MS-DOS 6.22 on retro Pentium-class hardware. It plays Daisuke "Pixel" Amaya's 2004 freeware classic on real 1990s-era PCs via [SDL3](https://www.libsdl.org/)'s [DOS backend](https://github.com/libsdl-org/SDL/pull/15377), [DJGPP](https://www.delorie.com/djgpp/), and [CWSDPMI](https://en.wikipedia.org/wiki/DOS_Protected_Mode_Interface).
The name is a portmanteau of **DOS** and **Doukutsu Monogatari** (Cave Story's original Japanese title).
DOSKUTSU exists for preservation and the engineering challenge of running Cave Story on a 1990s MS-DOS PC.
Status | Game Assets | Requirements | Usage | Configuration | Building | How It's Developed | Components and License
### Screenshots
| | |
|:---:|:---:|
|
|
|
| **Title Screen** | **Opening Transmission** |
|
|
|
| **First Lab Room** | **First Cave** |
captures from DOSBox-X running DOSKUTSU.EXE
---
## Status
DOSKUTSU plays the full game, start to finish.
Frame rate depends on the hardware and how music is played:
| Hardware | Music | Frame rate |
|---|---|---|
| Pentium-class (reference PC) | MIDI (OPL3) | ~30 fps |
| Pentium-class (reference PC) | Organya software synth | ~21 fps |
| 486-class (Am5x86-133) | MIDI (OPL3) | ~23 fps, playable |
| 486-class (486DX2-66) | MIDI (OPL3) | ~12 fps, playable but choppy |
| 486-class (486DX2-50) | MIDI (OPL3) | ~8 fps, marginal - runs slightly slow |
Cave Story runs at 50 fps; the reference PC's hardware limits fully-detailed rendering to about 30 fps. It still plays at the correct 50 Hz speed through [Fixed-Timestep mode](#fixed-timestep-mode), which advances game logic on a fixed 50 Hz clock independent of the render rate.
As of 1.0.1, the 486-class audio bugs are fixed and on by default: SFX no longer stutter on rapid fire, music tempo holds steady, and SFX play at the correct pitch and a balanced level against the music. See the [changelog](CHANGELOG.md) for details; each fix has a killswitch if needed.
### Cross-CPU benchmark (1.0.1, Cirrus CL-GD5430)
The 1.0.1 ship configuration, benchmarked with a fixed input recording across four CPUs on the same board (video card: Cirrus CL-GD5430):
| CPU | fps (median) |
|---|---|
| Pentium OverDrive 83 | ~33 |
| Am5x86-133 | ~32 |
| 486DX2-66 | ~19 |
| 486DX2-50 | ~15 |
The audio fixes did not cost frame rate. This benchmark uses a lighter scene than the heavy-music figures in the table above, so the two are not directly comparable; it is recorded as the Cirrus baseline for an upcoming video-card comparison.
### Fixed-Timestep mode
Cave Story's engine advances game logic once per rendered frame, so at 30 fps the game also runs at about 60% speed - sluggish. Fixed-Timestep mode decouples the two: logic advances on a fixed 50 Hz clock regardless of frame rate, so the game plays at its intended speed even though the screen draws fewer frames. The motion is less smooth; the speed is correct.
It is on by default as of 1.0; set `SDL_HINT_DOSKUTSU_FIXED_TIMESTEP=0` to use the legacy frame-coupled loop.
### Audio backends
The soundtrack plays with either Cave Story's original Organya synthesizer or with MIDI. Organya is more faithful to the original, but has a significant performance impact; MIDI is recommended and the default for playing on DOS.
MIDI plays through a hardware synthesizer, off the CPU. Two settings shape it:
| Setting | Environment variable | Options | Picks |
|---|---|---|---|
| Synthesizer | `SDL_HINT_DOSKUTSU_AUDIO_BACKEND` | `auto` (default), `wb`, `opl3`, `organya` | `auto`: probe WaveBlaster daughterboard first, fall back to OPL3 FM; explicit values force a specific backend. `wb`: WaveBlaster / DreamBlaster-class wavetable daughterboard on the SB16 WaveBlaster header (validated on Vibra16S CT2490 + DreamBlaster S2). `opl3`: the SB16 / Sound Blaster Pro 2 OPL3 FM chip. `organya`: software synthesis of Pixel's original Cave Story tracker format (higher CPU cost). |
| MIDI source | `SDL_HINT_DOSKUTSU_AUDIO_MIDI_SOURCE` | `wiimidi` (default), `orgmid` | `wiimidi`: the WiiWare arrangement, which tracks the original closely. `orgmid`: the Hart legacy `.mid` set |
| GM variant | `SDL_HINT_DOSKUTSU_AUDIO_MIDI_GM_VARIANT` | `v1`, `v2` | an `org2mid`-converted General MIDI variant |
---
## Game Assets
**DOSKUTSU does not include any Cave Story game data.** The binary built from this repository plays nothing on its own. Users supply their own copy of the 2004 EN freeware assets, extracted from the canonical `Doukutsu.exe`.
[docs/ASSETS.md](./docs/ASSETS.md) is the canonical, complete asset procedure - follow it start to finish; it covers fetching the freeware bundle and extracting the full data tree (maps, sprites, music, SFX) plus the expected directory layout. The two scripts below automate only the Pixtone-SFX slice of that workflow; running them alone does not produce a playable `DATA\` tree:
- `scripts/fetch-cs-pxt.py` is the one-shot orchestrator. It fetches the 2004 EN freeware bundle from [cavestory.one](https://www.cavestory.one/downloads/cavestoryen.zip) (SHA-256-pinned), extracts `Doukutsu.exe` to a tempdir, runs the Pixtone parameter extractor, and cleans up. The freeware archive does not persist on the user's machine after the script completes.
- `scripts/extract-pxt.py` is the canonical extractor, transcribed from NXEngine-evo's own `extract/extractpxt.cpp`. It operates on file offsets in `Doukutsu.exe` and emits ASCII Pixtone parameter files.
The same posture applies as the broader Cave Story port community ([NXEngine-evo](https://github.com/nxengine/nxengine-evo), [doukutsu-rs](https://github.com/doukutsu-rs/doukutsu-rs)): the engine code is open source; the game data is user-supplied freeware.
---
## Requirements
**Recommended**
- CPU: Pentium 75 MHz or faster
- RAM: 16 MB
- Video: VESA 1.2+ with 320x240 support
- Sound: Sound Blaster 16 or compatible
- OS: MS-DOS 6.22 or compatible
- Disk: 10 MB free
**Minimum**
- CPU: 486DX2-66 with FPU
- RAM: 8 MB
- Video: VESA 1.2+
- Sound: Sound Blaster 16 or compatible
- OS: MS-DOS 6.22 or compatible
- Disk: 10 MB free
---
## Usage
`DOSKUTSU.EXE`, the CWSDPMI host, and the Cave Story data all live together in one directory:
```
C:\DOSKUTSU\
DOSKUTSU.EXE the game
CWSDPMI.EXE the DPMI host - must sit beside DOSKUTSU.EXE
DATA\ Cave Story assets, user-extracted (see Game Assets)
```
Quick setup:
1. Get `DOSKUTSU.EXE` - build it ([Building](#building)) or take it from a release bundle.
2. Get `CWSDPMI.EXE`. The build tooling fetches it automatically (from its upstream, at a pinned checksum) via `make fetch-binaries`, which `make install` and `make dist` run; it is not a manual download. Release bundles already include it.
3. Extract the Cave Story data into `DATA\` ([Game Assets](#game-assets)).
4. Copy the whole directory to the DOS machine.
5. The DOS machine needs a standard DJGPP-compatible boot environment: `HIMEM.SYS` loaded, `NOEMS`, a SB16-compatible `BLASTER` variable set, and a VESA 1.2+ video BIOS (a software VESA driver works as a fallback).
Run it from the game directory:
```
C:\DOSKUTSU> DOSKUTSU
```
The title screen appears within a few seconds. Controls follow NXEngine-evo's defaults:
| Key | Action |
|---|---|
| Arrow keys | Move / navigate menus |
| Z | Jump / confirm |
| X | Fire / cancel |
| A / S | Cycle weapons |
| Q | Inventory |
| W | Map |
| Escape | Pause menu |
| F11 | Toggle fullscreen (no-op on DOS; always fullscreen) |
---
## Configuration
DOSKUTSU is configured through DOS environment variables - the music backend, the Fixed-Timestep game-speed mode, audio quality, and hardware-compatibility fallbacks. Set them with `SET` in `AUTOEXEC.BAT` or at the DOS prompt.
[docs/CONFIG.md](./docs/CONFIG.md) is the complete reference: every option with its values, defaults, usage examples, and performance impact.
---
## Building
Full build documentation in [docs/BUILDING.md](./docs/BUILDING.md): prerequisites, DJGPP cross-compiler install, the four-stage build (SDL3, SDL3_mixer, SDL3_image, NXEngine-evo), DOSBox-X testing, common errors.
Short version, once DJGPP is installed:
```bash
git clone ssh://git@forgejo.ecliptik.com/ecliptik/doskutsu.git
cd doskutsu
./scripts/setup-symlinks.sh # one-time: link tools/djgpp to the emulators hub
./scripts/fetch-sources.sh # clone the upstream repos at pinned SHAs
./scripts/apply-patches.sh # apply DOS-port patches
make # orchestrate all four build stages
make smoke-fast # headless DOSBox-X smoke (fast config)
```
---
## How This Project Is Developed
DOSKUTSU is developed agentically with [Claude Code](https://claude.com/code).
- **Claude Code authors the patches** across the SDL3 DOS backend, the NXEngine-evo engine, the build system, scripts, and docs. They land as `patches//NNNN-*.patch` files in this repository.
- **Human developers drive testing and iteration**: TAS replays, real-hardware playthroughs, bug reports, and deciding what to fix next.
- **Workspace-local patches only.** This project does not contribute patches upstream to [libsdl-org/SDL](https://github.com/libsdl-org/SDL), [libsdl-org/SDL_mixer](https://github.com/libsdl-org/SDL_mixer), [libsdl-org/SDL_image](https://github.com/libsdl-org/SDL_image), or [nxengine/nxengine-evo](https://github.com/nxengine/nxengine-evo).
---
## Components and License
DOSKUTSU's own source - the build system, scripts, and documentation - is **MIT-licensed** ([LICENSE](./LICENSE)). The shipped `DOSKUTSU.EXE` is **GPLv3**: it statically links NXEngine-evo, which is GPLv3, and that license governs the combined binary. The DOS-port patches under `patches/` are derivative works of their upstreams and carry those upstreams' licenses: GPLv3 for the NXEngine-evo patches, zlib for the SDL3 patches. Redistributed bundles carry the GPLv3 license text and a pointer back to this repository.
Each component below is listed with its purpose, license, and whether it links into `DOSKUTSU.EXE`:
| Component | Purpose | License | In `DOSKUTSU.EXE` |
|---|---|---|---|
| [DOSKUTSU port source](./LICENSE) (this repo) | Build system, patches, scripts, docs | MIT | n/a - source, not the binary |
| [NXEngine-evo](https://github.com/nxengine/nxengine-evo) | The C++11 re-implementation of the Cave Story engine | [GPLv3](https://github.com/nxengine/nxengine-evo/blob/master/LICENSE) | **Yes - governs the binary** |
| [SDL3](https://www.libsdl.org/) | Platform layer; its [DOS backend](https://github.com/libsdl-org/SDL/pull/15377) is what makes the port possible | [zlib](https://github.com/libsdl-org/SDL/blob/main/LICENSE.txt) | Yes |
| [SDL3_mixer](https://github.com/libsdl-org/SDL_mixer) | Audio mixing | [zlib](https://github.com/libsdl-org/SDL_mixer/blob/main/LICENSE.txt) | Yes |
| [SDL3_image](https://github.com/libsdl-org/SDL_image) | Image loading | [zlib](https://github.com/libsdl-org/SDL_image/blob/main/LICENSE.txt) | Yes |
| [DJGPP](https://www.delorie.com/djgpp/) libc | 32-bit DOS C runtime, by DJ Delorie | [GPL + runtime exception](https://www.delorie.com/djgpp/v2faq/faq11_2.html) | Yes - the exception permits static linking |
| [CWSDPMI](https://www.delorie.com/pub/djgpp/current/v2misc/) | DPMI host, by Charles W. Sandmann | [freeware, redistributable](./vendor/cwsdpmi/cwsdpmi.doc) | No - ships alongside as a separate program |
| [Cave Story](https://www.cavestory.org/) game data | Maps, sprites, music, and SFX, by Daisuke Amaya (2004) | [freeware, 2004 terms](https://www.cavestory.org/) | User extracted, not redistributed |
Built with the [DJGPP](https://www.delorie.com/djgpp/) toolchain (installed via [build-djgpp](https://github.com/andrewwutw/build-djgpp) by Andrew Wu), tested with [DOSBox-X](https://dosbox-x.com/) and real hardware.
Full attribution detail: [THIRD-PARTY.md](./THIRD-PARTY.md).