deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (native)

Black Ops II statically recompiled to ARM64 runs natively on Apple Silicon at up to 60 fps

A fan project converts a Steam copy of Call of Duty: Black Ops II from 32-bit x86 into native ARM64 macOS code using Remill and LLVM, running the full game on an M1 without Wine or emulation.

Black Ops II statically recompiled to ARM64 runs natively on Apple Silicon at up to 60 fps

A AAA Windows game becomes a native Mac app

A project called bo2-mac, published on GitHub by adrbn and shared on Hacker News on 11 November 2026, converts Call of Duty: Black Ops II from 32-bit x86 Windows into native ARM64 macOS machine code ahead of time. The game then runs as ordinary compiled code in a single process, with no Wine, no Rosetta translation layer and no just-in-time emulator involved. On an Apple M1, the project reports the full game — campaign, Zombies and multiplayer against bots — running at frame rates between roughly 51 and 60 fps.

What the converter does

According to the project's README, the tool reads the three Windows executables from the player's own Steam copy — the campaign, Zombies and multiplayer binaries, around 150,000 functions in total — and rewrites their x86 machine code into ARM64 before the game ever launches. The output is one Mac application whose menus switch between the three modes.

The repository deliberately contains no game assets and no translated game code, the README stresses: every player builds their own app from their own purchased copy. The converter accepts only one exact Steam build, verified by SHA-256, and the launcher asks the local Steam client whether the signed-in account owns the game before proceeding. The project describes itself as an unofficial fan effort with no connection to Activision, Treyarch or Microsoft.

The pipeline

Conversion happens in five steps, all on the player's Mac, and takes about one hour and forty-five minutes on an M1 (the campaign alone takes about forty-five minutes and is playable as soon as it finishes). Per the README:

  • Ghidra catalogues the functions in each executable.
  • Remill v6.0.1 lifts each function to LLVM IR; the project carries five patches to the lifter, including correct handling of the x87 rounding mode and a change that forks a running Remill per function instead of reloading x86 semantics.
  • Custom LLVM passes rewrite the lifted IR before compiling with clang at -O2: guest memory accesses are inlined, lock sequences become hardware atomics, returns stay inside the lifted code, and the x87 stack is promoted to local variables.
  • A hand-written runtime implements the Windows imports the game actually calls, with exact stdcall calling conventions. Direct3D 11 is implemented as a fake-COM layer over Metal, with DXBC shaders translated via the HLSLcc library; XAudio2 maps to CoreAudio and cutscene videos are decoded with libvpx.
  • Guest threads begin as coroutines on a single host thread, which makes runs replayable; a faster configuration moves the server, renderer, audio and job workers onto real host threads, requiring genuine memory barriers because ARM64 lacks x86's strong ordering guarantees.

DRM and fallbacks

Notably, the copy protection is executed rather than stripped out. The README explains that integrity checks hidden behind hardware exceptions, single-step traps and heap-decrypted code run inside a small x86 interpreter built on Unicorn, which then returns control to the recompiled code — no check result is forced. The same interpreter covers any code the translation missed, so gaps degrade performance instead of crashing the game. The Demonware online backend that Zombies and multiplayer expect is answered by a local responder, so no traffic leaves the machine.

Measured results and debugging notes

On an M1, the project measured the campaign at 51–60 fps with every mission loading and chaining through to the end credits; Zombies at 57–60 fps solo across six maps (Die Rise at 43–58); and multiplayer against bots at 53–60 fps across eleven game modes, all reached through the game's real menus. The in-game server clock tracked wall time at about 1.0×, and disk usage peaked near 10 GB, settling at about 4 GB.

The README also documents thirteen engineering findings. Examples: the lifter ignoring the x87 rounding mode made the prone view shake in Zombies because fistp rounded wrong in the view-height network codec; answering "no SSE2" to a CPU feature query sent the C runtime down an x87 code path that froze two missions; and stutter of 170–430 ms was traced to the project's own logging blocking the render thread on a nearly full disk.

Why it matters

Static recompilation has mostly been demonstrated on smaller or older titles; a DRM-protected commercial AAA release with three executables and 150,000 functions is a far harder target. This project shows the lift-optimize-relink approach can yield a full-speed, fully native port without Wine, Rosetta or runtime translation layers like DXVK or DXMT. Its documented findings — x87 semantics, memory ordering, feature-flag detection — form a practical catalogue of what breaks when x86 semantics move to ARM64, valuable far beyond this one game. The build-only-from-your-own-copy design is also a thoughtful attempt to answer the legal question that hangs over recompilation projects, though rights-holder tolerance for it remains untested.

  • #static-recompilation
  • #arm64
  • #apple-silicon
  • #macos
  • #llvm

Related posts