Skip to content

Commit

Permalink
Fix crash caused by non-numeric shortcut app IDs
Browse files Browse the repository at this point in the history
Applications such as Lutris create Steam shortcuts that use non-numeric
app IDs. Handle the app IDs gracefully by logging and skipping them
instead of crashing.

Fixes #147
  • Loading branch information
Matoking committed May 10, 2022
1 parent 361ee26 commit be3805b
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed
- Fix Wine crash on newer Steam Runtime installations due to renamed runtime executable
- Fix graphical Wine applications crashing on Wayland
- Fix Protontricks crash caused by Steam shortcuts created by 3rd party applications such as Lutris

## [1.8.1] - 2022-03-20
### Added
Expand Down
10 changes: 9 additions & 1 deletion src/protontricks/steam.py
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,15 @@ def get_custom_windows_shortcuts(steam_path):
shortcut_id = int(shortcut_id)

if "appid" in shortcut_data:
appid = shortcut_data["appid"] & 0xffffffff
try:
appid = shortcut_data["appid"] & 0xffffffff
except TypeError:
logger.info(
"Skipping unrecognized non-Steam shortcut with app ID "
"'%s'",
shortcut_data["appid"]
)
continue
else:
appid = get_appid_from_shortcut(
target=shortcut_data["exe"], name=shortcut_data["appname"]
Expand Down
22 changes: 15 additions & 7 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,15 @@ def shortcut_factory(steam_dir, steam_user):
"""
shortcuts_by_user = defaultdict(list)

def func(install_dir, name, steamid64=None, appid_in_vdf=False):
def func(
install_dir, name, steamid64=None, appid_in_vdf=False, appid=None):
if not steamid64:
steamid64 = steam_user

# Update shortcuts.vdf first
steamid3 = int(steamid64) & 0xffffffff
shortcuts_by_user[steamid3].append({
"install_dir": install_dir, "name": name
"install_dir": install_dir, "name": name, "appid": appid
})

shortcut_path = (
Expand All @@ -213,13 +214,14 @@ def func(install_dir, name, steamid64=None, appid_in_vdf=False):
for shortcut_data in shortcuts_by_user[steamid3]:
install_dir_ = shortcut_data["install_dir"]
name_ = shortcut_data["name"]
appid_ = shortcut_data["appid"]

entry = {
"AppName": name_,
"StartDir": install_dir_,
"exe": str(Path(install_dir_) / name_)
}
# Derive the shortcut ID
# Derive the shortcut ID like Steam would
crc_data = b"".join([
entry["exe"].encode("utf-8"),
entry["AppName"].encode("utf-8")
Expand All @@ -231,15 +233,21 @@ def func(install_dir, name, steamid64=None, appid_in_vdf=False):
if appid_in_vdf:
# Store the app ID in `shortcuts.vdf`. This is similar
# in behavior to newer Steam releases.
entry["appid"] = ~(result ^ 0xffffffff)
if appid_ is None:
entry["appid"] = ~(result ^ 0xffffffff)
else:
# For pre-determined app IDs, such as those created by
# Lutris, use them as-is
entry["appid"] = appid_

data["shortcuts"][str(shortcut_id)] = entry

shortcut_path.write_bytes(vdf.binary_dumps(data))

appid = get_appid_from_shortcut(
target=str(Path(install_dir) / name), name=name
)
if not appid:
appid = get_appid_from_shortcut(
target=str(Path(install_dir) / name), name=name
)

# Create the fake prefix
(steam_dir / "steamapps" / "compatdata" / str(appid) / "pfx").mkdir(
Expand Down
34 changes: 34 additions & 0 deletions tests/test_steam.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,40 @@ def test_get_custom_windows_shortcuts_read_vdf(
assert shortcut_apps[0].name == "Non-Steam shortcut: fakegame.exe"
assert shortcut_apps[0].appid == 4149337689

@pytest.mark.usefixtures("verbose_logging")
def test_get_custom_windows_shortcuts_non_numeric_appid(
self, steam_dir, shortcut_factory, caplog):
"""
Retrieve custom Windows shortcuts when one of the contained shortcuts
has a non-numeric app ID. This is usually the case for app IDs created
with 3rd party applications such as Lutris.
"""
# This won't be reported by Protontricks
shortcut_factory(
install_dir="/usr/bin", name="lutris",
appid_in_vdf=True, appid="lutris-fake-game"
)
shortcut_factory(
install_dir="fake/path/", name="fakegame.exe", appid_in_vdf=True
)

shortcut_apps = get_custom_windows_shortcuts(steam_dir)

assert len(shortcut_apps) == 1
assert shortcut_apps[0].name == "Non-Steam shortcut: fakegame.exe"

# The non-numeric app ID was logged
record = next(
record for record in caplog.records
if "Skipping unrecognized" in record.message
)

assert record.levelname == "INFO"
assert (
"Skipping unrecognized non-Steam shortcut with "
"app ID 'lutris-fake-game'" in record.message
)


class TestIsSteamDeck:
def test_not_steam_deck(self):
Expand Down

0 comments on commit be3805b

Please sign in to comment.