Ultima Underworld:
Ultima Underworld II:
Ultima Underworld: The Stygian Abyss (1992) and Ultima Underworld II: Labyrinth of Worlds (1993), dug out of their original DOS programs. For each game there is:
- Source code that rebuilds the original program byte for byte. It compiles with the original Borland tools to exactly the shipped
UW.EXEandUW2.EXE, with readable names, shared headers and notes on what each file does in the game. - A native port for macOS, Windows and Linux, built from that same source. It plays the game in a window with its music, speech and effects, and is checked against recorded DOS sessions for every change.
- A web build of the same port, compiled to WebAssembly: play both games in your browser, with nothing to install.
Arrived from a UW2Decomp or UW1Decomp link? Those repositories are now uw2/ and uw1/ here, with their full history; an old link to a shared page (this README, docs/BUILDING.md) lands on the page for both games, which links to each game's own.
This repository holds no game data: the native ports need your own copy of each game (the GOG releases). The web page is a separate site, with its own copy. This is a fan research project, not affiliated with or endorsed by the rights holders; see Licence and legal notice.
![]() |
![]() |
![]() |
![]() |
Screens from the recorded DOS sessions the ports are tested against: the ports draw exactly the same.
- Download the package for your system from Releases. Each release is for one game:
uw1-v…releases are Ultima Underworld,uw2-v…releases are Ultima Underworld II. - Install the game from GOG. The port finds it in GOG's usual folders, including inside GOG's Mac app and its
game.gogCD image, and on Windows where GOG's installer recorded it. If it finds nothing, it asks you to choose the folder, and remembers it. From a command line,--data /path/to/gamenames it. - Start it:
- macOS:
UW1.apporUW2.app. Both are signed and notarised, so they open like any app. - Windows:
uw1port.exeoruw2port.exe. - Linux: the AppImage (
chmod +x, then run it), or./uw1or./uw2from the tarball. The Linux packages need glibc 2.35 or later (Ubuntu 22.04 or newer).
- macOS:
The controls are the game's own: the mouse, and the keys in the game's manual. F11 (Cmd+, on macOS) opens the port's settings screen (SETTINGS.md). The game's cursor follows the system pointer; --mouse lock captures the pointer on a click instead, as DOSBox does, with Ctrl+F10 to release it. With the mouse-look enhancement on, the ` key turns the view by the mouse instead (ENHANCEMENTS.md). With modern-keys, the keys are UltimaHacks' layout (WASD, Space to attack, Q and E to look and use).
Saved games and settings are kept in the port's home folder, never in the game folder: ~/.uw1port or ~/.uw2port on macOS and Linux, %APPDATA%\uw1port or %APPDATA%\uw2port on Windows.
The first run gets a Sound Blaster with FM music and digitised speech or effects. For Roland MT-32 music you need your own MT-32 or CM-32L ROM images. The port finds them by itself in a roms folder in its home folder, beside the game or the program, or where DOSBox keeps MT-32 ROMs; or give --mt32-roms a folder or one of the files. Any file names work, whole images or their halves: the ROMs are recognised by their contents. On a Mac, drop the ROM folder onto the app. --sound picks other cards, and --help lists every option. Each game's README has its sound card numbers: uw1, uw2.
If something goes wrong, open an issue. Each play session's inputs are recorded to recordings/ in the home folder (the newest five are kept). Zip the newest folder with the log beside it and attach it: it replays your session here exactly.
Optional enhancements, all off by default, are listed in docs/ENHANCEMENTS.md.
https://abedegno.github.io/underworld-exhumed-web/ plays both games in a desktop browser, with MT-32 music. Choose a game; the page reloads itself once on a first visit to turn on the threads the port needs. The settings screen opens with the gear at the page's corner (or F11), the button beside it goes full screen, and with the mouse locked Esc frees the pointer. Saved games and settings are kept in the browser's storage, which is not as safe as a desktop's saves. docs/WEB.md has the details and how the page is built and checked.
Both games are byte-matching decompilations, made and checked with Exhume, the toolkit both were built with. It is included here as a submodule.
git clone --recursive https://github.com/abedegno/underworld-exhumed.git
cd underworld-exhumed
make setup # Exhume and its tools, then each game's setup
make port # both ports, no Borland toolchain or game data needed
make check # each game's gate: every source rebuilds its part of the EXE exactly
make test # the gate, the ports, routine fuzzing and every recorded session against DOSGAME=uw1 or GAME=uw2 runs a target for one game. docs/BUILDING.md has the requirements, the private CI bundles and releases.
What there is to read:
| docs/behaviour/ | what the games do, as rules with exact numbers, for game programmers and modders who don't read the C: NPC AI, schedules, combat, magic, conversations, player upkeep, traps and triggers |
| docs/FORMATS.md | every data file both games read and write, from the code that reads it |
| docs/UW1-UW2-DIFFERENCES.md | where the two games' rules differ, routine by routine |
| docs/PORT.md | how the ports work and how they are proved against DOS |
| docs/WEB.md | the web build: building it, checking it and deploying the page |
| uw1/docs/FINDINGS.md, uw2/docs/FINDINGS.md | what matching revealed: likely bugs in the originals, game rules, engine details |
uw1/vectors/, uw2/vectors/ |
input and output tables for game rules, made by running the original programs' code |
uw1/map/crosswalk.tsv, uw2/map/crosswalk.tsv |
every function by the IDA listing's name and address, with its matched source (tools/crosswalk.py --find) |
Each game folder (uw1/, uw2/) is self-contained: its sources, maps, tests and tools, its own make, and its own README with the details. CONTRIBUTING.md says how a change is proved.
Ultima Underworld (uw1/) |
Ultima Underworld II (uw2/) |
|
|---|---|---|
| Code matched | every code segment: 89 C files and 50 assembly modules | every code segment: 99 C files and the assembly |
| Exact link | identical to UW.EXE |
identical to UW2.EXE |
| Port | eleven recorded sessions identical to DOS on macOS, Linux and Windows, and the web build's under Node.js | twelve recorded sessions identical to DOS on macOS, Linux and Windows, and the web build's under Node.js |
| Releases | 1.0.1, published | 1.2.2, published |
| In a browser | the web page | the web page |
The project's own work (the tools, the documentation, and the comments, names and structure added to the decompiled code) is under the MIT licence (LICENSE). The decompiled code is derived from the games' programs, whose copyright belongs to their owners; the licence grants no right in it. NOTICE has the details, and each game's NOTICE and THIRD-PARTY-NOTICES the rest.



