aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-17 21:41:31 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-17 21:41:31 -0500
commit412eda0369cb89c8fed871d46227bbe671c4fd68 (patch)
tree4a85a660410635e55e711544b163ea24e1bbc114
parent2efc7e6124ef814ac9abd874bff0dc6b3e6768d0 (diff)
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 <noreply@anthropic.com>
-rwxr-xr-xdocker/build-inner.sh10
-rw-r--r--libs/basic_games/steam_utils.py1
-rw-r--r--libs/basic_games_native/src/basicgameplugin.cpp51
-rw-r--r--src/src/protonlauncher.cpp1
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 <uibase/utility.h>
#include <uibase/versioninfo.h>
+#include <uibase/log.h>
+
#include <QDateTime>
#include <QDirIterator>
#include <QStandardPaths>
@@ -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);