aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-23 21:22:43 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-23 21:25:13 -0500
commit12ce612e00af99990d364b1b74aca39e81b4d56f (patch)
tree6ea4643827ddb60d70d5b9ed4e322e089563c0c5 /libs
parent5891a5feabdd768a3e649ff6087fac9c42b129cc (diff)
Fix native Stardew launch paths
Diffstat (limited to 'libs')
-rw-r--r--libs/basic_games/games/game_stardewvalley.py106
1 files changed, 105 insertions, 1 deletions
diff --git a/libs/basic_games/games/game_stardewvalley.py b/libs/basic_games/games/game_stardewvalley.py
index 566a511..e9b6863 100644
--- a/libs/basic_games/games/game_stardewvalley.py
+++ b/libs/basic_games/games/game_stardewvalley.py
@@ -1,9 +1,12 @@
+import os
+import struct
import sys
-from PyQt6.QtCore import QDir, QFileInfo
+from PyQt6.QtCore import QDir, QFileInfo, qWarning
import mobase
+from ..basic_features import BasicLocalSavegames
from ..basic_game import BasicGame
@@ -34,6 +37,94 @@ def _has_native_linux_install(game_dir: QDir) -> bool:
return QFileInfo(game_dir, "StardewValley").exists()
+def _native_linux_config_dir() -> QDir:
+ xdg_config_home = os.environ.get("XDG_CONFIG_HOME")
+ candidates = []
+ if xdg_config_home:
+ candidates.append(os.path.join(xdg_config_home, "StardewValley"))
+ candidates.extend(
+ [
+ os.path.expanduser("~/.config/StardewValley"),
+ os.path.expanduser(
+ "~/.var/app/com.valvesoftware.Steam/.config/StardewValley"
+ ),
+ os.path.expanduser(
+ "~/.var/app/com.heroicgameslauncher.hgl/config/StardewValley"
+ ),
+ ]
+ )
+
+ seen = set()
+ for candidate in candidates:
+ if candidate in seen:
+ continue
+ seen.add(candidate)
+ directory = QDir(candidate)
+ if directory.exists():
+ return directory
+
+ return QDir(candidates[0])
+
+
+def _clear_executable_stack(path: str) -> None:
+ # Equivalent to `execstack -c`: clear PF_X on the PT_GNU_STACK program
+ # header. Stardew's Galaxy libraries can ship with this bit set, which
+ # makes some Linux systems refuse to load them.
+ PT_GNU_STACK = 0x6474E551
+ PF_X = 0x1
+
+ if not os.path.exists(path):
+ return
+
+ try:
+ with open(path, "r+b") as f:
+ ident = f.read(16)
+ if len(ident) != 16 or ident[:4] != b"\x7fELF":
+ return
+
+ elf_class = ident[4]
+ endian = "<" if ident[5] == 1 else ">" if ident[5] == 2 else None
+ if endian is None:
+ return
+
+ if elf_class == 2:
+ f.seek(32)
+ phoff = struct.unpack(endian + "Q", f.read(8))[0]
+ f.seek(54)
+ phentsize, phnum = struct.unpack(endian + "HH", f.read(4))
+ flags_offset = 4
+ elif elf_class == 1:
+ f.seek(28)
+ phoff = struct.unpack(endian + "I", f.read(4))[0]
+ f.seek(42)
+ phentsize, phnum = struct.unpack(endian + "HH", f.read(4))
+ flags_offset = 24
+ else:
+ return
+
+ for index in range(phnum):
+ header_offset = phoff + index * phentsize
+ f.seek(header_offset)
+ p_type = struct.unpack(endian + "I", f.read(4))[0]
+ if p_type != PT_GNU_STACK:
+ continue
+
+ flags_pos = header_offset + flags_offset
+ f.seek(flags_pos)
+ flags = struct.unpack(endian + "I", f.read(4))[0]
+ if flags & PF_X:
+ f.seek(flags_pos)
+ f.write(struct.pack(endian + "I", flags & ~PF_X))
+ return
+ except OSError as err:
+ qWarning(f"Failed to clear executable stack on '{path}': {err}")
+
+
+def _fix_native_linux_galaxy_libraries(game_dir: QDir) -> None:
+ for library in ("libGalaxy64.so", "libGalaxyCSharpGlue.so"):
+ _clear_executable_stack(game_dir.absoluteFilePath(library))
+
+
class StardewValleyGame(BasicGame):
Name = "Stardew Valley Support Plugin"
Author = "Syer10"
@@ -57,11 +148,24 @@ class StardewValleyGame(BasicGame):
def init(self, organizer: mobase.IOrganizer):
super().init(organizer)
self._register_feature(StardewValleyModDataChecker())
+ if self.isNativeLinux():
+ _fix_native_linux_galaxy_libraries(self.gameDirectory())
+ self._register_feature(BasicLocalSavegames(self))
return True
def isNativeLinux(self) -> bool:
return _has_native_linux_install(self.gameDirectory())
+ def documentsDirectory(self) -> QDir:
+ if self.isNativeLinux():
+ return _native_linux_config_dir()
+ return super().documentsDirectory()
+
+ def savesDirectory(self) -> QDir:
+ if self.isNativeLinux():
+ return QDir(self.documentsDirectory().absoluteFilePath("Saves"))
+ return super().savesDirectory()
+
def binaryName(self) -> str:
if self.isNativeLinux():
return "StardewValley"