@@ -124,36 +124,45 @@ std::wstring SteamTools::trim(std::wstring_view str) {
124124 return std::wstring (str.substr (first, last - first + 1 ));
125125}
126126
127- std::map<std::string, std::wstring> SteamTools::parseVdfContent (std::wstring_view content) {
128- std::map<std::string, std::wstring> appPaths;
127+ std::map<std::string, std::vector<std:: wstring> > SteamTools::parseVdfContent (std::wstring_view content) {
128+ std::map<std::string, std::vector<std:: wstring> > appPaths;
129129 std::wistringstream iss (content.data ());
130130 std::wstring line, currentPath;
131- bool inAppsSection = false ;
132131 bool inLibraryFolder = false ;
132+ bool inAppsSection = false ;
133133
134134 while (std::getline (iss, line)) {
135- line = trim (line);
135+ line.erase (0 , line.find_first_not_of (L" \t " )); // Trim left
136+ line.erase (line.find_last_not_of (L" \t " ) + 1 ); // Trim right
136137 if (line.empty ()) continue ;
137138
138- if (line.starts_with ( L" \" " ) && line[1 ] >= L' 0 ' && line[1 ] <= L ' 9 ' && line[2 ] == L' "' ) {
139+ if (line.size () >= 3 && line[0 ] == L' " ' && iswdigit ( line[1 ]) && line[2 ] == L' "' ) {
139140 inLibraryFolder = true ;
141+ currentPath.clear ();
140142 continue ;
141143 }
142144
143145 if (inLibraryFolder && line == L" {" ) continue ;
144146
145- if (line.starts_with (L" \" path\" " )) {
146- std::wistringstream lineStream (line);
147- std::wstring key, value;
148- if (!(lineStream >> std::quoted (key) >> std::quoted (value)) || key != L" path" ) {
149- throw VdfParseError (" Invalid path line format" );
147+ if (inLibraryFolder && line.starts_with (L" \" path\" " )) {
148+ size_t start = line.find (L' "' , 7 );
149+ size_t end = line.find (L' "' , start + 1 );
150+ if (start == std::wstring::npos || end == std::wstring::npos) {
151+ throw VdfParseError (" Malformed path line: " + std::string (line.begin (), line.end ()));
152+ }
153+ currentPath = line.substr (start + 1 , end - start - 1 );
154+ std::replace (currentPath.begin (), currentPath.end (), L' /' , L' \\ ' );
155+ std::wstring normalizedPath;
156+ for (size_t i = 0 ; i < currentPath.length (); ++i) {
157+ if (i > 0 && currentPath[i] == L' \\ ' && currentPath[i - 1 ] == L' \\ ' ) continue ;
158+ normalizedPath += currentPath[i];
150159 }
151- currentPath = value;
152- std::replace (currentPath.begin (), currentPath.end (), L' \\ ' , L' /' );
160+ currentPath = normalizedPath;
153161 Logger::logSuccess (std::format (L" Found library path: {}" , currentPath));
162+ continue ;
154163 }
155164
156- if (line == L" \" apps\" " ) {
165+ if (inLibraryFolder && line == L" \" apps\" " ) {
157166 inAppsSection = true ;
158167 continue ;
159168 }
@@ -165,62 +174,98 @@ std::map<std::string, std::wstring> SteamTools::parseVdfContent(std::wstring_vie
165174
166175 if (inLibraryFolder && !inAppsSection && line == L" }" ) {
167176 inLibraryFolder = false ;
168- currentPath.clear ();
169177 continue ;
170178 }
171179
172180 if (inAppsSection && line.starts_with (L" \" " )) {
173- std::wistringstream lineStream (line);
174- std::wstring wAppId, size;
175- if (!(lineStream >> std::quoted (wAppId) >> std::quoted (size))) {
176- throw VdfParseError (" Invalid app entry format" );
181+ size_t appIdStart = line.find (L' "' );
182+ size_t appIdEnd = line.find (L' "' , appIdStart + 1 );
183+ size_t sizeStart = line.find (L' "' , appIdEnd + 1 );
184+ size_t sizeEnd = line.find (L' "' , sizeStart + 1 );
185+ if (appIdEnd == std::wstring::npos || sizeEnd == std::wstring::npos) {
186+ throw VdfParseError (" Malformed app entry: " + std::string (line.begin (), line.end ()));
177187 }
178- std::string appId (wAppId.begin (), wAppId.end ()); // AppID remains ASCII
188+
189+ std::wstring wAppId = line.substr (appIdStart + 1 , appIdEnd - appIdStart - 1 );
190+ std::string appId (wAppId.begin (), wAppId.end ());
179191 if (!appId.empty () && !currentPath.empty ()) {
180- appPaths[appId] = currentPath + L" /steamapps/common/" ;
181- // Logger::logSuccess(std::format(L"Found app {} at {}", wAppId, appPaths[appId]));
192+ fs::path basePath = fs::path (currentPath) / L" steamapps" / L" common" ;
193+ std::wstring normalizedPath = basePath.wstring ();
194+ std::replace (normalizedPath.begin (), normalizedPath.end (), L' /' , L' \\ ' );
195+ std::wstring singleSlashPath;
196+ for (size_t i = 0 ; i < normalizedPath.length (); ++i) {
197+ if (i > 0 && normalizedPath[i] == L' \\ ' && normalizedPath[i - 1 ] == L' \\ ' ) continue ;
198+ singleSlashPath += normalizedPath[i];
199+ }
200+ appPaths[appId].push_back (singleSlashPath);
201+ // Logger::logInfo(std::format(L"App {} potentially at: {}", wAppId, singleSlashPath));
182202 }
183203 }
184204 }
205+
185206 return appPaths;
186207}
187208
188209std::optional<std::wstring> SteamTools::getAppInstallPath (std::string_view appId, const GameConfig& config) {
189210 try {
190- auto steamPath = getSteamInstallPath ();
191- auto vdfPath = fs::path (steamPath) / L" steamapps" / L" libraryfolders.vdf" ;
192- Logger::logInfo (std::format (L" Looking for VDF at: {}" , vdfPath.wstring ()));
211+ std::wstring steamPath = getSteamInstallPath ();
212+ if (steamPath.empty ()) {
213+ Logger::logWarning (L" Could not determine Steam installation path" );
214+ return std::nullopt ;
215+ }
216+ Logger::logSuccess (std::format (L" Steam install path from registry: {}" , steamPath));
217+
218+ fs::path vdfPath = fs::path (steamPath) / L" steamapps" / L" libraryfolders.vdf" ;
219+ std::wstring vdfPathStr = vdfPath.wstring ();
220+ std::replace (vdfPathStr.begin (), vdfPathStr.end (), L' /' , L' \\ ' );
221+ Logger::logInfo (std::format (L" Looking for VDF at: {}" , vdfPathStr));
193222
194223 std::wifstream file (vdfPath);
195224 if (!file.is_open ()) {
196- throw std::runtime_error (" Failed to open VDF file: " + std::string (vdfPath.string ().begin (), vdfPath.string ().end ()));
225+ Logger::logWarning (L" Failed to open VDF file: " + vdfPathStr);
226+ return std::nullopt ;
197227 }
198228
199229 std::wstringstream wss;
200230 wss << file.rdbuf ();
201231 std::wstring content = wss.str ();
202- Logger::logSuccess (" Successfully read VDF file" );
232+ file.close ();
233+ Logger::logSuccess (L" Successfully read VDF file" );
203234
204235 auto appPaths = parseVdfContent (content);
205- auto it = appPaths.find (appId. data ( ));
236+ auto it = appPaths.find (std::string (appId ));
206237 if (it == appPaths.end ()) {
207- Logger::logWarning (std::format (L" AppID {} not found in libraryfolders.vdf " , std::wstring (appId.begin (), appId.end ())));
238+ Logger::logWarning (std::format (L" AppID {} not found in VDF " , std::wstring (appId.begin (), appId.end ())));
208239 return std::nullopt ;
209240 }
210241
211- for (const auto & folder : config.possibleFolders ) {
212- std::wstring fullPath = it->second + folder;
213- if (fs::exists (fullPath) && fs::is_directory (fullPath)) {
214- Logger::logSuccess (std::format (L" Found {} at: {}" , std::wstring (appId.begin (), appId.end ()), fullPath));
215- return fullPath;
242+ const auto & potentialPaths = it->second ;
243+ for (const auto & basePath : potentialPaths) {
244+ Logger::logInfo (std::format (L" Exploring library path: {}" , basePath));
245+ for (const auto & folder : config.possibleFolders ) {
246+ fs::path fullPath = fs::path (basePath) / folder;
247+ std::wstring fullPathStr = fullPath.wstring ();
248+ std::replace (fullPathStr.begin (), fullPathStr.end (), L' /' , L' \\ ' );
249+ Logger::logInfo (std::format (L" Checking: {}" , fullPathStr));
250+ if (fs::exists (fullPath)) {
251+ if (fs::is_directory (fullPath)) {
252+ Logger::logSuccess (std::format (L" Found {} at: {}" ,
253+ std::wstring (appId.begin (), appId.end ()), fullPathStr));
254+ return fullPathStr;
255+ }
256+ else {
257+ Logger::logInfo (std::format (L" Path {} exists but is not a directory" , fullPathStr));
258+ }
259+ }
216260 }
217261 }
218262
219- Logger::logWarning (std::format (L" Found AppID {} but couldn't verify any known folder" , std::wstring (appId.begin (), appId.end ())));
263+ Logger::logWarning (std::format (L" AppID {} found in VDF but no valid installation folder located" ,
264+ std::wstring (appId.begin (), appId.end ())));
220265 return std::nullopt ;
221266 }
222267 catch (const std::exception& e) {
223- Logger::logWarning (std::format (" Error: {}" , e.what ()));
268+ Logger::logWarning (std::format (" Error getting app install path : {}" , e.what ()));
224269 return std::nullopt ;
225270 }
226271}
0 commit comments