Skip to content

Instantly share code, notes, and snippets.

@tomoyoirl
Last active April 23, 2026 03:08
Show Gist options
  • Select an option

  • Save tomoyoirl/dd1d0878bf1fc9e43356b24a137f02b0 to your computer and use it in GitHub Desktop.

Select an option

Save tomoyoirl/dd1d0878bf1fc9e43356b24a137f02b0 to your computer and use it in GitHub Desktop.
Advanced Fly By Wire fix for OSX / thank you Claude

KSP Advanced Fly By Wire — macOS Fix

Problem

On Apple Silicon Macs, KSP runs under Rosetta 2 (x86_64). The Advanced Fly By Wire mod depends on two native libraries that are not automatically available on macOS:

  • libSDL2-2.0.0 — used for joystick/controller input
  • XInputInterface — the native layer under XInputDotNetPure.dll, a Windows XInput wrapper

Both cause DllNotFoundException spam in the Unity log, and the AFBW config window appears empty because controller enumeration crashes on every frame.

The symlink approach (dropping dylibs next to the KSP binary) is not sufficient on its own because Unity's embedded Mono resolves native library names through its own dllmap config, not just the filesystem search path.

Solution Overview

  1. Install an x86_64 build of SDL2 via the Rosetta Homebrew prefix at /usr/local
  2. Build a stub XInputInterface dylib (x86_64) that returns "no controller" — this lets Mono load the library successfully so the mod falls through to its SDL2 code path
  3. Register both libraries in Unity's embedded Mono dllmap config so they are found by name

Prerequisites

  • Homebrew installed for x86_64 (Rosetta) at /usr/local/bin/brew
    • If not present, install it:
      arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
      
  • Xcode Command Line Tools (for clang):
    xcode-select --install
    

Step 1: Install x86_64 SDL2

arch -x86_64 /usr/local/bin/brew install sdl2

Verify it is the right architecture:

file /usr/local/lib/libSDL2-2.0.0.dylib
# should say: Mach-O 64-bit dynamically linked shared library x86_64

Step 2: Build and install the XInputInterface stub

The stub exports XInputGamePadGetState and XInputGamePadSetState — the only two symbols XInputDotNetPure.dll imports. GetState returns ERROR_DEVICE_NOT_CONNECTED (1167) so AFBW sees no XInput controllers and falls back to SDL2.

From this directory:

make
make install

make install copies xinputinterface.dylib to:

/Users/mtspaces/Library/Application Support/Steam/steamapps/common/Kerbal Space Program/KSP.app/Contents/MacOS/

Step 3: Register libraries in Unity's Mono dllmap config

Edit this file:

/Users/mtspaces/Library/Application Support/Steam/steamapps/common/Kerbal Space Program/KSP.app/Contents/MonoBleedingEdge/etc/mono/config

Add the following lines inside the <configuration> block (before the closing tag):

<dllmap dll="XInputInterface" target="/Users/mtspaces/Library/Application Support/Steam/steamapps/common/Kerbal Space Program/KSP.app/Contents/MacOS/xinputinterface.dylib" os="osx"/>
<dllmap dll="libSDL2-2.0.0" target="/usr/local/lib/libSDL2-2.0.0.dylib" os="osx"/>
<dllmap dll="SDL2" target="/usr/local/lib/libSDL2-2.0.0.dylib" os="osx"/>

Notes

  • The arm64 SDL2 at /opt/homebrew will NOT work — KSP is x86_64 and cannot load arm64 dylibs.
  • If KSP is updated via Steam it may overwrite the Mono config. Re-apply Step 3 if AFBW breaks again after an update.
  • The stub dylib in this repo only needs to be rebuilt if you wipe the KSP install. The Makefile install target handles placement.
KSP_MACOS = $(HOME)/Library/Application Support/Steam/steamapps/common/Kerbal Space Program/KSP.app/Contents/MacOS
xinputinterface.dylib: xinputinterface.c
arch -x86_64 clang -arch x86_64 -dynamiclib -o $@ $<
install: xinputinterface.dylib
cp $< "$(KSP_MACOS)/$<"
clean:
rm -f xinputinterface.dylib
#include <stdint.h>
#include <string.h>
typedef struct {
uint32_t dwPacketNumber;
uint16_t wButtons;
uint8_t bLeftTrigger;
uint8_t bRightTrigger;
int16_t sThumbLX;
int16_t sThumbLY;
int16_t sThumbRX;
int16_t sThumbRY;
} XInputState;
/* Returns ERROR_DEVICE_NOT_CONNECTED so the mod skips XInput and falls back to SDL2 */
int XInputGamePadGetState(int playerIndex, XInputState* state) {
if (state) memset(state, 0, sizeof(XInputState));
return 1167; /* ERROR_DEVICE_NOT_CONNECTED */
}
int XInputGamePadSetState(int playerIndex, float leftMotor, float rightMotor) {
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment