Skip to content

patch_and_script_reference

runner edited this page Oct 5, 2026 · 15 revisions

Patching & Script Reference

This page documents how the patches are built, applied, and deployed — the scripts, the patcher app, and the mod-injection chain support that make Arknights: Endfield run on Apple Silicon macOS. It is the technical reference for the "How It Works" section; the installation guide is the practical "get it running" path.


Table of Contents

  1. Overview: What Gets Patched
  2. The Patch Set
  3. Build Scripts
  4. Deploy Scripts
  5. The FineWine Patcher.app
  6. Mod Injection Chain Support (proxy_d3d11)
  7. Debug & Capture Scripts
  8. Troubleshooting the Patch Pipeline

Overview: What Gets Patched

The project does not redistribute CrossOver, Wine, or the game. It ships patches (modifications to Wine's source) plus scripts to build and deploy them. The deployment model is a surgical module swap: only three core Wine modules are replaced inside a copy of CrossOver, while everything else (graphics runtimes, fonts, TLS, windowing) stays stock CodeWeavers.

CrossOver.app (stock, licensed)
        │
        ▼  [scripts/swap-into-crossover.sh]
        │
CrossOver_Endfield_Patch.app
├── lib/wine/x86_64-unix/ntdll.so          ← patched (Rosetta fixes + QPC timing)
├── lib/wine/x86_64-windows/kernel32.dll   ← patched (KiUser*Dispatcher int3 spoof)
└── lib/wine/x86_64-windows/ntoskrnl.exe   ← patched (17 ntoskrnl em-backports)
```text

### Why only three modules?

| Reason | Explanation |
|---|---|
| **Licensing** | CrossOver's remaining binaries are proprietary; swapping only Wine's LGPL modules keeps the redistribution story clean. |
| **Stability** | Stock graphics, audio, and windowing stacks are known-good; we only touch what the anti-cheat and Rosetta bugs require. |
| **Reproducibility** | A minimal 64-bit-only Wine build is deterministic and auditable; a full CrossOver rebuild would bundle unreviewable components. |
| **GPTK independence** | Apple's Game Porting Toolkit (D3DMetal) is read from the user's own DMG, never bundled. |

See **[03-crossover-backend-landscape.md](project-information/03-crossover-wine-architecture.md)** for the backend layout and **[05-swapping-into-crossover.md](project-information/05-swapping-into-crossover.md)** for the code-signing mechanics.

---

## The Patch Set

Patches live in `patches/` and are split into two stages. All patches are **LGPL-2.1-or-later** (modifications to Wine); see **[patches/README.md](../patches/README.md)** for the full inventory and licensing.

### Stage 1 — macOS-specific Rosetta fixes

| Patch | File | Purpose |
|---|---|---|
| `stage1-macos/0001-ntdll-signal-0F-1F-NOP-skip.patch` | `dlls/ntdll/unix/signal_x86_64.c` | Decode and skip multi-byte `0F 1F` NOPs that Rosetta 2 falsely faults as illegal instructions (VMProtect/TenProtect "tpshell" emits 23k+ of these per launch). |
| `stage1-macos/0002-ntdll-signal-privileged-instr.patch` | `dlls/ntdll/unix/signal_x86_64.c` | Deliver `EXCEPTION_PRIV_INSTRUCTION` for privileged ops (e.g. `mov rbx, cr3`) instead of `EXCEPTION_ILLEGAL_INSTRUCTION`, matching Linux behavior so ACE's anti-VM probe passes. |

**Root cause:** both are Rosetta 2 bugs in how it delivers CPU faults for x86_64 instructions on ARM64. They are general CrossOver-on-Apple-Silicon issues (cf. WineHQ Bug 45083) and are candidates for upstreaming to CodeWeavers.

### Stage 2 — dw-proton anti-cheat port

Baked into `patches/stage2-dwproton/`, these are ports of the Linux [dw-proton (Dawn Winery)](https://dawn.wine/) patches that make Endfield launch on Proton.

| Component | Location | Purpose |
|---|---|---|
| `ntoskrnl.exe` em-backports (17 functions) | `dlls/ntoskrnl.exe/ntoskrnl.c` | Implement/stub kernel routines ACE's driver calls (`KeAcquireGuardedMutex`, `PsGetProcessSessionId`, `PsGetProcessImageFileName`, `MmGetPhysicalMemoryRanges`, …). |
| `KiUser*Dispatcher` int3 spoof | `dlls/kernel32/module.c` | Return a 4× `int3` stub for `KiUserApcDispatcher` / `KiUserCallbackDispatcher` when the process is `Endfield.exe` (or `EM-Win64-Shipping.exe`), defeating tpshell's dispatcher hook/probe. |
| `NtDelayExecution` QPC timing | `dlls/ntdll/unix/sync.c` | Relative waits computed via `NtQueryPerformanceCounter` + busy-`select()` for ACE's timing-sensitive checks. |

See **[02-dwproton-ace-patches.md](project-information/02-dwproton-ace-patches.md)** for the verbatim patch text and **[13-working-solution.md](project-information/13-working-solution.md)** for the full patch list and deployment.

---

## Build Scripts

All build scripts live in `scripts/`. The canonical entry point is `./scripts/build-wine.sh`.

### `build-wine.sh` — build the patched Wine

A single-shot script that runs the full pipeline: **deps → fetch source → apply patches → configure → build**.

```bash
./scripts/build-wine.sh all
```python

**Step-by-step mode** (useful for debugging a specific stage):

```bash
./scripts/build-wine.sh deps       # Homebrew: bison, mingw-w64, meson, pkg-config, ...
./scripts/build-wine.sh fetch      # download CrossOver 26.3 Wine source (~142 MB), git-init it
./scripts/build-wine.sh apply      # git apply all 24 patches (verified to apply cleanly)
./scripts/build-wine.sh configure  # 64-bit-only, under `arch -x86_64` (Rosetta host)
./scripts/build-wine.sh build      # make -j
```yaml

**Key build facts:**

- **No `cx-llvm` / `win32on64` needed.** Endfield is 64-bit only, so the build uses the standard toolchain — sidestepping the (now-unavailable) patched CrossOver clang. See **[04-building-crossover-wine.md](project-information/04-building-crossover-wine.md)**.
- **The build is minimal** (no bundled fonts/TLS/graphics libs). We swap only 3 core modules into CrossOver, which already provides everything else.
- **Build time:** ~10 min on a 10-core M4; ~1.5–2.5 h cold on the 3-vCPU CI runner.

### `build-moltenvk.sh` — build the patched MoltenVK (Vulkan renderer only)

Used only for the experimental Vulkan renderer. Requires a full Xcode install.

```bash
./scripts/build-moltenvk.sh
./scripts/package.sh
./scripts/apply-modules.sh
```bash

The patched MoltenVK is **already shipped** in the FineWine Patcher (from v1.1.0 on, including nightlies), so manual builds are rarely needed. See **[graphics-performance.md](graphics-performance.md#experimental-the-vulkan-renderer)**.

---

## Deploy Scripts

### `swap-into-crossover.sh` — deploy patched modules into CrossOver

Copies `/Applications/CrossOver.app` into a staging folder, swaps in the 3 patched modules (plus GPTK4's D3DMetal if `GPTK_DIR` is set), re-seals the bundle with an ad-hoc signature, verifies it, and moves it into place as `/Applications/CrossOver_Endfield_Patch.app`.

```bash

# Standard swap

./scripts/swap-into-crossover.sh

# Swap + install GPTK4 (mount the "Evaluation environment for Windows games…" DMG first)

GPTK_DIR="/Volumes/<mounted GPTK volume>/redist/lib/external" ./scripts/swap-into-crossover.sh

# Skip GPTK (rebuild with stock D3DMetal 3.0)

SKIP_GPTK=1 ./scripts/swap-into-crossover.sh

# Mod chain support only (no app rebuild)

MOD_CHAIN=1 SKIP_APP_PATCH=1 ./scripts/swap-into-crossover.sh
```text

**What the script does, in order:**

1. Copy CrossOver (must be 26.3) to a staging folder.
2. Swap the 3 patched modules (move originals aside as `.cxorig` backups).
3. Inject the `LC_RPATH` into `ntdll.so` so `cxcompatdb.so` → `libgnutls.30.dylib` → D3DMetal can load.
4. Re-seal the bundle ad-hoc (`codesign --force --sign - --preserve-metadata=entitlements`), keeping nested CodeWeavers signatures and entitlements.
5. Verify with `codesign --verify --deep --strict`.
6. Move the staged copy into `/Applications/`.

**Manual equivalent** (for auditing): see **[installation.md → Deploy into CrossOver — manual](installation.md#3-deploy-into-crossover-manual)**.

### `create-bottle.sh` — create the game bottle

Creates the `Arknights Endfield` bottle (Windows 11 64-bit) with D3DMetal + DLSS + MSync pre-configured.

```bash
./scripts/create-bottle.sh          # create a new bottle
UPDATE=1 ./scripts/create-bottle.sh # re-apply settings to an existing bottle
```bash

The script writes the bottle's `cxbottle.conf` keys:

```ini
[CX_GRAPHICS_BACKEND] = d3dmetal
[D3DM_ENABLE_METALFX] = 1
[WINEMSYNC]           = 1
```bash

See **[06-graphics-and-gptk.md](project-information/06-graphics-and-gptk.md#selecting-a-backend-in-crossover-26)** for the full key list.

### `launch-endfield.sh` — launch the game

Handles wineserver cleanup, graphics args, and debug logging.

```bash
./scripts/launch-endfield.sh                    # default: -force-d3d11
DEBUG=light ./scripts/launch-endfield.sh        # Wine errors only (cheap to leave on)
DEBUG=1 ./scripts/launch-endfield.sh            # full CrossOver log (heavy)
GFXARGS="-force-vulkan" ./scripts/launch-endfield.sh  # experimental Vulkan renderer
```bash

**Why not `WINEDEBUG`?** CrossOver's `bin/wine` is a Perl wrapper that overwrites the exported `WINEDEBUG` variable with its own channel list. Pass channels with `--debugmsg` instead (e.g. `--debugmsg err+all`).

See **[installation.md → Debug logging](installation.md#debug-logging)**.

---

## The FineWine Patcher.app

The GUI equivalent of the shell scripts, built with Swift Package Manager. It performs the same module swap + re-seal without requiring developer tools.

| Component | Role |
|---|---|
| `patcher-app/Sources/FineWinePatcher/PatcherEngine.swift` | Orchestrates the patch phases (app-bundle steps + optional bottle-side mod chain). |
| `patcher-app/Sources/FineWinePatcher/ContentView.swift` | UI: module swap, GPTK4 step, mod chain group box. |
| `patcher-app/Sources/FineWinePatcher/ModChain.swift` | Mod-chain logic: `ChainBackend`, `BottleInfo`, `ChainPlan`, `ModChain`. |
| `patcher-app/Tests/FineWinePatcherTests/` | 9 tests: apply/re-apply idempotence, byte-identical revert, CRLF preservation, backend parsing. |

**Phases:**

1. **App patch** — module swap + re-seal (mirrors `swap-into-crossover.sh`).
2. **Mod chain (EFMI)** — optional bottle-side phase that configures 3DMigoto's `proxy_d3d11` chain (see below).

**Licensing guardrail:** the app **never bundles CrossOver DLLs** in `Resources/payload/`; the chain target `d3d11.dll` is always read from the user's own CrossOver install at patch time. See **[patcher-app/README.md](../patcher-app/README.md)**.

---

## Mod Injection Chain Support (`proxy_d3d11`)

This repository supports modding via the **XXMI Launcher / EFMI** (3DMigoto-based model-import tooling) through a bottle-side configuration step — **no Wine patches required**. The mod platform's `d3d11.dll` must be layered **in front of** CrossOver's D3D→Metal mapping, and the ordering lever already exists inside 3DMigoto: `[System] proxy_d3d11` in `d3dx.ini`.

### Why this is needed

3DMigoto resolves its "original" `d3d11.dll` as a **full path to `C:\Windows\system32\d3d11.dll`**, which under CrossOver is the **wined3d** copy on every backend — so its chain silently terminates at wined3d instead of Metal. The fix is to point `proxy_d3d11` at CrossOver's backend `d3d11.dll` (DXMT's or the D3DMetal shim), which attaches to the already-loaded backend module by file identity:

```text
game call → mod d3d11 (hooks) → backend d3d11 (already loaded, same file) → Metal
```text

See **[07-load-ordering-and-chaining.md](mod-injection/07-load-ordering-and-chaining.md)** for the full resolution-rule analysis.

### How to enable it

**Via the Patcher.app:** a "Mod chain (EFMI)" group box appears when a bottle with an installed EFMI is detected.

```text
┌ Mod chain (EFMI) ────────────────────────────────────────────┐
│  Bottle:      [ Arknights Endfield ▾ ]      backend: dxmt    │
│  EFMI folder: ~/…/XXMI Launcher/EFMI   [Choose…]             │
│  ☑ Chain EFMI's d3d11 to CrossOver's d3d11 (proxy_d3d11)     │
│     target: lib\dxmt\x86_64-windows\d3d11.dll                │
│  [ Apply ]  [ Revert ]                                       │
└──────────────────────────────────────────────────────────────┘
```bash

**Via the shell script:** opt-in with flags or env vars (a flag wins):

```bash
MOD_CHAIN=1 \
  MOD_BOTTLE="Arknights Endfield" \
  MOD_APP="$DEST_APP" \
  MOD_CHAIN_MODE="path|copy" \
  ./scripts/swap-into-crossover.sh
```bash

Or run the mod step alone (no app rebuild):

```bash
MOD_CHAIN=1 SKIP_APP_PATCH=1 ./scripts/swap-into-crossover.sh
```bash

### What the step does

1. Resolve the backend → chain target path (table below) + verify the file exists in the patched app.
2. Locate the EFMI folder (`d3dx.ini` present).
3. Backup `d3dx.ini` → `d3dx.ini.cxorig`

Clone this wiki locally