diff --git a/CHANGELOG.md b/CHANGELOG.md index 11e0436..44a9e83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [Unreleased] +### Fixed + - Fix missing app icons for games installed using newer Steam client + ## [1.12.0] - 2024-09-16 ### Added - `--cwd-app` flag to set working directory to the game's installation directory diff --git a/src/protontricks/steam.py b/src/protontricks/steam.py index 98793e3..35a7463 100644 --- a/src/protontricks/steam.py +++ b/src/protontricks/steam.py @@ -1,5 +1,6 @@ import logging import os +import re import string import struct import zlib @@ -263,8 +264,31 @@ def from_appmanifest(cls, path, steam_lib_paths, steam_path=None): # If Steam path was provided, also populate the icon path if steam_path: - icon_path = \ - steam_path / "appcache" / "librarycache" / f"{appid}_icon.jpg" + # The icon path might be located in one of the two locations: + # 1. `/appcache/librarycache//<40 hex chars>.jpg + # 2. `/appcache/librarycache/_icon.jpg` + # + # There doesn't appear to be any other way to determine which is + # used other than by checking. This incurs some I/O for each app. + icon_path = None + library_cache_path = steam_path / "appcache" / "librarycache" + try: + # Try 1st location. This appears to be the newest, so this + # should hopefully be the first match, at least on newer Steam + # installations. + app_lib_cache_path = library_cache_path / str(appid) + + icon_path = next( + path for path in app_lib_cache_path.iterdir() + if re.match(r"[a-f0-9]{40}\.jpg", path.name) + ) + except (StopIteration, FileNotFoundError): + # Try 2nd location + icon_path = library_cache_path / f"{appid}_icon.jpg" + + if not icon_path.is_file(): + # No icon was found + icon_path = None # Check if the app requires another app. This is the case with # newer versions of Proton, which use Steam Runtimes installed as diff --git a/tests/test_gui.py b/tests/test_gui.py index bab4503..2dbc32d 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -9,6 +9,7 @@ from protontricks.gui import (prompt_filesystem_access, select_steam_app_with_gui, select_steam_installation) +from protontricks.steam import SteamApp @pytest.fixture(scope="function") @@ -96,16 +97,33 @@ def test_select_game_icons( Select a game using the GUI. Ensure that icons are used in the dialog whenever available. """ - steam_apps = [ - steam_app_factory(name="Fake game 1", appid=10), - steam_app_factory(name="Fake game 2", appid=20), - steam_app_factory(name="Fake game 3", appid=30), - ] + steam_app_factory(name="Fake game 1", appid=10) + steam_app_factory(name="Fake game 2", appid=20) + steam_app_factory(name="Fake game 3", appid=30) # Create icons for game 1 and 3 - for appid in (10, 30): - Image.new("RGB", (32, 32)).save( - steam_dir / "appcache" / "librarycache" / f"{appid}_icon.jpg") + # Old location for 10 + Image.new("RGB", (32, 32)).save( + steam_dir / "appcache" / "librarycache" / "10_icon.jpg" + ) + + # New location for 30 + (steam_dir / "appcache" / "librarycache" / "30").mkdir() + Image.new("RGB", (32, 32)).save( + steam_dir / "appcache" / "librarycache" / "30" + / "ffffffffffffffffffffffffffffffffffffffff.jpg" + ) + + # Read Steam apps using `SteamApp.from_appmanifest` to ensure + # icon paths are detected correctly + steam_apps = [ + SteamApp.from_appmanifest( + steam_dir / "steamapps" / f"appmanifest_{appid}.acf", + steam_path=steam_dir, + steam_lib_paths=[steam_dir] + ) + for appid in (10, 20, 30) + ] gui_provider.mock_stdout = "Fake game 2: 20" select_steam_app_with_gui(steam_apps=steam_apps, steam_path=steam_dir) @@ -114,7 +132,8 @@ def test_select_game_icons( assert b"librarycache/10_icon.jpg\nFake game 1" in input_ assert b"icon_placeholder.png\nFake game 2" in input_ - assert b"librarycache/30_icon.jpg\nFake game 3" in input_ + assert b"librarycache/30/ffffffffffffffffffffffffffffffffffffffff.jpg\nFake game 3" \ + in input_ def test_select_game_icons_ensure_resize( self, gui_provider, steam_app_factory, steam_dir, home_dir): diff --git a/tests/test_steam.py b/tests/test_steam.py index 347a034..dfe5e9a 100644 --- a/tests/test_steam.py +++ b/tests/test_steam.py @@ -52,8 +52,6 @@ def test_steam_app_from_appmanifest_and_steam_path( assert steam_app.name == "Fake game" assert steam_app.appid == 10 - assert steam_app.icon_path \ - == steam_dir / "appcache" / "librarycache" / "10_icon.jpg" @pytest.mark.parametrize( "content",