wolf: fix ES-DE ROMDirectory staying blank despite the self-healing patch

Root-caused against the user's own live output: the settings file had
<string name="ROMDirectory" value="" /> — the key was present, just
empty. The self-healing check only tested whether the string
"ROMDirectory" appeared anywhere in the file, so a key that exists with
a blank value (ES-DE can write this itself if its own first-run "select
ROM directory" step goes unanswered in a headless Moonlight session,
saving an empty path back over GOW's template) looked "already
populated" and got skipped — leaving ROM discovery broken even after
the fix had run.

Now ROMDirectory is independently forced to /ROMs whenever its value is
blank, on top of (not instead of) the existing RunInBackground patch —
no longer gated on the key's mere presence. Verified against the exact
reported bug (ROMDirectory present but blank) plus the existing missing-
file, already-correct, and idempotent-rerun scenarios in isolated /tmp
harnesses before touching the real script.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
This commit is contained in:
Claude
2026-09-03 18:57:29 +00:00
parent a212be09c3
commit cbfc28c2dd
+35 -15
View File
@@ -1604,13 +1604,28 @@ GOW_ESDE_SETTINGS_TEMPLATE = """<?xml version="1.0"?>
<string name="UserThemeDirectory" value="" /> <string name="UserThemeDirectory" value="" />
""" """
if 'ROMDirectory' not in content: if not content.strip():
content = GOW_ESDE_SETTINGS_TEMPLATE content = GOW_ESDE_SETTINGS_TEMPLATE
else: else:
content = re.sub(r'[ \t]*<bool name="RunInBackground" value="[^"]*"[ \t]*/>[ \t]*\n?', '', content) content = re.sub(r'[ \t]*<bool name="RunInBackground" value="[^"]*"[ \t]*/>[ \t]*\n?', '', content)
line = '<bool name="RunInBackground" value="false" />\n' line = '<bool name="RunInBackground" value="false" />\n'
content = content.rstrip('\n') + '\n' + line content = content.rstrip('\n') + '\n' + line
# CONFIRMED LIVE: a file can already contain a ROMDirectory *key* while
# its value is blank (e.g. ES-DE's own first-run "select ROM directory"
# step got skipped/cancelled in a headless Moonlight session and ES-DE
# saved that empty value back over GOW's own template) — checking only
# whether the key exists, as an earlier version of this fix did, treated
# that as "already populated" and left the blank path in place, so ES-DE
# kept finding zero games even though the fix had technically "run".
# Force it to /ROMs (the one true value in this container — see the
# 'roms:/ROMs' mount on every app that reads ROMs) whenever it's missing
# or empty, independent of whether RunInBackground needed touching.
m = re.search(r'<string name="ROMDirectory" value="([^"]*)"', content)
if not m or not m.group(1).strip():
content = re.sub(r'[ \t]*<string name="ROMDirectory" value="[^"]*"[ \t]*/>[ \t]*\n?', '', content)
content = content.rstrip('\n') + '\n' + '<string name="ROMDirectory" value="/ROMs" />\n'
with open(path, 'w') as f: with open(path, 'w') as f:
f.write(content) f.write(content)
ESDESETTINGSPY ESDESETTINGSPY
@@ -4790,21 +4805,26 @@ default behavior here.
It writes \`esde-settings/es_settings.xml\` directly from GOW's own real 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 template (100+ settings, fetched from GOW's own repo, byte-for-byte
identical except \`RunInBackground\` flipped to off) whenever that file is identical except \`RunInBackground\` flipped to off) whenever that file is
either missing or still the old broken placeholder — detected by checking missing or empty. If the file already has real content, only the one
for \`ROMDirectory\`, a key only GOW's own bootstrap or ES-DE itself ever \`RunInBackground\` line is patched, leaving every other setting (theme,
writes. If the file already has a real, fully-populated value (because scraper prefs, etc.) untouched — **except \`ROMDirectory\`, which is always
EmulationStation has already launched at least once and GOW wrote its own independently forced to \`/ROMs\` whenever its value is blank**, regardless
copy), only the one \`RunInBackground\` line is patched, leaving every of whether the rest of the file looks populated. Either way, one
other setting (theme, scraper prefs, etc.) untouched. Either way, one
\`sudo ./setup.sh wolf\` run is enough — no need to connect via Moonlight \`sudo ./setup.sh wolf\` run is enough — no need to connect via Moonlight
first. **Confirmed live, the hard way, twice:** earlier versions of this first. **Confirmed live, the hard way, three times:** earlier versions of
fix either pre-created a minimal stub that made GOW skip writing its own this fix (1) pre-created a minimal stub that made GOW skip writing its own
template (breaking ROM discovery on every system, since the ROM path fell template, breaking ROM discovery on every system since the ROM path fell
back to something other than \`/ROMs\`), or only patched an already-existing back to something other than \`/ROMs\`; (2) only patched an already-existing
file and left a genuinely missing/broken one alone, requiring exactly the file and left a genuinely missing/broken one alone, requiring a manual
manual Moonlight-connect step this version removes. Setting it by hand Moonlight-connect step; and (3) treated the mere *presence* of the
still works too, any time: **Main Menu → Other Settings → Run in \`ROMDirectory\` key as proof the file was already correctly populated —
background (while game is launched) → off**. but ES-DE can write that key with a **blank value** (its own first-run
"select ROM directory" step going unanswered in a headless Moonlight
session saves an empty path back over GOW's template), which the presence
check alone couldn't tell apart from a real one. Setting it by hand still
works too, any time: **Main Menu → Other Settings → Run in background
(while game is launched) → off**, and the ROM directory under **Main Menu
→ Other Settings → ROM directory → \`/ROMs\`** if it's ever blank again.
**Why this needed its own mount, not just a one-time write:** \`~/ES-DE\` **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 (settings, gamelists, scraped artwork, logs) had no bind mount onto the