feat: native macOS port (Apple Silicon ARM64, SDL_GPU Metal presenter, SDL3 audio/input)

Merged
TM
23d ago
+5204-90

Summary

Adds a native macOS build (Apple Silicon and Intel) beside the Windows build.
The Windows runner, presenter and audio paths are unchanged except where noted.

Runtime

  • win32_compat.c/.h: the Win32 file API subset kernel_file.c and the save
    transaction use (CreateFile, ReadFile, FindFirstFile, LockFile, attributes),
    implemented on POSIX.
  • Fibers: fiber_adapter.c creates and switches native fibers with small
    AArch64 and x86-64 assembly switches (fiber_switch_*.S).
  • -ffp-contract=off off MSVC, so AArch64 does not fuse multiply-adds and guest
    float results match the x86 hosts.
  • SHA through CommonCrypto; SSE helpers have portable fallbacks off x86.
  • Custom soundtracks need Media Foundation, so macOS has an empty catalog
    (documented as open work).

Presenter, audio, input

  • d3d_presenter_sdl_gpu.cpp: SDL_GPU on Metal with generated MSL, resolution
    scale, MSAA and letterboxing. SMAA is not available here.
  • audio_output_sdl.cpp: one SDL audio stream per guest voice, mirroring the
    XAudio2 backend's cushion and rate trim.
  • input_host_win32.c now reads the keyboard through SDL off Windows; the
    gamepad path was already SDL.

Shared change that affects Windows

  • dsound_service_adapter.c: guest-facing buffer calls copy the ring up to the
    cursor the guest is about to read (pump_buffer(..., force)), instead of
    waiting for the 10 ms batch, so the streaming decoder cannot overwrite
    unplayed audio. This runs on Windows too and has not been play-tested there.

Tools

  • build_game.sh, run_game.sh, launcher.sh (AppKit launcher compiled on
    first use) and macOS steps in docs/building.md. extract-xiso is built
    from source; Homebrew does not package it.
  • build_game.py builds with Ninja off Windows and rewrites the generated
    sources to CRLF, the bytes the recipe authenticates.
  • A runtime-tests-macos CI job.

Verification

  • ctest: 22 public tests pass on macOS arm64 (custom music skipped).
  • python -m unittest discover -s tools -p "test_*.py" and
    python tools/public_export.py verify --require-public-tree pass.
  • An authenticated local build on an M-series Mac starts, presents frames and
    reaches the save-select and options menus at 60 fps. This is a smoke run, not
    a play test.
  • Not yet run: Windows CI for this branch, and a Windows play test of the
    DirectSound change.