mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 03:21:08 +00:00
fix: Reuse the prefix the client made for a shortcut id (#1107)
* fix: Reuse the prefix the client made for a shortcut id
droiddeck-proton-run builds the prefix path as steamapps/compatdata/<appid>,
but the client names that folder with the signed 32-bit form of the shortcut
id. A shortcut whose id has the top bit set therefore got a second, empty
prefix, and the first launch after that ran wineboot from scratch: the
session sat on a black screen for the length of a full prefix init, and the
old prefix was never reused.
Match the folder by its unsigned value instead, the way the client does:
int(name) % 2 ** 32 == appid % 2 ** 32. prefix_of() also accepts a negative
folder name now, and reports its unsigned id so the Steam environment it sets
up stays valid.
* fix: Reject folder names int() cannot read, and skip files
lstrip("-").isdigit() accepts "--1" and superscript digits, which int() then
raises on, and one such entry in compatdata would stop shortcut resolution.
Match r"-?[0-9]+" instead. A matching name is also only used when it is a
directory, so a stray file cannot take STEAM_COMPAT_DATA_PATH.
* fix: reuse signed shortcut compatdata prefix
* fix: prefer initialized Proton shortcut prefixes
---------
Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
This commit is contained in:
2 files changed
+74
-5
No files matched your search
@@ -193,9 +193,9 @@ def steam_game(program, libs):
|
||||
def prefix_of(program):
|
||||
for folder, below in ancestors(program):
|
||||
parent = os.path.dirname(folder)
|
||||
if below == "pfx" and os.path.basename(parent) == "compatdata" and os.path.basename(folder).isdigit():
|
||||
if below == "pfx" and os.path.basename(parent) == "compatdata" and re.fullmatch(r"-?[0-9]+", os.path.basename(folder)):
|
||||
library = os.path.dirname(os.path.dirname(parent))
|
||||
return Target(os.path.basename(folder), folder, os.path.dirname(program), library, "prefix")
|
||||
return Target(str(int(os.path.basename(folder)) % 2 ** 32), folder, os.path.dirname(program), library, "prefix")
|
||||
return None
|
||||
|
||||
|
||||
@@ -238,6 +238,30 @@ def shortcut_entries(root):
|
||||
return entries
|
||||
|
||||
|
||||
def prefix_folder(root, appid):
|
||||
"""The prefix folder the client made for [appid], under either spelling of the id."""
|
||||
folder = os.path.join(root, "steamapps", "compatdata")
|
||||
try:
|
||||
names = sorted(os.listdir(folder))
|
||||
except OSError:
|
||||
names = []
|
||||
candidates = []
|
||||
for name in names:
|
||||
if not re.fullmatch(r"-?[0-9]+", name):
|
||||
continue
|
||||
try:
|
||||
matches = int(name) % 2 ** 32 == appid % 2 ** 32
|
||||
except ValueError:
|
||||
continue
|
||||
match = os.path.join(folder, name)
|
||||
if matches and os.path.isdir(match):
|
||||
candidates.append((os.path.isdir(os.path.join(match, "pfx")),
|
||||
name == str(appid % 2 ** 32), name, match))
|
||||
if candidates:
|
||||
return min(candidates, key=lambda item: (not item[0], not item[1], item[2]))[3]
|
||||
return os.path.join(folder, str(appid % 2 ** 32))
|
||||
|
||||
|
||||
def vdf_string(token):
|
||||
return re.sub(r'\\(.)', r'\1', token[1:-1]) if token.startswith('"') else ""
|
||||
|
||||
@@ -275,7 +299,7 @@ def steam_options(app, root):
|
||||
def shortcut(program, root):
|
||||
for appid, exe, start in shortcut_entries(root):
|
||||
if same_file(program, exe):
|
||||
return Target(appid, os.path.join(root, "steamapps", "compatdata", str(appid)),
|
||||
return Target(appid, prefix_folder(root, appid),
|
||||
os.path.dirname(program), root, "shortcut", start if os.path.isdir(start) else None)
|
||||
return None
|
||||
|
||||
@@ -292,7 +316,7 @@ def locate(program, root):
|
||||
if found:
|
||||
return found
|
||||
appid = own_id(program)
|
||||
return Target(appid, os.path.join(root, "steamapps", "compatdata", str(appid)),
|
||||
return Target(appid, prefix_folder(root, appid),
|
||||
os.path.dirname(program), root, "own")
|
||||
|
||||
|
||||
@@ -376,7 +400,8 @@ def steam_launch(appid, root):
|
||||
|
||||
|
||||
def shortcut_target(appid, program, start, root):
|
||||
return Target(appid, os.path.join(root, "steamapps", "compatdata", str(appid)), os.path.dirname(program), root,
|
||||
appid = int(appid) % 2 ** 32
|
||||
return Target(str(appid), prefix_folder(root, appid), os.path.dirname(program), root,
|
||||
"shortcut", start if start and os.path.isdir(start) else None)
|
||||
|
||||
|
||||
|
||||
@@ -570,6 +570,50 @@ class ProtonRunTest(Temp):
|
||||
self.game("70", "Linux Game", "run.sh")
|
||||
self.assertEqual(self.run_main(["--app", "70"])[0], 2)
|
||||
|
||||
def test_without_steam_a_shortcut_reuses_a_signed_prefix_folder(self):
|
||||
bat = self.write("Games/Epic/Game/.droiddeck-launch.bat", b"@echo off")
|
||||
start = self.tmp / "Games/Epic/Game/Binaries"
|
||||
start.mkdir()
|
||||
signed = self.root / "steamapps/compatdata/-1561598272"
|
||||
signed.mkdir(parents=True)
|
||||
code, calls, dirs = self.run_main(["--app", "2733369024", "--dir", str(start), "--", str(bat)])
|
||||
self.assertIsNone(code)
|
||||
env = calls[0][2]
|
||||
self.assertEqual(env["STEAM_COMPAT_DATA_PATH"], str(signed))
|
||||
self.assertFalse((self.root / "steamapps/compatdata/2733369024").exists())
|
||||
|
||||
def test_prefix_folder_prefers_initialized_unsigned_over_empty_signed(self):
|
||||
signed = self.root / "steamapps/compatdata/-1561598272"
|
||||
signed.mkdir(parents=True)
|
||||
initialized = self.root / "steamapps/compatdata/2733369024/pfx"
|
||||
initialized.mkdir(parents=True)
|
||||
|
||||
self.assertEqual(RUN["prefix_folder"](str(self.root), 2733369024), str(initialized.parent))
|
||||
|
||||
def test_prefix_folder_prefers_initialized_signed_over_empty_unsigned(self):
|
||||
unsigned = self.root / "steamapps/compatdata/2733369024"
|
||||
unsigned.mkdir(parents=True)
|
||||
initialized = self.root / "steamapps/compatdata/-1561598272/pfx"
|
||||
initialized.mkdir(parents=True)
|
||||
|
||||
self.assertEqual(RUN["prefix_folder"](str(self.root), 2733369024), str(initialized.parent))
|
||||
|
||||
def test_prefix_folder_prefers_unsigned_when_initialization_ties(self):
|
||||
unsigned = self.root / "steamapps/compatdata/2733369024/pfx"
|
||||
unsigned.mkdir(parents=True)
|
||||
signed = self.root / "steamapps/compatdata/-1561598272/pfx"
|
||||
signed.mkdir(parents=True)
|
||||
|
||||
self.assertEqual(RUN["prefix_folder"](str(self.root), 2733369024), str(unsigned.parent))
|
||||
|
||||
def test_prefix_folder_skips_numeric_names_too_long_to_convert(self):
|
||||
from unittest.mock import patch
|
||||
folder = self.root / "steamapps/compatdata"
|
||||
folder.mkdir(parents=True)
|
||||
with patch.object(RUN["os"], "listdir", return_value=["9" * 5000]):
|
||||
self.assertEqual(RUN["prefix_folder"](str(self.root), 2733369024),
|
||||
str(folder / "2733369024"))
|
||||
|
||||
def test_without_steam_a_shortcut_keeps_its_prefix_folder_and_launch_options(self):
|
||||
bat = self.write("Games/Epic/Game/.droiddeck-launch.bat", b"@echo off")
|
||||
start = self.tmp / "Games/Epic/Game/Binaries"
|
||||
|
||||
Reference in new issue
Block a user