From 412eda0369cb89c8fed871d46227bbe671c4fd68 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Tue, 17 Mar 2026 21:41:31 -0500 Subject: Fix Steam game mode: strip LD_PRELOAD, robust looksValid(), restore env for games Steam injects 32-bit gameoverlayrenderer.so via LD_PRELOAD which causes "wrong ELF class" errors for 64-bit Qt6 apps. Save and clear LD_PRELOAD in both launcher and AppRun scripts, restore it in ProtonLauncher for game processes. Also hardens looksValid() with QFileInfo::exists() for absolute paths (QDir::exists() has edge cases with relative subdirectory paths), adds case-insensitive directory walking fallback, and adds debug logging to both looksValid() and detectGame(). Adds ~/.steam/debian-installation to Python steam_utils.py candidates. Co-Authored-By: Claude Opus 4.6 --- docker/build-inner.sh | 10 +++++ libs/basic_games/steam_utils.py | 1 + libs/basic_games_native/src/basicgameplugin.cpp | 51 ++++++++++++++++++++++++- src/src/protonlauncher.cpp | 1 + 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/docker/build-inner.sh b/docker/build-inner.sh index 484412b..2d32e31 100755 --- a/docker/build-inner.sh +++ b/docker/build-inner.sh @@ -362,10 +362,16 @@ HERE="$(cd "$(dirname "$SELF")" && pwd)" # Without this, our bundled LD_LIBRARY_PATH leaks into game processes and # causes library conflicts. export FLUORINE_ORIG_LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}" +export FLUORINE_ORIG_LD_PRELOAD="${LD_PRELOAD:-}" export FLUORINE_ORIG_PATH="${PATH}" export FLUORINE_ORIG_XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" export FLUORINE_ORIG_QT_PLUGIN_PATH="${QT_PLUGIN_PATH:-}" +# Steam injects 32-bit gameoverlayrenderer.so via LD_PRELOAD which causes +# "wrong ELF class" errors for 64-bit Qt6 apps (see PrismLauncher #3421). +# Clear it for our process; game launches restore via FLUORINE_ORIG_LD_PRELOAD. +unset LD_PRELOAD + # ── Sync entire app to ~/.local/share/fluorine/bin/ ── # This gives instances a stable symlink target that won't break if the user # moves or deletes the original tarball extraction directory. @@ -630,10 +636,14 @@ BIN="${HERE}/usr/bin" APPIMAGE_DIR="$(dirname "$(readlink -f "${APPIMAGE:-$0}")")" export FLUORINE_ORIG_LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}" +export FLUORINE_ORIG_LD_PRELOAD="${LD_PRELOAD:-}" export FLUORINE_ORIG_PATH="${PATH}" export FLUORINE_ORIG_XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" export FLUORINE_ORIG_QT_PLUGIN_PATH="${QT_PLUGIN_PATH:-}" +# Steam injects 32-bit gameoverlayrenderer.so via LD_PRELOAD — clear it. +unset LD_PRELOAD + export PATH="${BIN}:${PATH}" # Replace (not append) LD_LIBRARY_PATH — Steam game mode injects its runtime # libs which break Python/Qt. RPATH handles the binary's own deps. diff --git a/libs/basic_games/steam_utils.py b/libs/basic_games/steam_utils.py index f61f392..3babd55 100644 --- a/libs/basic_games/steam_utils.py +++ b/libs/basic_games/steam_utils.py @@ -164,6 +164,7 @@ def find_steam_path() -> Path | None: # Linux: check common Steam install locations. for candidate in ( Path.home() / ".local" / "share" / "Steam", + Path.home() / ".steam" / "debian-installation", Path.home() / ".steam" / "steam", Path.home() / ".var" diff --git a/libs/basic_games_native/src/basicgameplugin.cpp b/libs/basic_games_native/src/basicgameplugin.cpp index 0631210..5f41f3a 100644 --- a/libs/basic_games_native/src/basicgameplugin.cpp +++ b/libs/basic_games_native/src/basicgameplugin.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include #include @@ -87,6 +89,8 @@ void BasicGamePlugin::detectGame() for (int steamId : m_def.steamAppIds) { QString path = findSteamGamePath(steamId); if (!path.isEmpty()) { + MOBase::log::debug("detectGame: '{}' found via Steam appid {} at '{}'", + m_def.gameName, steamId, path); setGamePath(path); return; } @@ -315,9 +319,50 @@ int BasicGamePlugin::nexusGameID() const bool BasicGamePlugin::looksValid(QDir const& dir) const { - // Primary check: binary at game root - if (dir.exists(m_def.binaryName)) + // Primary check: binary at game root. + // Use QFileInfo with the absolute path directly — QDir::exists() with + // relative paths containing subdirectories can have edge-case issues on + // some filesystems / Qt builds. + const QString absPath = dir.absoluteFilePath(m_def.binaryName); + if (QFileInfo::exists(absPath)) { return true; + } + + // Case-insensitive fallback: Linux filesystems are case-sensitive but + // Steam sometimes creates directories with different casing than expected. + // Walk the path components and try case-insensitive matching. + if (m_def.binaryName.contains('/')) { + QStringList parts = QString(m_def.binaryName).replace('\\', '/').split('/'); + QString current = dir.absolutePath(); + bool found = true; + for (const QString& part : parts) { + QDir d(current); + if (d.exists(part)) { + current = d.absoluteFilePath(part); + continue; + } + // Try case-insensitive match + const QStringList entries = + d.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + bool matched = false; + for (const QString& entry : entries) { + if (entry.compare(part, Qt::CaseInsensitive) == 0) { + current = d.absoluteFilePath(entry); + matched = true; + break; + } + } + if (!matched) { + found = false; + break; + } + } + if (found) { + MOBase::log::debug("looksValid: found '{}' via case-insensitive match in '{}'", + m_def.binaryName, dir.absolutePath()); + return true; + } + } // Fallback: check if the data directory exists relative to this path. // Some UE5 games (e.g. Oblivion Remastered) have their root exe nested @@ -334,6 +379,8 @@ bool BasicGamePlugin::looksValid(QDir const& dir) const } } + MOBase::log::debug("looksValid: '{}' not found in '{}'", m_def.binaryName, + dir.absolutePath()); return false; } diff --git a/src/src/protonlauncher.cpp b/src/src/protonlauncher.cpp index f25d355..57a88fc 100644 --- a/src/src/protonlauncher.cpp +++ b/src/src/protonlauncher.cpp @@ -70,6 +70,7 @@ void cleanAppImageEnv(QProcessEnvironment& env) const bool hasOrigVars = env.contains("FLUORINE_ORIG_PATH"); restoreOrStrip("LD_LIBRARY_PATH", "FLUORINE_ORIG_LD_LIBRARY_PATH", env); + restoreOrStrip("LD_PRELOAD", "FLUORINE_ORIG_LD_PRELOAD", env); restoreOrStrip("PATH", "FLUORINE_ORIG_PATH", env); restoreOrStrip("XDG_DATA_DIRS", "FLUORINE_ORIG_XDG_DATA_DIRS", env); restoreOrStrip("QT_PLUGIN_PATH", "FLUORINE_ORIG_QT_PLUGIN_PATH", env); -- cgit v1.3.1