Native macOS (Apple Silicon / arm64) build#4984
Conversation
Prep the shared source for non-DOS/Windows targets (native Linux and macOS/Apple Silicon). No behavioural change on the existing builds — every struct touched here is an in-memory-only runtime table, never serialized, and its layout is unchanged on the 32-bit build (the members were already at natural offsets). - globals.h: include <unistd.h>/<signal.h> on __unix__ and __APPLE__ too, not only the legacy `unix` macro, so the POSIX paths compile off-DOS. - Stop byte-packing in-memory-only structs (GUI buttons/menus, config NamedCommand/NamedField/ConfigFileData, GamekeySettings, DemoItem, and the TbLoadFiles/TbLoadFilesV2 loader lists). These hold pointers and function pointers; on arm64 a statically-initialized #pragma pack(1) struct places those pointers at unaligned offsets the linker rejects. Unpacking them lets the modern macOS linker emit them with no deprecated -ld_classic workaround. The serialized NetMessage struct and size-critical game-state structs stay packed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce a native Apple Silicon build path that compiles KeeperFX to a Mach-O arm64 binary, no emulation. Verified end-to-end: it launches, loads original DK data, and runs into live gameplay. - macos.mk: clang build using Homebrew libs via pkg-config (sdl2 + image/mixer/net, ffmpeg, luajit, openal-soft, libspng, minizip, miniupnpc, libnatpmp, zlib, curl). Uses -arch arm64 with no deprecated linker flags: the in-memory packed-pointer tables are unpacked in the shared headers, so the modern linker aligns every static pointer itself. - tools/build_macos_deps.sh: builds astronomy/centijson/enet6 from source as arm64 static libs into deps/ where macos.mk expects them. - .gitignore: exclude /game (a staged runtime install of copyrighted DK assets, which must never be committed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7d31c93 to
7427294
Compare
|
@matthewdeaves nice just tried it, this is very cool! The game starts up and I'm able to go into the first level but otherwise does not work: the imps are just stuck and don't do anything. Some game sounds also don't seem to be working. But this is very promising! |
|
Quick testing update: I've now played through level 1 and then level 2 of the original campaign back-to-back without issue on my MacBook Air (M5, macOS 26). |
@EugenHotaj what type of Mac and version of MacOS are you running it on? I've just been playing perfectly on my MacBook Air through level 1 and 2. What is the source of the base original game files on your setup? I got mine from the DK1 gold edition on gog.com (bought today for ~99p) |
|
For reference, here's exactly what my working setup looks like on the MacBook Air (M5, macOS 26) — KeeperFX 1.4.0.5204 ( Folder layout ( Key original-game files and sizes (these must come from your original DK1 data — this is the usual culprit):
If your What's the source of your original files, and what size is your |
dc2b9e1 to
e87d614
Compare
…check Bring the fork's release packager in line with PR #4984's macos-arm64-build branch. dylibbundler only follows LC_LOAD_DYLIB entries, so libraries loaded at runtime via dlopen()/SDL_LoadObject are invisible to it and must be added by hand. Generalise the SDL3 special-case into an add_runtime_dylib() helper that copies each such library in, rewrites its install id, and re-runs dylibbundler over it (with --overwrite-files, so it does not erase Contents/libs) to pull in its own transitive deps. Also mirror SDL2_mixer's decoder set defensively, and verify every Mach-O in the bundle is free of /opt/homebrew and /usr/local paths. This keeps the published KeeperFX.app.zip download built with the same packager the PR ships. Verified on an M5 with Homebrew hidden from the loader: zero libs resolved from /opt/homebrew and no loader errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Package bin/keeperfx into a redistributable dist/KeeperFX.app that runs on any
Apple Silicon Mac with no Homebrew, and behaves like a normal Mac app:
- Self-locating engine (src/linux.cpp): the engine is the bundle's main
executable. Finder launches apps with cwd=/, so on startup it resolves its
own path and, when running from inside a *.app/Contents/MacOS/, chdir's to the
folder containing the .app — where the user dropped the game data. Launched
outside a bundle (a dev running bin/keeperfx) the working directory is left
alone. This replaces an earlier bash-wrapper launcher.
- tools/make_macos_app.sh bundles all non-system dylibs via dylibbundler, with
load paths rewritten to @executable_path/../libs. dylibbundler only follows
LC_LOAD_DYLIB entries (what `otool -L` shows), so libraries pulled in at
runtime via dlopen()/SDL_LoadObject are invisible to it and are added by an
add_runtime_dylib() helper that copies each one in, rewrites its install id,
and re-runs dylibbundler over it (with --overwrite-files, so it does not erase
Contents/libs) to pull in that library's own transitive deps. This covers:
* libSDL3 — Homebrew's sdl2 is sdl2-compat, which dlopens SDL3 at runtime;
the shim's first search path is @loader_path/libSDL3.dylib (unversioned).
* SDL2_mixer's audio decoders (vorbis/FLAC/mpg123/opus/wavpack/...), mirrored
defensively under both versioned and unversioned names.
A final pass then verifies every Mach-O in the bundle (main binary + all
bundled dylibs) is free of /opt/homebrew and /usr/local references.
- Info.plist declares NSDesktop/Documents/DownloadsFolderUsageDescription and
the whole bundle is ad-hoc signed, so macOS shows a normal permission prompt
the first time it reads data from a privacy-protected folder (Desktop/
Documents/Downloads). Click Allow once and it runs from any location.
- Generates the .icns from res/ PNGs via iconutil.
.gitignore: exclude /dist (build output of the bundler).
Verified on an M5 MacBook Air with Homebrew hidden from the loader
(DYLD_FALLBACK_LIBRARY_PATH=/nonexistent): the app launches, loads zero
libraries from /opt/homebrew, and resolves every dependency — including the
dlopen'd libSDL3 and all music decoders — from inside the bundle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- docs/MACOS_ARM64_PORT.md: the full port write-up — the header edits arm64 required, the Homebrew prerequisites, build/run/package steps, the self-locating .app, the sdl2-compat/SDL3 runtime gotcha, and the folder-access permission prompt. Notes where to source the original DK data. - README.md: add a "macOS (Apple Silicon)" section (build, package, required original-DK files, drop-in layout, first-launch permission prompt) and list the native Linux/macOS arm64 builds under Features. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds KeeperFX on the GitHub-hosted Apple Silicon runner (macos-15): installs the Homebrew deps, builds the source-only deps via tools/build_macos_deps.sh (cached, keyed on that script's hash), runs `make -f macos.mk`, asserts the output is a native arm64 Mach-O, then packages the self-contained KeeperFX.app, checks no Homebrew path leaked into it, and verifies the bundle signature. Uploads both the bare binary and the .app. Unlike the PR-only Test Prototypes workflow, this also runs on push to master (path-filtered to macOS-relevant files) so the mainline macOS build stays validated. Also available via workflow_dispatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e87d614 to
13a4a5f
Compare
Packaging hardened + download rebuilt and verifiedI've tidied this branch and made the
Verified end-to-end on an M5 MacBook Air. I downloaded the published env DYLD_FALLBACK_LIBRARY_PATH=/nonexistent \
KeeperFX.app/Contents/MacOS/keeperfxResult: 0 libraries loaded from The one-click download reflects this exact packager: On the "some sounds don't work" report@EugenHotaj — while chasing this I found that on a Homebrew build, SDL2_mixer's decoders are actually link-time dependencies (they show in
If you still get no sound from the release build, running it once from Terminal and pasting the output would show whether any decoder fails to load: KeeperFX.app/Contents/MacOS/keeperfx 2>&1 | tee /tmp/kfx.log |
|
@matthewdeaves same issue with the latest download unfortunately. I'm trying it directly on my keeperfx folder which works with wine, so I think the folder structures / files should be good. I have an M2 MacBook Air and also got the game from GoG (a few years ago). One thing I can think of is that I'm using an old version of keeperfx (1.1) and your version seems to be 1.4, so there could be some conflict. I will try doing a "clean install" of a 1.4.x keeperfx and try again |
|
Ok that was indeed the issue :) works beautifully |
|
I'm having the same issue with certain sounds not working in-game. I have done a fresh/clean installation of KeeperFX 1.4 using the Windows download and Wine. Let this copy the DK files from my original GOG download of DK-Gold, verified this all works by running it with Wine, including all sounds. Then downloaded your latest release (from 19 hours ago at the time of writing this) and placed the app file into the KeeperFX folder. It runs fine but some sounds don't work such as the "shovel" noise when marking a square for digging or the "put down" noise when laying down some lair etc... Noises such as the imps actually digging the marked squares does work. I ran from the terminal as you suggested but the resulting log file is empty. I also ran it from the terminal without piping to tee and still no output at all. I am using a Macbook Air M1 from 2020 with Macos 26.5.1 |
|
I'll have a think about what I could do to investigate from this end. maybe a bit of a report tool or something that outputs info about your DK1 install that you can review and post.... might help me pinpoint whats going on. Be a few days till I'm back in action though, thanks for trying it! |
|
macOS sound troubleshooting — for anyone hitting "some in-game sounds don't play" (@hayesey, @EugenHotaj), the latest build now bundles a small read-only sound diagnostic so we can pin down what's happening on your machine. Grab the current download: https://github.com/matthewdeaves/keeperfx/releases/tag/macos-latest With It checks your It is read-only — it never changes your game — and the report contains no personal info (paths are shown as |
|
@matthewdeaves not sure if it helps, but I also tried to port the game to apple silicon a few days ago and fixed some unaligned reads along the way. A segmentation fault occurred after loading a savegame (fixed in my branch). I kind of abandoned the project because after posting on Discord the team didn't like the usage of LLMs / quality of the code, but perhaps it might help you with this: |
|
Here is my soundcheck file. I actually hear all the sounds played when running this! But still not in the game itself. I also cloned your repo and built it from scratch myself but still have the same issue. You'll see that I am using a bluetooth headset for audio but I have also tried it using the macbook built in speakers. |
@moretti thats fair enough, it’s their repo/work and AI and its impact on the world/life is a hot topic. I just wanted to play dungeon keeper again on my mac. Think I’ll just close this PR and do what I can on my own fork. |
|
@matthewdeaves are you going to continue your fork? I see it has been deleted or made private. I would be keen to keep helping you test it and get it to a stage where hopefully it can be merged into the main repo, I've been wanting a native mac version for a long time! |
|
this one is cleaner compared to the other one I'd say but those are tough ones to get an l don't think an llm could do cleanly, if you for the packing stuff if you could split that off and remove the comments as to why packing isn't needed there that would get merged |
Fwiw everything works perfectly for me now that I updated to KeeperFX 1.4. I no longer have any sound issues and actually the narrator's voice on the world map is much better than with Wine (sounds a bit choppy and cuts out). Re the AI stuff -- I get not wanting to land ai slop but would love to have a working native Mac version, even if it's just in "alpha" mode or some feature branch. At least until it can be landed properly. |
Adds a native Apple Silicon (arm64) build of KeeperFX — a real Mach-O binary built with clang and Homebrew libraries, no Wine or emulation.
I saw #4552 is already working toward macOS support (thanks @PieterVdc). This is an independent take that I've been able to build and test on real hardware through to gameplay, so hopefully it's useful — either on its own or to compare against. Happy to adjust or combine.
📥 Try it (Apple Silicon)
A ready-to-run, self-contained
KeeperFX.appis published here — no build needed:https://github.com/matthewdeaves/keeperfx/releases/tag/macos-latest
Download and unzip it, drop
KeeperFX.appnext to your existing KeeperFX game data, and double-click. On first launch, right-click → Open if Gatekeeper prompts, and click Allow if macOS asks for folder access. (For anyone on #4552 who wanted to test on a real Mac — @EugenHotaj, @AI-Guru — this should make it a one-click try.)What's included
#pragma pack(1)structs (they hold pointers, and arm64's linker rejects unaligned statically-initialized pointers) and widen one unix include guard. No behaviour change on Windows — the struct layouts are unchanged on the 32-bit build, and the serialized / size-critical structs stay packed.macos.mk+tools/build_macos_deps.shto build the engine and its three source-only deps (plain-arch arm64, no deprecated linker flags).tools/make_macos_app.sh— an optional packager that produces a self-containedKeeperFX.app. It bundles all non-system dylibs — including the ones loaded at runtime viadlopen, whichotool -Lcan't see: the SDL3 that Homebrew's sdl2-compat loads, and the SDL2_mixer audio decoders — and then verifies every Mach-O in the bundle is free of/opt/homebrewand/usr/localpaths. The engine self-locates its data next to the.app, and the bundle is ad-hoc signed with folder-access usage strings, so macOS shows a normal permission prompt on first launch and it runs from any location (Desktop included).docs/MACOS_ARM64_PORT.md) and a README section.macos-15Apple Silicon runner.How I tested it
Built and played on a MacBook Air (M5, macOS 26): main menu → land view → into level 1 of the original campaign. The original game data came from the Dungeon Keeper Gold release (its macOS app ships the
DATA/SOUNDfiles uncompressed). CI is green.Notes / limitations
.appis ad-hoc signed, not notarized — first launch may need right-click → Open.Disclosure: I put this together with the help of Claude Code, building and testing each step locally and in CI.