Strike Fighters / Wings Over Vietnam / Wings Over Europe / Wings Over Israel with ControllerBuddy on Linux
This guide describes the installation of Strike Fighters, Wings Over Vietnam, Wings Over Europe, and Wings Over Israel for use with ControllerBuddy on Linux via the Proton compatibility layer.
What you get with this setup:
- An installation of your chosen game that is nicely integrated into your Steam library.
- ControllerBuddy will start automatically when you start the game, load the correct profile, and exit when you quit the game.
- Steam (distribution package)
- protontricks Flatpak
- ControllerBuddy Flatpak
- Game installation media or installer (
Setup.exe)
Important
Before starting with the steps, make sure to read the Important Notes section in the README of this repository.
-
Add
Setup.exeas a Non-Steam game. -
Rename the setup.exe Steam shortcut to Strike Fighters, Wings Over Vietnam, Wings Over Europe, or Wings Over Israel accordingly.
-
Select Proton 9.0-4 as compatibility tool.
-
Launch the game Steam shortcut and install the game.
-
Obtain the
APP_IDof the Proton prefix:flatpak run com.github.Matoking.protontricks -l
Important
All subsequent commands must be executed within the same shell session to retain the APP_ID environment variable. Replace <APP_ID> with the actual APP ID obtained in this step.
-
Download and install the corresponding September 2008 update from here:
flatpak run com.github.Matoking.protontricks -c 'wine <UPDATER>.exe' "$APP_ID"
-
Install directplay and powershell into the Proton prefix:
flatpak run com.github.Matoking.protontricks "$APP_ID" directplay powershell -
Make sure all your game controllers are connected.
-
Hide all game controllers from the Proton prefix, except for ControllerBuddy's UINPUT joystick device:
reg_file=$(mktemp -p '' joysticks-XXXX.reg) && python3 - <<'EOF' "$reg_file" && import ctypes import ctypes.util import sys sdl2_path = ctypes.util.find_library("SDL2") if not sdl2_path: raise RuntimeError("Could not find SDL2 library") sdl = ctypes.CDLL(sdl2_path) Uint16 = ctypes.c_uint16 Uint32 = ctypes.c_uint32 class SDL_JoystickGUID(ctypes.Structure): _fields_ = [("data", ctypes.c_uint8 * 16)] sdl.SDL_Init.argtypes = [ctypes.c_uint32] sdl.SDL_Init.restype = ctypes.c_int sdl.SDL_Quit.argtypes = [] sdl.SDL_Quit.restype = None sdl.SDL_NumJoysticks.argtypes = [] sdl.SDL_NumJoysticks.restype = ctypes.c_int sdl.SDL_JoystickNameForIndex.argtypes = [ctypes.c_int] sdl.SDL_JoystickNameForIndex.restype = ctypes.c_char_p sdl.SDL_JoystickGetDeviceGUID.argtypes = [ctypes.c_int] sdl.SDL_JoystickGetDeviceGUID.restype = SDL_JoystickGUID sdl.SDL_GetJoystickGUIDInfo.argtypes = [SDL_JoystickGUID, ctypes.POINTER(Uint16), ctypes.POINTER(Uint16), ctypes.POINTER(Uint16), ctypes.POINTER(Uint16)] sdl.SDL_GetJoystickGUIDInfo.restype = None SDL_INIT_JOYSTICK = 0x00002000 if sdl.SDL_Init(SDL_INIT_JOYSTICK) != 0: print("SDL_Init failed:", sdl.SDL_GetError().decode("utf-8")) sys.exit(1) # see: https://github.com/wine-mirror/wine/blob/master/dlls/hidclass.sys/device.c device_strings = { (0x045E, 0x028E): "Controller (XBOX 360 For Windows)", (0x045E, 0x028F): "Controller (XBOX 360 For Windows)", (0x045E, 0x02D1): "Controller (Xbox One For Windows)", (0x045E, 0x02DD): "Controller (Xbox One For Windows)", (0x045E, 0x02E3): "Controller (Xbox One For Windows)", (0x045E, 0x02EA): "Controller (Xbox One For Windows)", (0x045E, 0x02FD): "Controller (Xbox One For Windows)", (0x045E, 0x0719): "Controller (XBOX 360 For Windows)", (0x045E, 0x0B00): "Controller (Xbox One For Windows)", (0x045E, 0x0B05): "Controller (Xbox One For Windows)", (0x045E, 0x0B12): "Controller (Xbox One For Windows)", (0x045E, 0x0B13): "Controller (Xbox One For Windows)", (0x054C, 0x05C4): "Wireless Controller", (0x054C, 0x09CC): "Wireless Controller", (0x054C, 0x0BA0): "Wireless Controller", (0x054C, 0x0CE6): "Wireless Controller", (0x054C, 0x0DF2): "Wireless Controller", } count = sdl.SDL_NumJoysticks() joysticks = [] for i in range(count): name_ptr = sdl.SDL_JoystickNameForIndex(i) if not name_ptr: continue name = name_ptr.decode("utf-8") if name == "ControllerBuddy Joystick": continue guid = sdl.SDL_JoystickGetDeviceGUID(i) vendor = Uint16() product = Uint16() version = Uint16() crc16 = Uint16() sdl.SDL_GetJoystickGUIDInfo(guid, ctypes.byref(vendor), ctypes.byref(product), ctypes.byref(version), ctypes.byref(crc16)) key = (vendor.value, product.value) if key in device_strings: name = device_strings[key] joysticks.append(name) sdl.SDL_Quit() if not joysticks: print("Error: No joysticks detected on this system.", file=sys.stderr) sys.exit(1) def escape_reg_string(s: str) -> str: return s.replace('"', '""') print("Found the following joysticks:") for name in joysticks: print(f" {name}") if len(sys.argv) > 1: path = sys.argv[1] with open(path, "w", encoding="utf-16") as f: f.write("\ufeffWindows Registry Editor Version 5.00\n\n") f.write("[HKEY_CURRENT_USER\\Software\\Wine\\DirectInput\\Joysticks]\n") for name in joysticks: safe_name = escape_reg_string(name) f.write(f"\"{safe_name}\"=\"disabled\"\n") print(f"Wrote registry file: {path}") EOF flatpak run --filesystem="$reg_file":ro com.github.Matoking.protontricks -c "wine reg import '$reg_file'" "$APP_ID" rm -f "$reg_file"
Important
In the following steps the placeholders denoted by <...> must be replaced accordingly to the following table:
| Placeholder | Description |
|---|---|
<USER> |
Your username |
<APP_ID> |
The Proton prefix APP ID obtained in step 5 |
<GAME_FOLDER> |
The path to the game's installation folder |
<EXE> |
The game's executable name |
-
Configure the game to work with the
Strike_Fighters.jsonprofile from ControllerBuddy-Profiles:controller_buddy_profiles_dir=$(realpath -s "$(flatpak info -l de.bwravencl.ControllerBuddy)/../active/files/share/ControllerBuddy-Profiles") && cp "$controller_buddy_profiles_dir/configs/Strike_Fighters/Default.ini" "$HOME/.local/share/Steam/steamapps/compatdata/$APP_ID/pfx/drive_c/<GAME_FOLDER>/Controls/"
-
Update the Steam shortcut as follows:
TARGET:
"/home/<USER>/.local/share/Steam/steamapps/compatdata/<APP_ID>/pfx/drive_c/<GAME_FOLDER>/<EXE>.exe"START IN:
"/home/<USER>/.local/share/Steam/steamapps/compatdata/<APP_ID>/pfx/drive_c/<GAME_FOLDER>"LAUNCH OPTIONS:
"${STEAM_RUNTIME}"/scripts/switch-runtime.sh --runtime='' -- flatpak run de.bwravencl.ControllerBuddy -autostart local -profile /app/share/ControllerBuddy-Profiles/Strike_Fighters.json -tray & timeout=15; timeout "$timeout" bash -c 'until grep -q "ControllerBuddy Joystick" /proc/bus/input/devices ; do sleep 1 ; done' && %command% || { [ $? -eq 124 ] && zenity --error --text="Launch aborted because ControllerBuddy wasn't ready within $timeout seconds.\n\nCheck if your controller is connected." --width 500 ; } ; killall -q ControllerBuddy
To allow launching the game with ControllerBuddy from the Steam Deck's Gaming Mode, a custom second shortcut must be created. If the normal shortcut is used, ControllerBuddy will launch but the overlay will not be visible.
-
Create a new text file named
Strike_Fighters.shin your home directory with the following content:#!/bin/bash # replace placeholder values manually SteamAppId=<APP_ID> game_dir="$HOME/.local/share/Steam/steamapps/compatdata/$SteamAppId/pfx/drive_c/<GAME_FOLDER>" exe_file=<EXE>.exe proton_version='Proton 9.0 (Beta)' cb_profile=Strike_Fighters.json export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.steam/root/" export STEAM_COMPAT_DATA_PATH="$HOME/.local/share/Steam/steamapps/compatdata/$SteamAppId" flatpak run de.bwravencl.ControllerBuddy -autostart local -profile "/app/share/ControllerBuddy-Profiles/$cb_profile" -tray & trap 'killall -q ControllerBuddy' EXIT timeout=15 cb_device_name="ControllerBuddy Joystick" i=0 while ! grep -q "$cb_device_name" /proc/bus/input/devices do if ! pgrep -f 'flatpak-spawn --host /bin/bash -c FLATPAK_ID=de.bwravencl.ControllerBuddy' > /dev/null then (( i++ )) if [ "$i" -ge "$timeout" ] then zenity --error --text="Launch aborted because $cb_device_name wasn't ready within $timeout seconds.\n\nCheck if your controller is connected." --width 500 exit 1 fi fi sleep 1 done cd "$game_dir" && "$HOME/.local/share/Steam/ubuntu12_32/steam-launch-wrapper" -- \ "$HOME/.local/share/Steam/ubuntu12_32/reaper" SteamLaunch AppId="$SteamAppId" -- \ "$HOME/.local/share/Steam/steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point" --verb=waitforexitandrun -- \ "$HOME/.local/share/Steam/steamapps/common/$proton_version/proton" waitforexitandrun \ "$game_dir/$exe_file"
-
Replace the placeholders
<APP_ID>,<GAME_FOLDER>, and<EXE>in the script with their respective values. -
Make the script executable:
chmod +x "$HOME/Strike_Fighters.sh" -
Add the
Strike_Fighters.shlaunch script as a Non-Steam game to your Steam library. -
Rename the Strike_Fighters.sh Steam shortcut to Strike Fighters (Gaming Mode).
Important
The other Steam shortcut must not be deleted, as this would also delete the Proton prefix.
Important
Since the Steam Deck's controller hardware is exposed to games via Steam Input, even if you do not care for the touchpad controls, you must at least apply the default Steam Input layout called Gamepad With Camera Controls to the Strike Fighters (Gaming Mode) shortcut to ensure the controller can be detected by ControllerBuddy.
There is a special ControllerBuddy Steam Input controller layout available which configures the Steam Deck's touchpads to act as a mouse replacement.
| Control | Function |
|---|---|
| Right Touchpad | Move mouse cursor |
| Right Touchpad Click | Left mouse button |
| Left Touchpad Click | Right mouse button |
| Left Touchpad Y-Axis | Scroll up/down |
To use this layout:
-
Add the ControllerBuddy Steam Input layout to your Steam controller layouts:
xdg-open steam://controllerconfig/3259858387/3672925155
-
Apply the layout to both Strike Fighters shortcuts in your Steam library.