docs: update SWBF2 guide with direct-exe bypass and Origin registry spoof

Revised approach: skip link2ea:// and EA App account requirement entirely.
Launch starwarsbattlefrontii.exe directly via the wrapper, spoof Origin
registry entries so the game's built-in launcher check passes. Documents
what doesn't work and why (msiexec/bwrap/EA account issues).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs
This commit is contained in:
Claude
2026-06-22 04:25:26 +00:00
parent 0202515163
commit 6b385e62ba
+148 -147
View File
@@ -9,11 +9,13 @@
## Why this is hard
SWBF2 (2017) is an EA game. EA mandated their **EA App** launcher for all EA Steam titles.
On launch, SWBF2 calls `ShellExecuteW("link2ea://launchgame/1237950?platform=steam&theme=swbfii")`.
GE-Proton's `steam.exe` intercepts that URL, finds `Link2EA.exe` from the EA Desktop install, and
hands off to the EA App which then launches the actual game.
On launch, Steam passes `link2ea://launchgame/1237950?platform=steam&theme=swbfii` as the
"executable" to GE-Proton. The game binary itself also checks for Origin/EA App in the
registry before starting.
Without EA Desktop installed in the Wine prefix, the game exits immediately.
EA App requires an EA account login, and ea.com/signup has been unreliable. The workaround
here **bypasses EA App entirely**: launch the game executable directly and spoof the Origin
registry entries so the game thinks its launcher is installed.
---
@@ -35,214 +37,213 @@ Only Steam itself can properly launch programs through sniper+GE-Proton.
---
## Solution: Steam launch wrapper
## Solution overview
Use SWBF2's **Steam launch options** to intercept the launch, run a helper script,
then pass control back to Steam's real launch chain.
1. Intercept the Steam launch with a wrapper script (via launch options)
2. Spoof Origin registry entries so SWBF2 thinks its launcher is present
3. Skip link2ea:// entirely — launch `starwarsbattlefrontii.exe` directly
Steam passes all its args to the wrapper as `$@`:
```
steam-launch-wrapper -- reaper SteamLaunch AppId=1237950 -- <sniper> --verb=waitforexitandrun -- <proton> waitforexitandrun <game>
```
The wrapper runs msiexec (or anything else) inside the full sniper+GE-Proton environment
that Steam set up, then `exec "$@"` to hand off to the game.
No EA account needed. No EA App login needed.
---
## Step-by-step
### 1. Lock localconfig.vdf against Steam overwriting it
Steam uses atomic rename (write temp + rename) when saving config.
`chmod 444` on the file is bypassed. Lock the **directory** instead:
### 1. Find the WolfSteam container name and Steam user ID
```bash
WOLF_CONTAINER="WolfSteam_XXXXXXXXXXXXXXXX" # docker ps to find exact name
CONFIG_DIR="/home/retro/.steam/steam/userdata/XXXXXXXXX/config"
WOLF_CONTAINER=$(docker ps --format '{{.Names}}' | grep Wolf)
echo "$WOLF_CONTAINER"
# Find container name
docker ps --format '{{.Names}}' | grep Wolf
STEAM_UID=$(docker exec "$WOLF_CONTAINER" ls /home/retro/.steam/steam/userdata/)
echo "$STEAM_UID"
# Find Steam user ID
docker exec "$WOLF_CONTAINER" ls /home/retro/.steam/steam/userdata/
CONFIG_DIR="/home/retro/.steam/steam/userdata/$STEAM_UID/config"
```
### 2. Lock localconfig.vdf against Steam overwriting it
Steam uses atomic rename (write temp + rename) when saving config.
`chmod 444` on the file is bypassed by the rename. Lock the **directory** instead:
```bash
docker exec "$WOLF_CONTAINER" chmod 555 "$CONFIG_DIR"
```
### 2. Set SWBF2 launch options
### 3. Set SWBF2 launch options in localconfig.vdf
Edit localconfig.vdf **while Steam is not running** (or kill Steam first):
Kill Steam first so it flushes and releases the file, then edit:
```bash
CONFIG_FILE="$CONFIG_DIR/localconfig.vdf"
# Kill Steam so it flushes and stops watching
docker exec "$WOLF_CONTAINER" pkill -f steam.sh || true
sleep 3
# Edit — find the LaunchOptions line for AppID 1237950
# Add before it if missing:
docker exec "$WOLF_CONTAINER" bash -c "
sed -i '/\"1237950\"/,/}/ s/\"LaunchOptions\"\s*\"[^\"]*\"/\"LaunchOptions\"\t\t\"STEAM_UNIX_SOCKET=\/tmp\/steam.sock \/home\/retro\/ea_install.sh %command%\"/' '$CONFIG_FILE'
"
# Or if LaunchOptions key doesn't exist yet, add it manually.
# Verify:
docker exec "$WOLF_CONTAINER" grep -A5 '"1237950"' "$CONFIG_FILE" | grep LaunchOptions
CONFIG_FILE="$CONFIG_DIR/localconfig.vdf"
# The file needs a LaunchOptions entry under AppID 1237950.
# Find the block and add/replace LaunchOptions:
docker exec "$WOLF_CONTAINER" grep -A5 '"1237950"' "$CONFIG_FILE"
# If LaunchOptions is missing, add it manually to the 1237950 block:
# "LaunchOptions" "STEAM_UNIX_SOCKET=/tmp/steam.sock /home/retro/ea_install.sh %command%"
```
### 3. Write the EA install wrapper
The launch option value must be:
```
STEAM_UNIX_SOCKET=/tmp/steam.sock /home/retro/ea_install.sh %command%
```
The wrapper runs `msiexec /a` (administrative install — no custom actions) to extract
EA Desktop files, then execs the real game:
### 4. Write the Origin registry spoof
SWBF2's binary checks for Origin in the registry before starting. Spoof it by writing
directly to Wine's system.reg (plain text file — no Wine needed):
```bash
SYSREG="/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/system.reg"
TIMESTAMP=$(date +%s)
docker exec "$WOLF_CONTAINER" bash -c "cat >> '$SYSREG' << 'REGEOF'
[Software\\\\Origin] $TIMESTAMP
\"ClientPath\"=\"C:\\\\\\\\Program Files\\\\\\\\Electronic Arts\\\\\\\\EA Desktop\\\\\\\\EA Desktop\\\\\\\\EADesktop.exe\"
\"InstallDir\"=\"C:\\\\\\\\Program Files\\\\\\\\Electronic Arts\\\\\\\\EA Desktop\\\\\\\\EA Desktop\\\\\\\\\"
[Software\\\\Wow6432Node\\\\Origin] $TIMESTAMP
\"ClientPath\"=\"C:\\\\\\\\Program Files\\\\\\\\Electronic Arts\\\\\\\\EA Desktop\\\\\\\\EA Desktop\\\\\\\\EADesktop.exe\"
\"InstallDir\"=\"C:\\\\\\\\Program Files\\\\\\\\Electronic Arts\\\\\\\\EA Desktop\\\\\\\\EA Desktop\\\\\\\\\"
REGEOF"
# Verify
docker exec "$WOLF_CONTAINER" grep -A3 'Software\\\\Origin' "$SYSREG"
```
### 5. Write the launch wrapper
The wrapper receives Steam's full launch chain as `$@` (args 111 are the
sniper+GE-Proton preamble, arg 12 is normally `link2ea://...`). Replace the last
arg with the actual game executable:
```bash
cat > /tmp/ea_install.sh << 'EOF'
#!/bin/bash
echo "=== ea_install.sh called $(date) ===" >> /tmp/ea_install.log
echo "Args: $@" >> /tmp/ea_install.log
"$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" \
msiexec /i "C:\\ea_app.msi" /qn \
TARGETDIR="C:\\Program Files\\Electronic Arts" \
/l*v "C:\\msi_admin.log" \
>> /tmp/ea_install.log 2>&1
echo "MSI exit: $?" >> /tmp/ea_install.log
exec "$@"
echo "=== launch $(date) ===" >> /tmp/ea_install.log
GAME_EXE="/home/retro/.steam/steam/steamapps/common/STAR WARS Battlefront II/starwarsbattlefrontii.exe"
exec "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" "$GAME_EXE"
EOF
chmod +x /tmp/ea_install.sh
docker cp /tmp/ea_install.sh "$WOLF_CONTAINER":/home/retro/ea_install.sh
```
### 4. Get the EA App MSI
### 6. Launch SWBF2 in Moonlight
The MSI ships inside SWBF2's game files. Copy it into the Wine prefix:
Steam → wrapper → `starwarsbattlefrontii.exe` runs directly inside sniper+GE-Proton.
The game checks Origin registry, finds the spoofed entries, and starts.
You may see Vulkan shader compilation prompts on first launch — these can be skipped;
the game will still proceed (shaders compile lazily or are skipped).
### 7. Restore clean state (after confirmed working)
```bash
MSI_SRC="/home/retro/.steam/steam/steamapps/common/STAR WARS Battlefront II/__Installer/Origin/redist/internal/EAappInstaller.exe"
# EAappInstaller.exe is a WiX Burn bootstrapper — the real MSI is buried inside.
# Easier: use the MSI that gets extracted to the Wine prefix during a first launch attempt:
MSI_IN_PREFIX="/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/ea_app.msi"
# Restore directory to writable so Steam can save normally
docker exec "$WOLF_CONTAINER" chmod 755 "$CONFIG_DIR"
# Remove LaunchOptions for 1237950 from localconfig.vdf while Steam is stopped
```
The MSI appears in the Wine prefix (`drive_c/ea_app.msi`, ~227 MB) after SWBF2 is launched
once — GE-Proton's protonfixes or steam.exe drops it there.
---
### 5. Extract EA Desktop files on the host (bypassing Wine custom actions)
## Background: what Steam actually passes to the wrapper
The MSI's `JunoConfigureRegistry` custom action fails in Wine (it tries to set registry
ACLs via `SetSecurityDescriptorSddlForm` which Wine doesn't support). This causes
MSI rollback and removes all installed files. Bypass it by extracting on Linux:
```
$1 = /path/to/steam-launch-wrapper
$2 = --
$3 = /path/to/reaper
$4 = SteamLaunch
$5 = AppId=1237950
$6 = --
$7 = /path/to/SteamLinuxRuntime_sniper/_v2-entry-point
$8 = --verb=waitforexitandrun
$9 = --
$10 = /path/to/GE-Proton10-34/proton
$11 = waitforexitandrun
$12 = link2ea://launchgame/1237950?platform=steam&theme=swbfii ← we replace this
```
---
## What doesn't work (and why)
### Running msiexec / Wine via docker exec
`docker exec` carries Linux capabilities that break bwrap. Any attempt to run
Proton/wine64 outside Steam's own launch chain gets:
```
setting up uid map: Permission denied
```
### Running the EA App MSI through Wine (INST-14-1603)
The MSI's `JunoConfigureRegistry` .NET custom action uses `SetSecurityDescriptorSddlForm`
to set registry ACLs. Wine doesn't support this — the action returns 0 (failure),
causing the entire install to roll back. `DISABLEROLLBACK=1` doesn't help because
MSI logs the product as already-registered from the first attempt, causing subsequent
runs to produce an empty log and exit.
### link2ea:// approach with EA account
EA App requires an EA account linked to your Steam account. `ea.com/signup` is
unreliable. Even with an account, EA Desktop must be installed in the Wine prefix
(see msiextract bypass below if needed). Bypassing the game exe directly is simpler.
### msiextract bypass (if EA App files are needed for something else)
If you need EA Desktop files in the prefix for another reason:
```bash
# Copy MSI out of container
# Copy MSI out of container (appears in drive_c after first launch attempt)
docker cp "$WOLF_CONTAINER:/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/ea_app.msi" \
/tmp/ea_app.msi
# Extract (msitools package)
# Extract on host (bypasses JunoConfigureRegistry entirely)
sudo apt-get install -y msitools
mkdir -p /tmp/ea_app_extracted
msiextract -C /tmp/ea_app_extracted /tmp/ea_app.msi
```
### 6. Copy EA Desktop into the Wine prefix
The MSI installs to `C:\Program Files\Electronic Arts\EA Desktop\` with a versioned
subdirectory (`14.2.0.3345`) and a symlink `EA Desktop -> 14.2.0.3345`.
The normal MSI install creates that symlink via `JunoSetupSymlinkMode`. After a
failed install + rollback, the symlink may remain but the versioned dir is gone.
```bash
PFXBASE="/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/Program Files/Electronic Arts/EA Desktop"
# Remove any broken symlink
docker exec "$WOLF_CONTAINER" bash -c "
[ -L '$PFXBASE/EA Desktop' ] && rm '$PFXBASE/EA Desktop'
mkdir -p '$PFXBASE/14.2.0.3345'
ln -s 14.2.0.3345 '$PFXBASE/EA Desktop'
"
# Copy extracted files into versioned directory
docker cp "/tmp/ea_app_extracted/Electronic Arts/EA Desktop/EA Desktop/." \
"$WOLF_CONTAINER:$PFXBASE/14.2.0.3345/"
# Verify
docker exec "$WOLF_CONTAINER" find "$PFXBASE" -name "Link2EA.exe"
```
Expected output:
```
/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/Program Files/Electronic Arts/EA Desktop/14.2.0.3345/Link2EA.exe
```
### 7. Switch wrapper to pass-through and launch
Once EA Desktop files are in place, update the wrapper to just exec the game:
```bash
cat > /tmp/ea_passthrough.sh << 'EOF'
#!/bin/bash
echo "=== launch $(date) ===" >> /tmp/ea_install.log
exec "$@"
EOF
chmod +x /tmp/ea_passthrough.sh
docker cp /tmp/ea_passthrough.sh "$WOLF_CONTAINER":/home/retro/ea_install.sh
```
Launch SWBF2 in Moonlight. Steam → GE-Proton → SWBF2 → link2ea:// → steam.exe
finds Link2EA.exe → EA App launches → SWBF2 loads.
### 8. EA account login (one-time)
On first launch, EA Desktop will show a login screen. Log in with your EA account.
Credentials are cached; subsequent launches skip login.
### 9. Restore clean state
After EA Desktop is confirmed working, remove the launch wrapper:
```bash
# Restore directory to writable
docker exec "$WOLF_CONTAINER" chmod 755 "$CONFIG_DIR"
# Remove LaunchOptions from localconfig.vdf
# (edit the file while Steam is stopped, remove the LaunchOptions line for 1237950)
```
---
## Troubleshooting
### "link2ea not found" / game exits in ~6 seconds
EA Desktop files are missing or in wrong path. Check:
### Game exits in ~6 seconds, no popup
Check wrapper log inside container:
```bash
docker exec "$WOLF_CONTAINER" find \
"/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/Program Files/Electronic Arts" \
-name "Link2EA.exe"
docker exec "$WOLF_CONTAINER" bash -c "
cat /tmp/ea_install.log | tail -20
find /home/retro/.steam/steam/steamapps/compatdata/1237950/ -name '*.log' \
-newer /tmp/ea_install.log 2>/dev/null | xargs tail -20 2>/dev/null
"
```
### "Origin is not installed" popup
Origin registry entries missing. Repeat step 4 and verify:
```bash
docker exec "$WOLF_CONTAINER" grep -A3 'Software\\\\Origin' \
'/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/system.reg'
```
### Wrapper never runs (Steam ignores launch options)
Steam wrote config before chmod 555 was applied. Kill Steam, chmod 555 the directory,
then edit localconfig.vdf. Steam reads config fresh on startup.
### INST-14-1603 error in EA App installer
`JunoConfigureRegistry` .NET custom action returns 0 in Wine. This is a Wine limitation
with registry ACL operations. Use the `msiextract` bypass (Step 56) instead of
running the MSI through Wine.
### msiexec DISABLEROLLBACK=1 produces empty log
MSI detected a stale product registration from a previous failed install.
Check: `grep -i C2622085 /home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/system.reg`
If present, delete those registry keys then retry.
### wine64 segfaults outside Steam (exit 139)
GE-Proton10's wine64 requires the sniper runtime. Never call it directly from
`docker exec` — always use Steam's launch chain via the wrapper approach.
Steam wrote config before chmod 555 was applied. Kill Steam, verify chmod 555 is set
on the config directory, edit localconfig.vdf, then start Steam again.
### localconfig.vdf gets overwritten on Steam shutdown
Steam uses atomic rename (temp file + rename into place). `chmod 444` on the file
is bypassed by the rename. Solution: `chmod 555` on the **directory** containing
localconfig.vdf — this prevents new files being renamed into the directory.
`chmod 555` on the directory (not the file) prevents the atomic rename Steam uses.
### wine64 segfaults outside Steam (exit 139)
GE-Proton10's wine64 requires the sniper runtime. Never call it via `docker exec`.
---
@@ -251,9 +252,9 @@ localconfig.vdf — this prevents new files being renamed into the directory.
| Path | Description |
|------|-------------|
| `/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/` | SWBF2 Wine prefix |
| `/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/ea_app.msi` | EA App MSI (~227 MB) |
| `/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/Program Files/Electronic Arts/EA Desktop/14.2.0.3345/` | EA Desktop files |
| `/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/Program Files/Electronic Arts/EA Desktop/EA Desktop` | Symlink → `14.2.0.3345` |
| `/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/system.reg` | Wine HKLM registry (plain text) |
| `/home/retro/.steam/steam/steamapps/compatdata/1237950/pfx/drive_c/ea_app.msi` | EA App MSI (~227 MB, appears after first launch) |
| `/home/retro/.steam/steam/steamapps/common/STAR WARS Battlefront II/starwarsbattlefrontii.exe` | Actual game binary |
| `/home/retro/.steam/steam/userdata/<uid>/config/localconfig.vdf` | Steam per-user config (launch options) |
| `/home/retro/ea_install.sh` | Launch wrapper script |
| `/tmp/ea_install.log` | Wrapper log (inside container) |
| `/tmp/ea_install.log` | Wrapper log (inside container, resets on container restart) |