* Make distro test matrix dynamic Resolve Ubuntu LTS and Fedora releases from endoflife.date at workflow run time so the matrix tracks in-support versions automatically, and use Debian's rolling oldstable/stable/testing tags. Switch install-step gating from image-string equality to a packageManager key on each distro entry. Drop the fedora:39 nlohmann workaround now that fedora:39 is no longer in the matrix. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Bootstrap git and read apt deps from apt-deps.txt Add a per-package-manager step that installs git first so the checkout can fetch submodules inside the container, then move checkout above the main install steps so they have repo files on disk. The apt install now sources its package list from .github/workflows/apt-deps.txt, matching generate-builds.yml. Drop the redundant echo-the-command-then-run-it lines from each install step. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move libzip deps into apt-deps.txt The libzip family (libzip-dev, zipcmp, zipmerge, ziptool) was previously appended to each workflow's apt install line because the appimage build needs libzip without crypto support. Add it to apt-deps.txt instead and have the appimage build job apt-remove libzip-dev before its from-source rebuild, mirroring the existing tinyxml2 pattern. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Drop libopengl-dev from distro test workflow libglew-dev (in apt-deps.txt) already pulls in the OpenGL headers via libgl-dev / libglvnd-dev, and generate-builds.yml has been building without libopengl-dev for a long time. The line was originally added on a guess; removing it brings the test workflow into alignment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move git, cmake, lsb-release into apt-deps.txt These are real build-time deps (git for submodule checkout, cmake for the build, lsb-release for soh CMakeLists distro detection). Moving them into apt-deps.txt makes the file a single canonical answer to what apt packages are needed to build, and reduces the distro test workflow's apt install line to just the compiler plus the file. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move apt deps file out of .github and point BUILDING.md at it Rename .github/workflows/apt-deps.txt to linux-build-deps/apt.txt so the canonical list lives at a path users can reasonably be told to look at. Update both workflows to the new path. BUILDING.md grows a "Clone the repo and enter the directory" section before Install dependencies so its apt commands can source from the file via $(cat ...) — the clone snippet is dropped from the later Build block to avoid duplication. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add cmake version verification section to BUILDING.md Older distros ship cmake too old for this project; point users at how to check their version against the project's minimum and link to a few ways to install a newer cmake (pypi, kitware apt repo, Homebrew). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add cmake verify/update step to distro test workflow ubuntu:22.04 and debian:oldstable ship cmake older than the project's minimum (3.26+), failing at configure time. Add a step before Build SoH that compares the installed cmake to the project minimum and, if too old, installs a newer cmake via pipx (per-distro pipx package). The new cmake's bin dir is added to GITHUB_PATH so the Build SoH step picks it up. Mirrors the BUILDING.md guidance pointing users at pypi cmake. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Set ccache on PATH once per job in generate-builds Replace the repeated per-step `export PATH="/usr/lib/ccache:..."` with a single "Add ccache to PATH" step in each Linux job that writes both ccache dirs to GITHUB_PATH. From there it persists for every subsequent step in that job, so each from-source build (SDL, SDL_net, tinyxml2, libzip) and the final cmake compile pick up ccache automatically. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Extract tinyxml2 from-source build into a composite action generate-builds had the same 13-line tinyxml2 install block in both the generate-soh-otr and build-linux jobs, and the distro test workflow is about to need the same logic conditionally. Move it into a composite action at .github/actions/install-tinyxml2 and have generate-builds use it. The action only builds and installs from source; removing the distro package stays in the caller since that command is package-manager specific. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add tinyxml2 troubleshooting tip to BUILDING.md Older distros ship tinyxml2 pre-10.0.0 which doesn't include the cmake config file the project's find_package call needs. Point users at either brew or the install-tinyxml2 composite action script when they hit the "Could not find a package configuration file" error. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Install latest tinyxml2 in distro test workflow when missing ubuntu:22.04 ships libtinyxml2-dev 9.0.0, which is before tinyxml2 started providing a cmake config file, so find_package(tinyxml2) fails. Add a step (apt-only) that checks for tinyxml2Config.cmake on disk and, if missing, removes the distro package and calls the install-tinyxml2 composite action to build 10.0.0 from source. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Extract SDL2_net from-source build into a composite action Same shape as the install-tinyxml2 action: generate-builds had identical SDL2_net install blocks in both the generate-soh-otr and build-linux jobs, and the distro test workflow is about to need the same logic conditionally. Move it into a composite action at .github/actions/install-sdl2-net. The cp of /usr/local/lib/libSDL* into the multiarch lib dir stays in the caller since it's appimage-specific. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Refactor cmake-config troubleshooting tip and add SDL2_net Restructure the troubleshooting tip into a generic "older distros ship packages without the cmake config files" framing with a list of known failing package versions, then two paths to install a newer version: Homebrew (with a note about CMAKE_PREFIX_PATH) or building from source using the install-* composite actions as reference. Add SDL2_net to the list alongside tinyxml2. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Install latest SDL2_net in distro test workflow when missing ubuntu:22.04 ships libsdl2-net-dev 2.0.x, which is before SDL2_net started providing a cmake config file, so find_package(SDL2_net) fails silently and the link step errors on the missing SDL2_net::SDL2_net target. Mirror the tinyxml2 pattern: an apt-only check for sdl2_net-config.cmake on disk that triggers the install-sdl2-net composite action when missing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Document minimum GCC and Clang versions Minimums pinned empirically by walking up versions on the test-gcc-10 test branch until each compiler built clean. The version numbers live in linux-build-deps/minimum-{gcc,clang}-version.txt so the workflow can read them and BUILDING.md can link to a single source of truth. Test runs that pinned the floors: - GCC 9 failure (missing <compare> header): https://github.com/briaguya0/Shipwright/actions/runs/27491491936 - GCC 10 success: https://github.com/briaguya0/Shipwright/actions/runs/27490774081 - Clang 15 failure (structured-binding-capture in lambda): https://github.com/briaguya0/Shipwright/actions/runs/27491715930 - Clang 16 success: https://github.com/briaguya0/Shipwright/actions/runs/27492790573 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add verify-compiler and install-newer-compiler actions Two composite actions that work together to keep the compiler used by the build at or above the project minimum. verify-compiler reads linux-build-deps/minimum-${compiler}-version.txt, compares against the installed default, and emits four outputs: needs_install, available_in_distro, cc, cxx, version. install-newer-compiler consumes those and installs ${compiler}-${version} either from the distro repos (apt or zypper) or via apt.llvm.org for clang on apt distros. Any combination without a known install path fails with "Minimum version not readily available. An alternative installation method for ${compiler} ${version} is needed." Validated on the test-verify-compiler branch via two dedicated test workflows that exercise every reachable matrix combination and assert expected outputs / install outcomes: - verify-compiler: https://github.com/briaguya0/Shipwright/actions/runs/27510863067 - install-newer-compiler: https://github.com/briaguya0/Shipwright/actions/runs/27512420765 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Wire verify-compiler and install-newer-compiler into distro test workflow Between the dependency-install step and the cmake verify step, run verify-compiler and (conditionally) install-newer-compiler so distros that ship a compiler below the project minimum get a newer one. Build SoH's CC/CXX env now reads the resolved binary names from verify-compiler's outputs instead of using matrix.cc/cxx directly, so a freshly-installed gcc-N/clang-N actually gets used by cmake. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add fmt/clang consteval workaround tip to BUILDING.md Point users at the fmtlib/fmt#4807 issue and the -DCMAKE_CXX_FLAGS=-DFMT_CONSTEVAL=constexpr workaround when they hit the "call to consteval function" error while building with clang. Affects distros that ship libfmt 10.x with newer clang. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Probe fmt/clang consteval compat and apply workaround when needed Self-checking version of the BUILDING.md tip: between the dep-install steps and Build SoH, try to compile the minimal repro from fmtlib/fmt#4807. If the compile fails, the build job adds -DCMAKE_CXX_FLAGS=-DFMT_CONSTEVAL=constexpr to cmake's invocation; otherwise the flag stays empty. The probe uses the resolved CXX from verify-compiler, so it works regardless of whether the compiler came from the distro or a freshly-installed newer version. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move Arch packages into linux-build-deps/pacman.txt Mirror the apt pattern: extract the Arch package list to linux-build-deps/pacman.txt (everything except the compiler) and point both BUILDING.md and the distro test workflow at it. Also brings the workflow's Arch install up to parity with BUILDING.md — it was missing opusfile and libvorbis. Add linux-build-deps/README.md explaining that apt.txt is verified on every push but the other per-distro lists can drift, and inviting PRs / issues / Discord messages when something's missing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move Fedora packages into linux-build-deps/dnf.txt Mirror the apt/pacman pattern: extract the Fedora package list to linux-build-deps/dnf.txt (everything except the compiler and the gcc-c++ companion package) and point both BUILDING.md and the distro test workflow at it. Brings the workflow's Fedora install up to parity with BUILDING.md — it was missing SDL2_net-devel, nlohmann-json-devel, opusfile-devel, and libvorbis-devel. Also drops the leftover wget that was only needed for an old from-source workaround. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move openSUSE packages into linux-build-deps/zypper.txt Mirror the apt/pacman/dnf pattern: extract the openSUSE package list to linux-build-deps/zypper.txt and point the distro test workflow at it. The list adds SDL2_net-devel and the audio family (libogg-devel, libvorbis-devel, libopus-devel, opusfile-devel) that the workflow's inline list was missing — package names verified in a Tumbleweed distrobox. Also adds a new openSUSE section to BUILDING.md (it wasn't there before), with libstdc++-devel in the clang line because clang on openSUSE doesn't pull in libstdc++ headers transitively the way it does on other distros. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add in-support openSUSE Leap releases to the distro test matrix Fetch openSUSE cycles from endoflife.date and include any with an EOL date in the future as opensuse/leap:${cycle} images alongside the rolling Tumbleweed entry. Today that's just Leap 16.0 (15.6 went EOL 2026-04-30); new Leap releases will appear automatically as they ship and old ones drop off as they EOL. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move Nix flake into linux-build-deps/flake.nix Extract the flake.nix content out of BUILDING.md's inline code block into linux-build-deps/flake.nix and update the Nix section to point at it. Users run `nix develop ./linux-build-deps` from the repo root instead of copying the flake out to a file they have to maintain themselves. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Add build-nix job to distro test workflow Verifies that `nix develop ./linux-build-deps` produces a shell that builds SoH. Runs on its own (no distro matrix) and uses cachix/install-nix-action per nix.dev's CI guidance. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Drop LINUX_RUNNER override from distro test workflow vars.LINUX_RUNNER isn't referenced anywhere else in .github/ — the override is dead code. Replace all three call sites with plain ubuntu-latest. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Website
Official Website: https://www.shipofharkinian.com/
Discord
Official Discord: https://discord.com/invite/shipofharkinian
If you're having any trouble after reading through this README, feel free to ask for help in the Support text channels. Please keep in mind that we do not condone piracy.
Quick Start
The Ship does not include any copyrighted assets. You are required to provide a supported copy of the game.
1. Verify your ROM dump
You can verify you have dumped a supported copy of the game by using the compatibility checker at https://ship.equipment/. If you'd prefer to manually validate your ROM dump, you can cross-reference its sha1 hash with the hashes here.
2. Download The Ship of Harkinian from Releases
3. Launch the Game!
Windows
- Extract the zip
- Launch
soh.exe
Linux
- Place your supported copy of the game in the same folder as the appimage.
- Execute
soh.appimage. You may have tochmod +xthe appimage via terminal.
macOS
- Run
soh.app. When prompted, select your supported copy of the game. - You should see a notification saying
Processing OTR, then, once the process is complete, you should get a notification sayingOTR Successfully Generated, then the game should start.
Nintendo Switch
- Run one of the PC releases to generate an
oot.o2rand/oroot-mq.o2rfile. After launching the game on PC, you will be able to find these files in the same directory assoh.exeorsoh.appimage. On macOS, these files can be found in/Users/<username>/Library/Application Support/com.shipofharkinian.soh/ - Copy the files to your sd card
sdcard
└── switch
└── soh
├── oot-mq.o2r
├── oot.o2r
├── soh.nro
└── soh.o2r
- Launch via Atmosphere's
Game+Rlauncher method.
4. Play!
Congratulations, you are now sailing with the Ship of Harkinian! Have fun!
Configuration
Default keyboard configuration
| N64 | A | B | Z | Start | Analog stick | C buttons | D-Pad |
|---|---|---|---|---|---|---|---|
| Keyboard | X | C | Z | Space | WASD | Arrow keys | TFGH |
Other shortcuts
| Keys | Action |
|---|---|
| ESC | Toggle menu |
| F2 | Toggle capture mouse input |
| F5 | Save state |
| F6 | Change state |
| F7 | Load state |
| F9 | Toggle Text-to-Speech (Windows and Mac only) |
| F11 | Fullscreen |
| Tab | Toggle Alternate assets |
| Ctrl+R | Reset |
Project Overview
Ship of Harkinian (SOH) is built atop a custom library dubbed libultraship (LUS). Back in the N64 days, there was an SDK distributed to developers named libultra; LUS is designed to mimic the functionality of libultra on modern hardware. In addition, we are dependant on the source code provided by the OOT decompilation project.
In order for the game to function, you will require a legally acquired ROM for Ocarina of Time. Click here to check the compatibility of your specific rom. Any copyrighted assets are extracted from the ROM and reformatted as a .o2r archive file which the code uses.
Graphics Backends
Currently, there are three rendering APIs supported: DirectX11 (Windows), OpenGL (all platforms), and Metal (MacOS). You can change which API to use in the Settings menu of the menubar, which requires a restart. If you're having an issue with crashing, you can change the API in the shipofharkinian.json file by finding the line gfxbackend:"" and changing the value to sdl for OpenGL. DirectX 11 is the default on Windows.
Custom Assets
Custom assets are packed in .otr archive files. To use custom assets, place them in the mods folder.
If you're interested in creating and/or packing your own custom asset .otr files, check out the following tools:
Development
Building
If you want to manually compile SoH, please consult the building instructions.
Playtesting
If you want to playtest a continuous integration build, you can find them at the links below. Keep in mind that these are for playtesting only, and you will likely encounter bugs and possibly crashes.
Further Reading
More detailed documentation can be found in the 'docs' directory, including the aforementioned building instructions.

