Skip to content

Commit 0485977

Browse files
committed
Detect app icon location
Previously we could assume the app icon path for Steam apps. Starting with newer Steam client, this is no longer the case and the icon path might be located under either of the two paths: * `<steam_path>/appcache/librarycache/<appid>/<40 hex chars>.jpg` (new) * `<steam_path/appcache/librarycache/<appid>_icon.jpg` This also means we have to perform additional IO for each Steam app to detect the icon location.
1 parent 09e9a1f commit 0485977

File tree

4 files changed

+58
-13
lines changed

4 files changed

+58
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7+
## [Unreleased]
8+
### Fixed
9+
- Fix missing app icons for games installed using newer Steam client
10+
711
## [1.12.0] - 2024-09-16
812
### Added
913
- `--cwd-app` flag to set working directory to the game's installation directory

src/protontricks/steam.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
import os
3+
import re
34
import string
45
import struct
56
import zlib
@@ -263,8 +264,31 @@ def from_appmanifest(cls, path, steam_lib_paths, steam_path=None):
263264

264265
# If Steam path was provided, also populate the icon path
265266
if steam_path:
266-
icon_path = \
267-
steam_path / "appcache" / "librarycache" / f"{appid}_icon.jpg"
267+
# The icon path might be located in one of the two locations:
268+
# 1. `<steam_path>/appcache/librarycache/<appid>/<40 hex chars>.jpg
269+
# 2. `<steam_path>/appcache/librarycache/<appid>_icon.jpg`
270+
#
271+
# There doesn't appear to be any other way to determine which is
272+
# used other than by checking. This incurs some I/O for each app.
273+
icon_path = None
274+
library_cache_path = steam_path / "appcache" / "librarycache"
275+
try:
276+
# Try 1st location. This appears to be the newest, so this
277+
# should hopefully be the first match, at least on newer Steam
278+
# installations.
279+
app_lib_cache_path = library_cache_path / str(appid)
280+
281+
icon_path = next(
282+
path for path in app_lib_cache_path.iterdir()
283+
if re.match(r"[a-f0-9]{40}\.jpg", path.name)
284+
)
285+
except (StopIteration, FileNotFoundError):
286+
# Try 2nd location
287+
icon_path = library_cache_path / f"{appid}_icon.jpg"
288+
289+
if not icon_path.is_file():
290+
# No icon was found
291+
icon_path = None
268292

269293
# Check if the app requires another app. This is the case with
270294
# newer versions of Proton, which use Steam Runtimes installed as

tests/test_gui.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from protontricks.gui import (prompt_filesystem_access,
1010
select_steam_app_with_gui,
1111
select_steam_installation)
12+
from protontricks.steam import SteamApp
1213

1314

1415
@pytest.fixture(scope="function")
@@ -96,16 +97,33 @@ def test_select_game_icons(
9697
Select a game using the GUI. Ensure that icons are used in the dialog
9798
whenever available.
9899
"""
99-
steam_apps = [
100-
steam_app_factory(name="Fake game 1", appid=10),
101-
steam_app_factory(name="Fake game 2", appid=20),
102-
steam_app_factory(name="Fake game 3", appid=30),
103-
]
100+
steam_app_factory(name="Fake game 1", appid=10)
101+
steam_app_factory(name="Fake game 2", appid=20)
102+
steam_app_factory(name="Fake game 3", appid=30)
104103

105104
# Create icons for game 1 and 3
106-
for appid in (10, 30):
107-
Image.new("RGB", (32, 32)).save(
108-
steam_dir / "appcache" / "librarycache" / f"{appid}_icon.jpg")
105+
# Old location for 10
106+
Image.new("RGB", (32, 32)).save(
107+
steam_dir / "appcache" / "librarycache" / "10_icon.jpg"
108+
)
109+
110+
# New location for 30
111+
(steam_dir / "appcache" / "librarycache" / "30").mkdir()
112+
Image.new("RGB", (32, 32)).save(
113+
steam_dir / "appcache" / "librarycache" / "30"
114+
/ "ffffffffffffffffffffffffffffffffffffffff.jpg"
115+
)
116+
117+
# Read Steam apps using `SteamApp.from_appmanifest` to ensure
118+
# icon paths are detected correctly
119+
steam_apps = [
120+
SteamApp.from_appmanifest(
121+
steam_dir / "steamapps" / f"appmanifest_{appid}.acf",
122+
steam_path=steam_dir,
123+
steam_lib_paths=[steam_dir]
124+
)
125+
for appid in (10, 20, 30)
126+
]
109127

110128
gui_provider.mock_stdout = "Fake game 2: 20"
111129
select_steam_app_with_gui(steam_apps=steam_apps, steam_path=steam_dir)
@@ -114,7 +132,8 @@ def test_select_game_icons(
114132

115133
assert b"librarycache/10_icon.jpg\nFake game 1" in input_
116134
assert b"icon_placeholder.png\nFake game 2" in input_
117-
assert b"librarycache/30_icon.jpg\nFake game 3" in input_
135+
assert b"librarycache/30/ffffffffffffffffffffffffffffffffffffffff.jpg\nFake game 3" \
136+
in input_
118137

119138
def test_select_game_icons_ensure_resize(
120139
self, gui_provider, steam_app_factory, steam_dir, home_dir):

tests/test_steam.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ def test_steam_app_from_appmanifest_and_steam_path(
5252

5353
assert steam_app.name == "Fake game"
5454
assert steam_app.appid == 10
55-
assert steam_app.icon_path \
56-
== steam_dir / "appcache" / "librarycache" / "10_icon.jpg"
5755

5856
@pytest.mark.parametrize(
5957
"content",

0 commit comments

Comments
 (0)