Implement macOS (Intel) support

This commit is contained in:
Julio C. Rocha committed 2026-09-17 00:35:30 -07:00
1 parent 768ac8d837
commit 62b2105458
16 files changed
+376 -35

No files matched your search

+114 -9
View File
@@ -25,11 +25,26 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
# name as an inherited one — CCTVRecord's `pad` — see docs/internals.md.)
# An explicit -DCMAKE_C_COMPILER=... always wins.
if(NOT DEFINED CMAKE_C_COMPILER)
find_program(_GE007_GCC NAMES gcc)
if(CMAKE_HOST_APPLE)
# Homebrew installs real GNU GCC with a version suffix; /usr/bin/gcc is
# Apple Clang and does not support the Plan 9 struct extensions required
# by the decomp's `inherits` pattern.
find_program(_GE007_GCC
NAMES gcc-16 gcc-15 gcc-14 gcc-13 gcc-12 gcc-11 gcc)
else()
find_program(_GE007_GCC NAMES gcc)
endif()
if(_GE007_GCC)
set(CMAKE_C_COMPILER "${_GE007_GCC}" CACHE FILEPATH "C compiler" FORCE)
get_filename_component(_GE007_GCC_DIR "${_GE007_GCC}" DIRECTORY)
find_program(_GE007_GXX NAMES g++ PATHS "${_GE007_GCC_DIR}" NO_DEFAULT_PATH)
if(CMAKE_HOST_APPLE)
find_program(_GE007_GXX
NAMES g++-16 g++-15 g++-14 g++-13 g++-12 g++-11 g++
PATHS "${_GE007_GCC_DIR}" NO_DEFAULT_PATH)
else()
find_program(_GE007_GXX NAMES g++
PATHS "${_GE007_GCC_DIR}" NO_DEFAULT_PATH)
endif()
if(_GE007_GXX)
set(CMAKE_CXX_COMPILER "${_GE007_GXX}" CACHE FILEPATH "CXX compiler" FORCE)
endif()
@@ -163,7 +178,18 @@ configure_file(
# the toolchain include dir from the compiler location (MinGW/MSYS2 layout:
# bin/../include) and generate an absolute-path include.
get_filename_component(_PORT_GCC_DIR "${CMAKE_C_COMPILER}" DIRECTORY)
get_filename_component(PORT_HOST_STRING_H "${_PORT_GCC_DIR}/../include/string.h" ABSOLUTE)
if(APPLE)
execute_process(
COMMAND xcrun --show-sdk-path
OUTPUT_VARIABLE _PORT_MACOS_SDK OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
if(NOT _PORT_MACOS_SDK)
message(FATAL_ERROR "Cannot locate the macOS SDK; install the Xcode Command Line Tools")
endif()
set(PORT_HOST_STRING_H "${_PORT_MACOS_SDK}/usr/include/string.h")
else()
get_filename_component(PORT_HOST_STRING_H "${_PORT_GCC_DIR}/../include/string.h" ABSOLUTE)
endif()
configure_file(
"${CMAKE_SOURCE_DIR}/port/include/hoststring.h.in"
"${CMAKE_BINARY_DIR}/port/include/hoststring.h" @ONLY)
@@ -172,7 +198,11 @@ configure_file(
# the include path and shadows the system header; libstdc++'s <thread>/gthr
# headers in the fast3d C++ TUs need the real <sched.h> (cpu_set_t,
# sched_yield). Both MinGW and Linux keep <sched.h> at bin/../include.
get_filename_component(PORT_HOST_SCHED_H "${_PORT_GCC_DIR}/../include/sched.h" ABSOLUTE)
if(APPLE)
set(PORT_HOST_SCHED_H "${_PORT_MACOS_SDK}/usr/include/sched.h")
else()
get_filename_component(PORT_HOST_SCHED_H "${_PORT_GCC_DIR}/../include/sched.h" ABSOLUTE)
endif()
configure_file(
"${CMAKE_SOURCE_DIR}/port/include/hostsched.h.in"
"${CMAKE_BINARY_DIR}/port/include/hostsched.h" @ONLY)
@@ -182,7 +212,11 @@ configure_file(
# one C++ TU (fast3d): MinGW's own sec_api/stdlib_s.h does `#include
# <stdlib.h>`, which resolves to the N64 header via -I. The shim routes C++
# TUs to the host header by absolute path (same trick as hoststring.h).
get_filename_component(PORT_HOST_STDLIB_H "${_PORT_GCC_DIR}/../include/stdlib.h" ABSOLUTE)
if(APPLE)
set(PORT_HOST_STDLIB_H "${_PORT_MACOS_SDK}/usr/include/stdlib.h")
else()
get_filename_component(PORT_HOST_STDLIB_H "${_PORT_GCC_DIR}/../include/stdlib.h" ABSOLUTE)
endif()
configure_file(
"${CMAKE_SOURCE_DIR}/port/include/hoststdlib.h.in"
"${CMAKE_BINARY_DIR}/port/include/hoststdlib.h" @ONLY)
@@ -328,6 +362,13 @@ elseif(UNIX AND NOT APPLE)
# relocations are fine (unlike a 0x140000000 base, which needs mcmodel=large).
# This is the ELF analogue of the Windows build's fixed 0x140000000 base.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Ttext-segment=0x20000000")
elseif(APPLE)
# Mach-O executables normally reserve [0, 4 GiB) as __PAGEZERO. The port
# needs fixed mappings at 0x10000000 (ROM), 0x70000000 (s32-safe DRAM), and
# 0x80000000 (KSEG0 mirror), so retain null/low-pointer protection only up
# while leaving ample space before the first required mapping.
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -Wl,-pagezero_size,0x10000")
endif()
# port/shim first: PC-only shadow headers (bondtypes.h, bondconstants.h,
@@ -342,6 +383,7 @@ include_directories(
"${CMAKE_SOURCE_DIR}/src/game"
"${CMAKE_SOURCE_DIR}/src/libultra"
"${CMAKE_SOURCE_DIR}/src/libultra/audio"
"${CMAKE_SOURCE_DIR}/src/libultra/gu"
"${CMAKE_SOURCE_DIR}/port/include"
"${CMAKE_BINARY_DIR}/port/include"
)
@@ -395,6 +437,8 @@ if(NOT EXTRA_LIBRARIES)
set(EXTRA_LIBRARIES dbghelp -static-libstdc++ pthread)
elseif(WIN32)
set(EXTRA_LIBRARIES dbghelp -static-libstdc++)
elseif(APPLE)
set(EXTRA_LIBRARIES stdc++ m pthread)
else()
set(EXTRA_LIBRARIES stdc++ m dl pthread)
endif()
@@ -414,14 +458,41 @@ file(GLOB_RECURSE SRC_FAST3D "${CMAKE_SOURCE_DIR}/port/fast3d/*.cpp"
# scripts/gen_romassets.py). Defines every obseg/ramrom/music label and the
# ge007.ld segment markers as 0x10000000 + rom_offset, so `&symbol` / romCopy()
# work once romdata.c maps the .z64 at 0x10000000.
set(SRC_ROMASSETS "${CMAKE_SOURCE_DIR}/port/src/romassets_${ASSET_REGION}.s")
if(NOT EXISTS ${SRC_ROMASSETS})
message(FATAL_ERROR "Missing ${SRC_ROMASSETS} — run: python3 scripts/gen_romassets.py ${ASSET_REGION}")
set(SRC_ROMASSETS_INPUT "${CMAKE_SOURCE_DIR}/port/src/romassets_${ASSET_REGION}.s")
if(NOT EXISTS ${SRC_ROMASSETS_INPUT})
message(FATAL_ERROR "Missing ${SRC_ROMASSETS_INPUT} — run: python3 scripts/gen_romassets.py ${ASSET_REGION}")
endif()
# Absolute symbols inside the reserved N64-DRAM region (port/src/dram.c):
# cfb_16 (replaces src/cfb.c) and _bssSegmentEnd (mempool start marker).
set(SRC_DRAM_SYMS "${CMAKE_SOURCE_DIR}/port/src/dram_syms.s")
set(SRC_DRAM_SYMS_INPUT "${CMAKE_SOURCE_DIR}/port/src/dram_syms.s")
if(APPLE)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
set(SRC_ROMASSETS "${CMAKE_BINARY_DIR}/port/src/romassets_${ASSET_REGION}_macho.s")
set(SRC_DRAM_SYMS "${CMAKE_BINARY_DIR}/port/src/dram_syms_macho.s")
add_custom_command(
OUTPUT "${SRC_ROMASSETS}"
COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/gen_macho_syms.py"
"${SRC_ROMASSETS_INPUT}" "${SRC_ROMASSETS}"
DEPENDS "${SRC_ROMASSETS_INPUT}"
"${CMAKE_SOURCE_DIR}/scripts/gen_macho_syms.py"
VERBATIM)
add_custom_command(
OUTPUT "${SRC_DRAM_SYMS}"
COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/gen_macho_syms.py"
"${SRC_DRAM_SYMS_INPUT}" "${SRC_DRAM_SYMS}"
DEPENDS "${SRC_DRAM_SYMS_INPUT}"
"${CMAKE_SOURCE_DIR}/scripts/gen_macho_syms.py"
VERBATIM)
set_source_files_properties(${SRC_ROMASSETS} ${SRC_DRAM_SYMS}
PROPERTIES GENERATED TRUE)
else()
set(SRC_ROMASSETS "${SRC_ROMASSETS_INPUT}")
set(SRC_DRAM_SYMS "${SRC_DRAM_SYMS_INPUT}")
endif()
# Game code — compiled unmodified.
file(GLOB_RECURSE SRC_GAME "${CMAKE_SOURCE_DIR}/src/game/*.c")
@@ -452,6 +523,39 @@ file(GLOB SRC_LIBULTRARE_AUDIO "${CMAKE_SOURCE_DIR}/src/libultrare/audio/*.c")
# gu matrix helpers.
file(GLOB SRC_GU "${CMAKE_SOURCE_DIR}/src/libultra/gu/*.c")
# GCC for Darwin crashes while lowering the decomp's ELF-style
# `#pragma weak alias = target` directives. Compile generated copies with
# those directives removed, then provide their aliases in Mach-O assembly.
set(SRC_MACHO_ALIASES "")
if(APPLE)
set(_MACHO_GENERATED_SOURCES "")
set(_MACHO_WEAK_INPUTS
"${CMAKE_SOURCE_DIR}/src/game/lv.c"
"${CMAKE_SOURCE_DIR}/src/game/objective_status.c"
"${CMAKE_SOURCE_DIR}/src/game/spectrum.c"
"${CMAKE_SOURCE_DIR}/src/libultra/gu/sinf.c"
"${CMAKE_SOURCE_DIR}/src/libultra/gu/cosf.c")
foreach(_INPUT IN LISTS _MACHO_WEAK_INPUTS)
get_filename_component(_NAME "${_INPUT}" NAME)
set(_OUTPUT "${CMAKE_BINARY_DIR}/port/src/macho_${_NAME}")
add_custom_command(
OUTPUT "${_OUTPUT}"
COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/strip_weak_pragmas.py"
"${_INPUT}" "${_OUTPUT}"
DEPENDS "${_INPUT}" "${CMAKE_SOURCE_DIR}/scripts/strip_weak_pragmas.py"
VERBATIM)
list(APPEND SRC_MACHO_ALIASES "${_OUTPUT}")
list(APPEND _MACHO_GENERATED_SOURCES "${_OUTPUT}")
list(REMOVE_ITEM SRC_GAME "${_INPUT}")
list(REMOVE_ITEM SRC_GU "${_INPUT}")
endforeach()
list(APPEND SRC_MACHO_ALIASES
"${CMAKE_SOURCE_DIR}/port/src/macho_weak_aliases.s")
set_source_files_properties(${_MACHO_GENERATED_SOURCES}
PROPERTIES GENERATED TRUE)
endif()
# VI mode table (DATA, not a driver): fr.c references osViModeTable ~15x.
# This is the only libultra/io file we compile — the rest are hardware
# drivers that get shimmed in port/src/libultra.c.
@@ -569,6 +673,7 @@ set(SRC
${SRC_LIBULTRARE_LIBC}
${SRC_ASSETS}
${SRC_GU}
${SRC_MACHO_ALIASES}
${SRC_ENGINE}
)
+5 -5
View File
@@ -75,8 +75,8 @@ listed plainly there.
**v0.2.2 - playable, with known rough edges.** The full single-player
campaign is completable end to end (all 21 missions, Agent difficulty,
playtested), at a steady 60 fps; all 21 solo missions load, render and run
crash-free, verified on Windows, Linux and real Steam Deck hardware. Feedback
is very welcome.
crash-free, verified on Windows, Linux and real Steam Deck hardware. Native
Intel macOS builds are supported. Feedback is very welcome.
**Working:** boot sequence and front end (menu → mission select → briefing →
start), front-end menu navigation on the left stick to match the F10 overlay
@@ -87,7 +87,7 @@ dual-stick controller layout; file-backed saves; faithful N64 progression by def
(F10 → *All unlocked* opens every level, 007 mode and the full cheat menu); F10 in-game
options overlay (resolution, frame cap, MSAA, filtering, FOV, sensitivity,
quit to desktop);
Windows and Linux.
Windows, Linux, and Intel macOS.
**Known issues:**
@@ -130,7 +130,7 @@ Windows and Linux.
capture full faulting registers in `ge007.crash.log`.
- Assorted further cosmetic defects are tracked in
[`docs/dev/GRAPHICS-BACKLOG.md`](docs/dev/GRAPHICS-BACKLOG.md).
- No macOS or ARM support; no controller rebinding UI.
- No ARM support; no controller rebinding UI.
Root causes and fix status for every item: the [release notes](https://github.com/jkdansereau/goldeneye-pc-port/releases)
and the finding log in [`docs/dev/findings.md`](docs/dev/findings.md).
@@ -173,7 +173,7 @@ directionally, on the way to v1.0:
- **PAL and JP ROM support** ([issue #85](https://github.com/jkdansereau/goldeneye-pc-port/issues/85)); NTSC-U is the only supported region today.
- **Real widescreen** (properly expanding the field of view at 16:9, rather
than today's 4:3-stretch).
- **Controller rebinding UI**, and macOS/ARM builds.
- **Controller rebinding UI**, and ARM builds.
- **LAN multiplayer**: reviving GoldenEye's original split-screen/deathmatch
netplay across multiple PCs on a local network. Genuinely under
consideration, but early and not started; no ETA.
+15 -7
View File
@@ -1,6 +1,6 @@
---
title: Building
description: Full build and asset-extraction guide for the GoldenEye 007 PC port, covering Windows (MSYS2) and Linux.
description: Full build and asset-extraction guide for the GoldenEye 007 PC port on Windows, Linux, and macOS.
---
## Building the PC port
@@ -27,21 +27,29 @@ for accepted versions and hashes.
### Port build
> **The Windows (MSYS2 MINGW64) path is the primary one.** The Linux build is
> compiled by CI on every push and ships in the release bundle (also
> playtested on Steam Deck hardware); the macOS column is best-effort
> guidance and has never been built or run there. Expect to fix build breaks
> yourself on untested platforms.
> **The Windows (MSYS2 MINGW64) path is the primary one.** Linux is compiled
> by CI and ships in the release bundle. Native Intel macOS builds are also
> supported; Apple Silicon remains untested.
| Need | Windows (MSYS2 MINGW64) | Debian/Ubuntu | macOS (Homebrew) |
|------|------------------------|---------------|------------------|
| toolchain | `mingw-w64-x86_64-toolchain` | `build-essential` | Xcode CLT / `gcc` |
| toolchain | `mingw-w64-x86_64-toolchain` | `build-essential` | Xcode CLT + Homebrew `gcc` |
| CMake | `mingw-w64-x86_64-cmake` | `cmake` | `cmake` |
| SDL2 | `mingw-w64-x86_64-SDL2` | `libsdl2-dev` | `sdl2` |
| zlib | `mingw-w64-x86_64-zlib` | `zlib1g-dev` | `zlib` |
| OpenGL | (in the toolchain) | `libgl1-mesa-dev` | (system) |
| Python 3 | `mingw-w64-x86_64-python` | `python3` | `python3` |
On macOS, install the build dependencies with:
```sh
brew install cmake gcc sdl2 zlib python3
```
The project requires real GNU GCC for the decomp's Plan 9 struct extensions.
Apple's `/usr/bin/gcc` is Clang and is not compatible. CMake automatically
selects Homebrew's versioned `gcc-N`/`g++-N` executables.
### Asset extraction (decompilation toolchain)
The extraction scripts need `binutils-mips-linux-gnu` (or an equivalent MIPS
+48
View File
@@ -561,6 +561,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D258 | **PAL/JP asset conversion is broken at the source-data level — `scripts/filelist.e.csv` and `filelist.j.csv` carry naming defects, so `gen_romassets.py e/j` (and…** — full `## D258` entry at file tail | OPEN — repair path verified, not applied (out of v0.2.0 scope). |
| D285 | **Steam Deck SIGSEGV during D255 live-testing = SEPARATE bug: the `sndHandleEvent` preempt-scan walks the active-sound list unlocked (M-140; fixed M-141).** — full `## D285` entry at file tail | **FIXED (M-141, `src/snd.c`)** — `osSetIntMask` lock added around the preempt-scan, matching `sndSetupSound`'s existing unconditional (no `#ifdef PORT`) pattern in the same file; build green, `-level_09`/`-level_20` unregressed (1400+ frames, no crash, no D152 lock-stuck warnings). Not yet gdb/live-confirmed against a real reproduced crash — see M-141 body. Do not conflate with D255 — different thread, different subsystem. |
| D293 | **QoL ask: no discoverable way to quit the game from the UI (M-141, Steam Deck user playtest — "couldn't see a way to exit the game. Maybe build this into the…** — full `## D293` entry at file tail | FIXED (M-141) — "Quit to desktop" row added to the F10 overlay; build-verified Win+Linux, not yet live-confirmed.… |
| D294 | **Native Intel macOS build support.** Apple Clang lacks the required Plan 9 struct extensions; Darwin also needs SDK header discovery, Mach-O absolute/weak-alias symbol spelling, POSIX shared-memory backing instead of Linux `memfd_create`, and Darwin signal-context handling. | **FIXED** — CMake selects versioned Homebrew GNU GCC, generates Mach-O assembly/source adapters in the build tree, and the port layer now handles Darwin libc macros, shared memory, and crash diagnostics. Fresh `build-pc.sh ntsc-final` build and `--version` smoke test pass on x86_64 macOS 26.6.2. |
Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS
threads, compiled GE's real `src/sched.c`, and brought in PD's fast3d software
@@ -10149,3 +10150,50 @@ do {
## D293 — QoL ask (M-141, Steam Deck user playtest): no discoverable way to quit the game from the UI on a controller-only setup
**Context.** Deck/controller-only setups had only window-close/Alt+F4 to quit; no in-UI affordance.
**FIXED.** Added a `ROW_ACTION` row kind (not config-backed, same pattern as the existing `__Resolution` row) to the F10 options overlay ("Quit to desktop") that triggers the identical `configSave()`+`exit(0)` path `SDL_QUIT` already uses. Build-verified on Windows and Linux; `verify.sh dam` PASS, no regression.
## D294 — Native Intel macOS build support
**Context.** The CMake file contained nominal Apple branches, but had never
completed a Darwin build. `/usr/bin/gcc` is Apple Clang and rejects the
decomp's required `-fplan9-extensions`; host-header paths assumed MinGW/Linux;
ELF-style absolute symbols and weak aliases are invalid in Mach-O; the DRAM
mirror used Linux-only `memfd_create`; and crash diagnostics only implemented
Windows/Linux.
**FIXED.** CMake now selects Homebrew's versioned GNU `gcc-N`/`g++-N`, locates
libc headers through the macOS SDK, omits Linux-only `-ldl`, and generates
Mach-O-spelled copies of the ROM/DRAM absolute symbols. Five sources containing
ELF weak-alias pragmas are copied into the build tree with only those pragmas
removed; a Mach-O assembly file supplies equivalent indirect symbols. Darwin
fortify/`errno` macros are suppressed while legacy declarations parse.
`dramReserve` uses an immediately-unlinked POSIX shared-memory object, and the
POSIX crash handler supports Darwin's x86-64 signal context.
**Verified.** On x86_64 macOS 26.6.2 with Homebrew GCC 16.2.0, both an
incremental native build and a fresh build through `build-pc.sh` completed at
100%. `file` identifies the output as a Mach-O 64-bit x86_64 executable,
`otool -L` resolves SDL2, zlib, libstdc++, Cocoa, and OpenGL, and
`ge007.x86_64 --version` reports `x86_64-osx`. Runtime gameplay was not
exercised because ROM-derived user data is not part of build verification.
Apple Silicon remains unsupported/untested.
**Runtime follow-up.** The first real launch exposed a link-time/runtime gap:
Darwin's default Mach-O `__PAGEZERO` segment reserved `[0, 0x100000000)`.
Consequently both the cartridge mapping at `0x10000000` and the mirrored DRAM
mapping at `0x70000000`/`0x80000000` failed despite a successful build.
The Apple link now sets `-pagezero_size 0x10000`, preserving null-page
protection while leaving all three N64 address windows available. Darwin also
lacks Linux's `MAP_FIXED_NOREPLACE`; the ROM mapping
therefore uses `MAP_FIXED` after the linker has guaranteed the cartridge range
is vacant. Without it, `mmap` relocated the cartridge despite the now-free
range, boot fell back to a heap copy, and direct absolute ROM-symbol reads
crashed during level initialization. With fixed ROM mapping working, the next
launch reached rendering and exposed AppKit's main-thread-only event rule:
fast3d polled SDL from the scheduler/render thread even though the port's host
main loop already owns a complete event pump. The fast3d pump is now disabled
only on macOS; `videoPumpEvents()` continues to process input/window events.
After generating all three required sidecar sets from the user's NTSC ROM,
`-level_09` ran for more than 1,500 rendered frames with the ROM fixed at
`0x10000000`, both DRAM views active, all 512 model and 73 stage sidecars
patched, and no crash. The missing packed-vertex OpenGL entry-point messages
are non-fatal capability noise from the GL loader; rendering proceeds on the
created macOS OpenGL 4.1 core context.
+24
View File
@@ -485,6 +485,30 @@ through a converter or a runtime bswap fixup reads scrambled.
## C2. Port-layer / SDL shims
- **Darwin assembly uses Mach-O symbol spelling.** GAS absolute symbols that
are `.global foo` / `.set foo, address` on ELF/PE must become `.globl _foo`
/ `.set _foo, address`; Darwin also rejects the ELF `.section .data`
spelling used by these symbol-only files. Generate transformed build-tree
copies rather than changing the cross-platform source files (D294).
- **Darwin GCC cannot lower ELF-style `#pragma weak alias = target`.** It can
ICE in `assemble_alias`; compile generated copies with those pragmas removed
and provide Mach-O indirect symbols (`.set _alias, _target`) separately
(D294).
- **Mach-O's default `__PAGEZERO` reserves the entire low 4 GiB.** A build
can compile and link successfully yet fail every fixed N64 mapping at
runtime (`0x10000000`, `0x70000000`, `0x80000000`). Link with
`-Wl,-pagezero_size,0x10000` so null-page pointers remain protected while
the cartridge and DRAM ranges are available (D294).
- **Darwin has no `MAP_FIXED_NOREPLACE`; a fixed-address hint can relocate.**
Once `__PAGEZERO` is deliberately bounded below the cart range, use
`MAP_FIXED` for the `0x10000000` cartridge mapping. Retaining the advisory
hint silently falls back to a heap ROM and later crashes on direct absolute
ROM-symbol reads (D294).
- **AppKit event polling is main-thread-only.** This port has both a host-main
`videoPumpEvents()` loop and fast3d's render-thread event pump. The latter
must be disabled on macOS or `SDL_PollEvent` raises
`NSInternalInconsistencyException`; the main-thread loop already handles
the complete event set (D294).
- `#include <PR/os.h>` in a port `.c`/`.h` that also sees `<errno.h>`
breaks: `OSContStatus`/`OSContPad` have a `u8 errno;` field vs errno.h's
macro. libultra.c wraps the include in `#pragma push_macro("errno")` /
+7
View File
@@ -309,6 +309,12 @@ static void gfx_sdl_get_dimensions(uint32_t* width, uint32_t* height, int32_t* p
}
static void gfx_sdl_handle_events(void) {
#if defined(__APPLE__)
/* AppKit requires event polling on the process main thread. The host
* loop in videoPumpEvents owns the complete SDL queue, so the scheduler
* render thread must not call SDL_PollEvent on macOS. */
return;
#else
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
@@ -357,6 +363,7 @@ static void gfx_sdl_handle_events(void) {
break;
}
}
#endif
}
/* The window/context are created on the host main thread, but the game's
+11
View File
@@ -28,9 +28,20 @@
* "using typedef-name after 'struct'"), and no C++ TU has an implicit
* declaration needing a fix here. C++ TUs keep their pre-D38 header exposure.
*/
#ifndef _PC_PROTOS_H_
#define _PC_PROTOS_H_
#if defined(__APPLE__)
/* Darwin SDK headers expose these libc calls as fortify function-like
* macros. This legacy declaration list needs their actual symbol names. */
#undef memcpy
#undef snprintf
#undef strcat
#undef strcpy
#undef strncpy
#endif
#if defined(PORT) && defined(__x86_64__) && !defined(__cplusplus)
#include <PR/ultratypes.h> /* u8..s32, f32, size_t (host on PC) */
+12
View File
@@ -28,7 +28,19 @@
#define _PORT_SHIM_OS_H_
#if defined(PORT)
# if defined(__APPLE__)
/* Darwin exposes errno and sprintf as function-like macros. Suppress them
* while parsing the N64 API's errno fields and sprintf declaration. */
# pragma push_macro("errno")
# pragma push_macro("sprintf")
# undef errno
# undef sprintf
# endif
# include "include/PR/os.h"
# if defined(__APPLE__)
# pragma pop_macro("sprintf")
# pragma pop_macro("errno")
# endif
# undef OS_K0_TO_PHYSICAL
# define OS_K0_TO_PHYSICAL(x) ((u32)((char *)(x) - 0x70000000))
+15 -7
View File
@@ -11,6 +11,12 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#if defined(__APPLE__) && !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 700
#endif
#if defined(__APPLE__) && !defined(_DARWIN_C_SOURCE)
#define _DARWIN_C_SOURCE 1
#endif
#include <stdlib.h>
#include <stdio.h>
@@ -303,7 +309,7 @@ static LONG __stdcall crashHandler(PEXCEPTION_POINTERS exinfo)
return EXCEPTION_CONTINUE_EXECUTION;
}
#elif defined(PLATFORM_LINUX)
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
#include <ucontext.h>
#include <signal.h>
@@ -341,7 +347,7 @@ static void crashStackTrace(char *msg, int sig, void *pc, ucontext_t *ucontext,
if (siginfo && (sig == SIGSEGV || sig == SIGBUS)) {
CRASH_MSG("FAULT ADDR: %p\n", (void *)siginfo->si_addr);
}
#if defined(PLATFORM_X86_64)
#if defined(PLATFORM_LINUX) && defined(PLATFORM_X86_64)
if (ucontext) {
CRASH_MSG("REGS: Rax=%p Rcx=%p Rdx=%p Rsi=%p Rdi=%p R8=%p R9=%p R10=%p R11=%p\n",
(void *)ucontext->uc_mcontext.gregs[REG_RAX],
@@ -410,10 +416,12 @@ static void crashHandler(int sig, siginfo_t *siginfo, void *ctx)
ucontext_t *ucontext = ctx ? (ucontext_t *)ctx : NULL;
void *pc = NULL;
if (ucontext) {
#ifdef PLATFORM_X86
#if defined(PLATFORM_LINUX) && defined(PLATFORM_X86)
pc = (void *)ucontext->uc_mcontext.gregs[REG_EIP];
#elif defined(PLATFORM_X86_64)
#elif defined(PLATFORM_LINUX) && defined(PLATFORM_X86_64)
pc = (void *)ucontext->uc_mcontext.gregs[REG_RIP];
#elif defined(PLATFORM_MACOS) && defined(PLATFORM_X86_64)
pc = (void *)ucontext->uc_mcontext->__ss.__rip;
#endif
}
@@ -470,7 +478,7 @@ void crashDumpThreads(const unsigned long *tids, const char **names, int count)
}
}
#endif /* PLATFORM_WINDOWS / PLATFORM_LINUX */
#endif /* PLATFORM_WINDOWS / POSIX */
int g_CrashEnabled = 0;
@@ -480,7 +488,7 @@ void crashInit(void)
SetErrorMode(SEM_FAILCRITICALERRORS);
prevExFilter = SetUnhandledExceptionFilter(crashHandler);
g_CrashEnabled = 1;
#elif defined(PLATFORM_LINUX)
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
struct sigaction sigact = { 0 };
sigact.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigact.sa_sigaction = crashHandler;
@@ -501,7 +509,7 @@ void crashShutdown(void)
if (prevExFilter) {
SetUnhandledExceptionFilter(prevExFilter);
}
#elif defined(PLATFORM_LINUX)
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
sigaction(SIGSEGV, &prevSigAction, NULL);
sigaction(SIGABRT, &prevSigAction, NULL);
sigaction(SIGBUS, &prevSigAction, NULL);
+15 -2
View File
@@ -53,7 +53,10 @@
#include <windows.h>
#else
#define _GNU_SOURCE /* memfd_create — must precede all system headers */
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
@@ -101,10 +104,20 @@ void *dramReserve(void)
CloseHandle(hSec);
return v1;
#else
/* memfd + two MAP_SHARED mmaps = one backing store, two views. */
/* One shared file descriptor + two MAP_SHARED mmaps = one backing store,
* two views. Linux can keep it fully anonymous with memfd; Darwin uses a
* POSIX shared-memory object and unlinks its name immediately. */
#if defined(PLATFORM_MACOS)
char shmName[64];
snprintf(shmName, sizeof(shmName), "/ge007_dram_%ld", (long)getpid());
int fd = shm_open(shmName, O_RDWR | O_CREAT, 0600);
if (fd >= 0)
shm_unlink(shmName);
#else
int fd = memfd_create("ge007_dram", 0);
#endif
if (fd < 0) {
sysFatalError("dram: memfd_create failed");
sysFatalError("dram: could not create shared backing store");
}
if (ftruncate(fd, DRAM_SIZE) != 0) {
sysFatalError("dram: ftruncate failed");
+1 -1
View File
@@ -972,7 +972,7 @@ static void piServiceDma(s32 direction, u32 srcPA, void *dstVA, u32 size)
char win[1200] = "";
char *wp = win;
for (int i = 0; i < 32; i++) {
wp += snprintf(wp, win + sizeof(win) - (wp - win),
wp += snprintf(wp, sizeof(win) - (size_t)(wp - win),
" %p", (void *)sp[i]);
}
#if defined(PLATFORM_WINDOWS)
+25
View File
@@ -0,0 +1,25 @@
/* Mach-O equivalents of the decomp's `#pragma weak alias = target`. */
.globl _g_DebugPortalsInputBufferSource1
.set _g_DebugPortalsInputBufferSource1, _g_DebugPortalsInputBuffer1
.globl _g_DebugPortalsInputBufferSource2
.set _g_DebugPortalsInputBufferSource2, _g_DebugPortalsInputBuffer2
.globl _g_DebugPortalsInputBufferSource3
.set _g_DebugPortalsInputBufferSource3, _g_DebugPortalsInputBuffer3
.globl _g_DebugPortalsInputBufferSource4
.set _g_DebugPortalsInputBufferSource4, _g_DebugPortalsInputBuffer4
.globl _objectiveGetStatus_WEAK
.set _objectiveGetStatus_WEAK, _get_status_of_objective
.globl _spec_keyboard_row_caps_z_x_c_v
.set _spec_keyboard_row_caps_z_x_c_v, _spec_keyboard_buffer
.globl _fsin
.set _fsin, ___sinf
.globl _sinf
.set _sinf, ___sinf
.globl _fcos
.set _fcos, ___cosf
.globl _cosf
.set _cosf, ___cosf
+9 -2
View File
@@ -36,6 +36,7 @@ extern int snprintf(char *str, size_t maxsize, const char *format, ...);
/* POSIX: mmap the ROM at the fixed cart address (mirrors the VirtualAlloc
* path). <sys/mman.h> is a host header the decomp include path does not
* shadow. */
#include <errno.h>
#include <sys/mman.h>
#endif
@@ -382,9 +383,15 @@ int romdataInit(void)
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
#ifdef MAP_FIXED_NOREPLACE
flags |= MAP_FIXED_NOREPLACE;
#elif defined(PLATFORM_MACOS)
/* __PAGEZERO ends at CART_BASE in the macOS build, so this range
* is deliberately vacant. Darwin has no MAP_FIXED_NOREPLACE and
* otherwise treats CART_BASE only as a hint, relocating it. */
flags |= MAP_FIXED;
#endif
void *at = mmap((void *)(uintptr_t)CART_BASE, maplen,
PROT_READ | PROT_WRITE, flags, -1, 0);
int mapErrno = errno;
if (at != MAP_FAILED && at == (void *)(uintptr_t)CART_BASE) {
mappedLen = maplen;
return romdataFinishCartMap(tok, img, sideTotal, cgTotal);
@@ -392,8 +399,8 @@ int romdataInit(void)
if (at != MAP_FAILED)
munmap(at, maplen);
sysLogPrintf(LOG_WARNING, "romdataInit: could not map 0x%08X "
"(ASLR/kernel refused the fixed address); using heap "
"copy — direct ROM reads will fail", CART_BASE);
"(mmap result=%p errno=%d); using heap copy — direct "
"ROM reads will fail", CART_BASE, at, mapErrno);
#endif
}
+6 -2
View File
@@ -12,6 +12,9 @@
#include <stdio.h>
#include <string.h>
/* Prevent GCC's implicit declaration warning */
extern void exit(int status);
#if defined(_WIN32)
#include <direct.h>
#define GE_MKDIR(p) _mkdir(p)
@@ -496,8 +499,9 @@ void videoStartFrame(void)
* CREATED the window pumps them — and every game thread can be blocked on a
* message queue at any time. So the host main thread (which created the
* window in videoInit) must keep pumping; otherwise the window goes
* "Not Responding" and ESC/close never arrive. fast3d's own handle_events
* (which runs during rendering) remains as a backstop.
* "Not Responding" and ESC/close never arrive. On macOS AppKit strictly
* requires this main-thread pump, so fast3d's render-thread event handler is
* disabled there; on other platforms it remains as a backstop.
*/
void videoPumpEvents(void)
{
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Convert ELF/PE GAS absolute symbols to Mach-O spelling."""
import pathlib
import re
import sys
def main() -> int:
if len(sys.argv) != 3:
print(f"usage: {sys.argv[0]} INPUT OUTPUT", file=sys.stderr)
return 2
source = pathlib.Path(sys.argv[1])
output = pathlib.Path(sys.argv[2])
lines = []
for line in source.read_text(encoding="utf-8").splitlines():
if re.match(r"^\s*\.section\s+\.data\s*$", line):
continue
match = re.match(r"^(\s*)\.global\s+([A-Za-z_]\w*)\s*$", line)
if match:
lines.append(f"{match.group(1)}.globl _{match.group(2)}")
continue
match = re.match(
r"^(\s*)\.set\s+([A-Za-z_]\w*)(\s*,.*)$", line
)
if match:
lines.append(
f"{match.group(1)}.set _{match.group(2)}{match.group(3)}"
)
continue
lines.append(line)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text("\n".join(lines) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Remove ELF-style weak aliases from a C source copied for Mach-O."""
import pathlib
import re
import sys
def main() -> int:
if len(sys.argv) != 3:
print(f"usage: {sys.argv[0]} INPUT OUTPUT", file=sys.stderr)
return 2
source = pathlib.Path(sys.argv[1])
output = pathlib.Path(sys.argv[2])
text = source.read_text(encoding="utf-8")
text = re.sub(r"^[ \t]*#pragma[ \t]+weak[^\n]*\n", "", text,
flags=re.MULTILINE)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(text, encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())