diff --git a/services/wolf.sh b/services/wolf.sh
index 7e34ae7..3de4e00 100644
--- a/services/wolf.sh
+++ b/services/wolf.sh
@@ -325,8 +325,8 @@ EOF
echo " and Wii U (Cemu) in ES-DE via a second, opt-in 'Alternative emulators' command"
echo " - Expose Wolf's REST API socket to the host (WOLF_SOCKET_PATH + /var/run/wolf mount)"
echo " so './manage.sh controllers' can force distinct pad types per controller slot"
- echo " - Mount ~/ES-DE/settings durably + force 'Run in background' off (only once"
- echo " GOW's own es_settings.xml template already exists — never pre-created)"
+ echo " - Mount ~/ES-DE/settings durably + force 'Run in background' off automatically"
+ echo " (self-healing — writes GOW's real template if missing/broken, no manual step)"
return 0
fi
@@ -1393,7 +1393,7 @@ PS2XMLPY
log_success "PS2: added PCEE2 as the default RetroArch core (custom_systems/es_systems.xml) — ES-DE's"
log_info "own build doesn't ship it yet (added upstream in the not-yet-released 3.5.0)"
- # ── ES-DE: force "Run in background" off (once GOW's own template exists) ──
+ # ── ES-DE: force "Run in background" off (self-healing, no manual step) ──
# GOW's own image bakes a FULL es_settings.xml template (100+ settings —
# confirmed against apps/es-de/build/configs/es/es_settings.xml in the
# games-on-whales/gow repo) that deliberately sets RunInBackground=true
@@ -1410,39 +1410,212 @@ PS2XMLPY
# ROMDirectory (GOW's template sets it to the /ROMs this whole repo
# mounts games at; ES-DE's own default is not that).
#
- # CONFIRMED LIVE, THE HARD WAY: an earlier version of this exact step
- # pre-created a MINIMAL stub file (containing only the RunInBackground
- # line) before the container had ever started — GOW's startup.sh saw
- # that file "already existing", skipped its own copy, and ES-DE's
- # ROMDirectory silently fell back to a default that isn't /ROMs, finding
- # zero games on every single system. Root-caused by walking GOW's own
- # startup.sh line by line against a real "ES-DE finds no game files"
- # report. Never pre-create this file — only ever patch an ALREADY
- # existing one (i.e. one GOW's own bootstrap has already populated in
- # full, meaning ES-DE has been launched via Moonlight at least once).
+ # CONFIRMED LIVE, THE HARD WAY (twice): an earlier version of this step
+ # pre-created a MINIMAL stub file before the container had ever started,
+ # which made GOW skip its own copy and broke ROM discovery on every
+ # system. A second attempt only ever patched an ALREADY non-empty file
+ # and left a pre-existing broken stub (or a genuinely missing file)
+ # alone, requiring a manual "connect once via Moonlight, then re-run
+ # setup" step — exactly the manual dependency this version removes. This
+ # embeds GOW's own real template verbatim (fetched directly from
+ # games-on-whales/gow:apps/es-de/build/configs/es/es_settings.xml, with
+ # only RunInBackground flipped true→false) and writes it whenever the
+ # on-disk file is missing OR is that same broken stub — detected by the
+ # absence of the ROMDirectory key, which only a real GOW-written or
+ # ES-DE-written file ever has — so this fix now works unconditionally,
+ # regardless of whether ES-DE has ever actually launched, with no
+ # dependency on connection order.
mkdir -p "$GAME_STORAGE_DIR/esde-settings"
_ESDE_SETTINGS="$GAME_STORAGE_DIR/esde-settings/es_settings.xml"
- if [ -s "$_ESDE_SETTINGS" ]; then
- backup_if_exists "$_ESDE_SETTINGS"
- python3 - "$_ESDE_SETTINGS" << 'ESDESETTINGSPY'
+ backup_if_exists "$_ESDE_SETTINGS"
+ python3 - "$_ESDE_SETTINGS" << 'ESDESETTINGSPY'
import re, sys
path = sys.argv[1]
-with open(path) as f:
- content = f.read()
-content = re.sub(r'[ \t]*[ \t]*\n?', '', content)
-line = '\n'
-content = content.rstrip('\n') + '\n' + line
+try:
+ with open(path) as f:
+ content = f.read()
+except FileNotFoundError:
+ content = ''
+
+GOW_ESDE_SETTINGS_TEMPLATE = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+if 'ROMDirectory' not in content:
+ content = GOW_ESDE_SETTINGS_TEMPLATE
+else:
+ content = re.sub(r'[ \t]*[ \t]*\n?', '', content)
+ line = '\n'
+ content = content.rstrip('\n') + '\n' + line
+
with open(path, 'w') as f:
f.write(content)
ESDESETTINGSPY
- chown "$ACTUAL_USER:$ACTUAL_USER" "$_ESDE_SETTINGS"
- log_success "ES-DE: 'Run in background (while game is launched)' forced off (esde-settings/es_settings.xml)"
- else
- log_info "ES-DE: 'Run in background' fix deferred — esde-settings/es_settings.xml doesn't"
- log_info "exist yet (GOW writes its real, full template the first time EmulationStation"
- log_info "actually launches). Connect to it via Moonlight once, then re-run"
- log_info "'sudo ./setup.sh wolf' to apply this fix without touching anything else."
- fi
+ chown "$ACTUAL_USER:$ACTUAL_USER" "$_ESDE_SETTINGS"
+ log_success "ES-DE: 'Run in background (while game is launched)' forced off (esde-settings/es_settings.xml)"
# ── App selection ─────────────────────────────────────────────────────────
echo ""
@@ -4373,26 +4546,25 @@ ES-DE's own *compiled* default for this is off — but GOW's own bundled
deliberately sets it **on**, which is the actual, confirmed source of the
default behavior here.
-**This installer forces it back off** by patching \`esde-settings/es_settings.xml\`
-— but only an *already-populated* copy of that file, never by creating one
-itself. That distinction matters: GOW's own container startup script only
-writes its real template (100+ settings, including where ES-DE looks for
-your ROMs) the very first time EmulationStation actually launches, gated
-on that file not already existing — pre-creating even a minimal version of
-it ourselves, before that first launch, would make GOW skip writing its
-own template entirely and leave ES-DE on vanilla upstream defaults for
-everything else in that file. **Confirmed live, the hard way:** an earlier
-version of this exact fix did precisely that, and ROM discovery broke
-completely (every system, not just one) because ES-DE's ROM path fell
-back to something other than \`/ROMs\`.
-
-So in practice: if you've already launched EmulationStation via Moonlight
-at least once, re-running \`sudo ./setup.sh wolf\` applies this immediately.
-**If you haven't yet** (a genuinely fresh install), the installer says so
-and skips the patch rather than guessing — connect to EmulationStation
-once first, then re-run \`sudo ./setup.sh wolf\`. Setting it by hand works
-too, any time: **Main Menu → Other Settings → Run in background (while
-game is launched) → off**.
+**This installer forces it back off automatically, with no manual step.**
+It writes \`esde-settings/es_settings.xml\` directly from GOW's own real
+template (100+ settings, fetched from GOW's own repo, byte-for-byte
+identical except \`RunInBackground\` flipped to off) whenever that file is
+either missing or still the old broken placeholder — detected by checking
+for \`ROMDirectory\`, a key only GOW's own bootstrap or ES-DE itself ever
+writes. If the file already has a real, fully-populated value (because
+EmulationStation has already launched at least once and GOW wrote its own
+copy), only the one \`RunInBackground\` line is patched, leaving every
+other setting (theme, scraper prefs, etc.) untouched. Either way, one
+\`sudo ./setup.sh wolf\` run is enough — no need to connect via Moonlight
+first. **Confirmed live, the hard way, twice:** earlier versions of this
+fix either pre-created a minimal stub that made GOW skip writing its own
+template (breaking ROM discovery on every system, since the ROM path fell
+back to something other than \`/ROMs\`), or only patched an already-existing
+file and left a genuinely missing/broken one alone, requiring exactly the
+manual Moonlight-connect step this version removes. Setting it by hand
+still works too, any time: **Main Menu → Other Settings → Run in
+background (while game is launched) → off**.
**Why this needed its own mount, not just a one-time write:** \`~/ES-DE\`
(settings, gamelists, scraped artwork, logs) had no bind mount onto the