build(macos): native Apple Silicon arm64 build#216
Open
blackden wants to merge 6 commits into
Open
Conversation
Three minimal patches to get RealRTCW building and launching natively on macOS Tahoe (clang 21 / Apple Silicon): * Makefile: add ffmpeg pkg-config to the darwin section. Upstream only wires libavcodec/libavformat/libavutil/libswscale/libswresample into the Linux block; on macOS cl_cin.c failed with "libavcodec/avcodec.h file not found" because pkg-config silently returned empty. * Makefile: replace the darwin SDL block. The bundled code/libs/macosx/libSDL2-2.0.0.dylib is stale (predates the upstream SDL3 API migration), and -framework SDL2 fails to resolve SDL_PutAudioStreamData, SDL_UpdateGamepads, SDL_SetAudioStreamGain, etc. Now linked against Homebrew SDL3 via pkg-config (sdl3). * code/game/g_main.c: fix include path "../../steam/steam.h" -> "../steam/steam.h". All other 8 files in code/game/ already use the correct relative path; this was a stray typo that happened to compile under upstream's Windows toolchain but not on a stricter macOS clang. Recipe (macOS arm64): brew install pkgconf sdl3 ffmpeg opusfile freetype jpeg opus libogg libvorbis make ARCH=arm64 USE_INTERNAL_LIBS=0 Outputs RealRTCW.arm64 plus arm64 .dylib game modules. Vendored zlib 1.2.11 and freetype 2.9 break under clang 21 C23 strictness, so they are bypassed via USE_INTERNAL_LIBS=0 in favour of system zlib and Homebrew freetype - matching the MacSourcePorts/iortcw approach. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HOWTO-Build-macOS.md walks through the full path from a stock macOS 26.x / Apple Silicon machine to a running native arm64 binary: brew install pkgconf sdl3 ffmpeg freetype jpeg opus opusfile libogg libvorbis make ARCH=arm64 USE_INTERNAL_LIBS=0 ./build/release-darwin-arm64-nosteam/RealRTCW.arm64 It also documents what the three macOS patches (ffmpeg pkg-config, SDL3 link, g_main.c include typo) are actually doing, so the diff is intelligible to anyone reviewing or upstreaming it. scripts/mac/install-rtcw-steamcmd.sh fetches Return to Castle Wolfenstein (AppID 9010) via steamcmd against the Windows depot, since RTCW ships no macOS depot on Steam, then symlinks its .pk3 files into ~/Library/Application Support/RealRTCW/main (the homepath that FS_Startup checks first). Pair it with a regular 'steamcmd +app_update 1379630' for the RealRTCW mod data itself. Tested end-to-end on macOS 26.5.1 / M-series: SDL3 + Cocoa, Apple GL 2.1-on-Metal renderer, RealRTCW 5.4 main menu and gameplay. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CG_CheckAmmo iterated weapon enums up to WP_NUM_WEAPONS (57 in RealRTCW) but tested membership with `weapons[0] & (1 << i)`. Two independent bugs in that one line: * For i >= 31, `1 << i` is signed-int undefined behaviour on a 32-bit int (and trapped by UBSan). * Even where the shift accidentally produces the "right" value, weapons[0] only holds slots 0-31. Slots 32+ live in weapons[1], so every weapon with enum >= 32 (WP_VENOM and onwards: MP44, FG42, BAR, M97, Auto-5, Browning, MG42M, all explosives, all alt-fire variants, monster attacks) was silently excluded from the low-ammo warning check. Replaced with the engine-provided helper COM_BitCheck (q_shared.c:188), which already handles the multi-int bitfield correctly and is used at dozens of other call sites in cgame/, game/, ai_cast/ etc. for the same weapons[] array. Found by cppcheck (shiftTooManyBits + integerOverflow at cg_playerstate.c:62).
…stack pointer
Item_Text_Paint() reads ui_savegameInfo cvar into a local stack buffer
and writes its address into item->text:
char infostring[SAVE_INFOSTRING_LENGTH];
...
item->text = &infostring[0];
The itemDef_t lives in the long-lived UI string arena, but infostring
dies the moment Item_Text_Paint returns. From that point until the next
frame's call rewrites item->text, the pointer is dangling. The very
next paint frame's Item_SetTextExtents() reads item->text before the
textSavegameInfo branch re-runs, so the read of indeterminate stack
contents happens every frame the savegame menu stays open.
Stash the original "savegameinfo" literal in a local on entry, replace
the four early returns (WINDOW_WRAPPED, WINDOW_AUTOWRAPPED, text+cvar
both NULL, empty textPtr) plus the natural fall-through with a single
goto cleanup that puts item->text back. savedTextValid guards against
mutating item->text when the savegame branch never fired.
Found by cppcheck (autoVariables at ui_shared.c:3453).
scripts/mac/playtest.sh wraps the arm64 release binary and:
* redirects stdin to /dev/null so the Q3 engine's built-in TTY
console doesn't steal keyboard input from the SDL window,
* sets ASAN_OPTIONS / UBSAN_OPTIONS log paths under /tmp,
* auto-detects sanitizer builds via otool -L,
* prints the A1-A4 manual-verification checklist on exit.
Local dev convenience only; not wired into the build.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The darwin block hardcoded -I/System/Library/Frameworks/OpenAL.framework/Headers, but Apple removed those headers from the macOS SDK years ago, so USE_OPENAL=1 hasn't built on any modern Xcode toolchain. Replace the dead framework include with the pkg-config-populated \$(OPENAL_CFLAGS). Brew's openal-soft is keg-only (it conflicts with Apple's frozen OpenAL.framework dylib in /System), so naked pkg-config can't find openal.pc; the new darwin-block probe asks brew for the prefix and re-runs pkg-config with the keg's pkgconfig dir on PKG_CONFIG_PATH. End result: \`make USE_OPENAL=1\` Just Works on any standard Homebrew-equipped Mac with \`brew install openal-soft pkgconf\`. The existing -framework OpenAL fallback under USE_INTERNAL_LIBS=1 is preserved (it links the dylib, but headers still come from brew via \$(OPENAL_CFLAGS) since the SDK headers are gone). Tested: make ARCH=arm64 USE_INTERNAL_LIBS=0 USE_OPENAL=1 -j8 on macOS 26 / Apple M1 with brew openal-soft 1.25.2, no env override. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three minimal patches that let RealRTCW build and run natively on
Apple Silicon (M-series, macOS 11+). The bundled
make-macosx-ub.shin this repo still targets gcc-4.0 and the 10.5/10.6 SDKs, so there
was no working path from a current macOS install — this PR provides
one with
make ARCH=arm64 USE_INTERNAL_LIBS=0.Makefile: addffmpegviapkg-configto thedarwinblock.Upstream only wires
libavcodec/libavformat/libavutil/libswscale/ libswresampleflags into the Linux block, so on macOScl_cin.cfails with
'libavcodec/avcodec.h' file not foundeven with ffmpeginstalled.
Makefile: replace the darwin SDL block. The bundledcode/libs/macosx/libSDL2-2.0.0.dylibpredates the SDL3 APImigration, so
-framework SDL2no longer resolves symbols likeSDL_PutAudioStreamData,SDL_UpdateGamepads,SDL_SetAudioStreamGain. Now linked against Homebrew SDL3 viapkg-config sdl3.code/game/g_main.c: fix#include "../../steam/steam.h"→"../steam/steam.h". Every other file incode/game/alreadyuses the correct relative path; this is a stray typo that happens
to compile under the Windows toolchain but not on a stricter
clang.
Also adds
HOWTO-Build-macOS.mdwith the full recipe andscripts/mac/install-rtcw-steamcmd.sh, a small helper that usessteamcmd(Windows depot) to fetch the RTCW data files and symlinkthem into the engine's homepath. No engine, renderer, or platform
code is touched.
USE_INTERNAL_LIBS=0is load-bearing becauseclang 21(the stockCLT compiler on recent macOS releases) rejects the vendored
zlib-1.2.11andfreetype-2.9under C23 strictness. This matchesthe approach the MacSourcePorts
iortcwfork already uses for theshipped
iowolfsp.app.Test plan
brew install pkgconf sdl3 ffmpeg freetype jpeg opus opusfile libogg libvorbismake ARCH=arm64 USE_INTERNAL_LIBS=0succeeds on macOS 26.5.1 / Apple SiliconRealRTCW.arm64isMach-O arm64;qagame/cgame/ui.sp.arm64.dyliblikewisepak0.pk3,sp_pak1..4.pk3, and the RealRTCW mod paksrealrtcwdefault.cfgis read; noSys_ErrorNot yet covered (would be a follow-up): Universal 2 (Intel slice),
renderer2build on macOS, code signing / notarisation,.dmg-bundled release.🤖 Generated with Claude Code