1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
|
"""
DLL detection service for Lossless Scaling.
"""
import os
import re
from pathlib import Path
from typing import Dict, Any, List
from .base_service import BaseService
from .constants import (
ENV_LSFG_DLL_PATH, ENV_XDG_DATA_HOME, ENV_HOME,
STEAM_COMMON_PATH, LOSSLESS_DLL_NAME
)
from .types import DllDetectionResponse
class DllDetectionService(BaseService):
"""Service for detecting Lossless Scaling DLL"""
def check_lossless_scaling_dll(self) -> DllDetectionResponse:
"""Check if Lossless Scaling DLL is available at the expected paths
Search order:
1. LSFG_DLL_PATH environment variable
2. XDG_DATA_HOME Steam directory
3. HOME/.local/share Steam directory
4. All Steam library folders (including SD cards)
Returns:
DllDetectionResponse with detection status and path information
"""
try:
dll_path = self._check_env_dll_path()
if dll_path:
return dll_path
xdg_path = self._check_xdg_data_home()
if xdg_path:
return xdg_path
home_path = self._check_home_local_share()
if home_path:
return home_path
steam_libraries_path = self._check_steam_library_folders()
if steam_libraries_path:
return steam_libraries_path
return {
"detected": False,
"path": None,
"source": None,
"message": "Lossless Scaling DLL not found in expected locations",
"error": None
}
except Exception as e:
error_msg = f"Error checking Lossless Scaling DLL: {str(e)}"
self.log.error(error_msg)
return {
"detected": False,
"path": None,
"source": None,
"message": None,
"error": str(e)
}
def _check_env_dll_path(self) -> DllDetectionResponse | None:
"""Check LSFG_DLL_PATH environment variable
Returns:
DllDetectionResponse if found, None otherwise
"""
dll_path = os.getenv(ENV_LSFG_DLL_PATH)
if dll_path and dll_path.strip():
dll_path_obj = Path(dll_path.strip())
if dll_path_obj.exists():
self.log.info(f"Found DLL via {ENV_LSFG_DLL_PATH}: {dll_path_obj}")
return {
"detected": True,
"path": str(dll_path_obj),
"source": f"{ENV_LSFG_DLL_PATH} environment variable",
"message": None,
"error": None
}
return None
def _check_xdg_data_home(self) -> DllDetectionResponse | None:
"""Check XDG_DATA_HOME Steam directory
Returns:
DllDetectionResponse if found, None otherwise
"""
data_dir = os.getenv(ENV_XDG_DATA_HOME)
if data_dir and data_dir.strip():
dll_path = Path(data_dir.strip()) / "Steam" / STEAM_COMMON_PATH / LOSSLESS_DLL_NAME
if dll_path.exists():
self.log.info(f"Found DLL via {ENV_XDG_DATA_HOME}: {dll_path}")
return {
"detected": True,
"path": str(dll_path),
"source": f"{ENV_XDG_DATA_HOME} Steam directory",
"message": None,
"error": None
}
return None
def _check_home_local_share(self) -> DllDetectionResponse | None:
"""Check HOME/.local/share Steam directory
Returns:
DllDetectionResponse if found, None otherwise
"""
home_dir = os.getenv(ENV_HOME)
if home_dir and home_dir.strip():
dll_path = Path(home_dir.strip()) / ".local" / "share" / "Steam" / STEAM_COMMON_PATH / LOSSLESS_DLL_NAME
if dll_path.exists():
self.log.info(f"Found DLL via {ENV_HOME}/.local/share: {dll_path}")
return {
"detected": True,
"path": str(dll_path),
"source": f"{ENV_HOME}/.local/share Steam directory",
"message": None,
"error": None
}
return None
def _check_steam_library_folders(self) -> DllDetectionResponse | None:
"""Check all Steam library folders for Lossless Scaling DLL
This method parses Steam's libraryfolders.vdf file to find all
Steam library locations and checks each one for the DLL.
Returns:
DllDetectionResponse if found, None otherwise
"""
steam_libraries = self._get_steam_library_paths()
for library_path in steam_libraries:
dll_path = Path(library_path) / STEAM_COMMON_PATH / LOSSLESS_DLL_NAME
if dll_path.exists():
self.log.info(f"Found DLL in Steam library: {dll_path}")
return {
"detected": True,
"path": str(dll_path),
"source": f"Steam library folder: {library_path}",
"message": None,
"error": None
}
return None
def _get_steam_library_paths(self) -> List[str]:
"""Get all Steam library folder paths from libraryfolders.vdf
Returns:
List of Steam library folder paths
"""
library_paths = []
steam_paths = []
data_dir = os.getenv(ENV_XDG_DATA_HOME)
if data_dir and data_dir.strip():
steam_paths.append(Path(data_dir.strip()) / "Steam")
home_dir = os.getenv(ENV_HOME)
if home_dir and home_dir.strip():
steam_paths.append(Path(home_dir.strip()) / ".local" / "share" / "Steam")
for steam_path in steam_paths:
if steam_path.exists():
library_paths.append(str(steam_path))
vdf_path = steam_path / "steamapps" / "libraryfolders.vdf"
if vdf_path.exists():
try:
additional_paths = self._parse_library_folders_vdf(vdf_path)
library_paths.extend(additional_paths)
except Exception as e:
self.log.warning(f"Failed to parse {vdf_path}: {str(e)}")
seen = set()
unique_paths = []
for path in library_paths:
if path not in seen:
seen.add(path)
unique_paths.append(path)
self.log.info(f"Found {len(unique_paths)} Steam library paths: {unique_paths}")
return unique_paths
def _parse_library_folders_vdf(self, vdf_path: Path) -> List[str]:
"""Parse Steam's libraryfolders.vdf file to extract library paths
Args:
vdf_path: Path to the libraryfolders.vdf file
Returns:
List of additional Steam library folder paths
"""
library_paths = []
try:
with open(vdf_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
path_pattern = r'"path"\s*"([^"]+)"'
matches = re.findall(path_pattern, content, re.IGNORECASE)
for path_match in matches:
path = path_match.replace('\\\\', '/').replace('\\', '/')
library_path = Path(path)
if library_path.exists() and (library_path / "steamapps").exists():
library_paths.append(str(library_path))
self.log.info(f"Found additional Steam library: {library_path}")
except Exception as e:
self.log.error(f"Error parsing libraryfolders.vdf: {str(e)}")
return library_paths
|