mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
785 lines
35 KiB
C#
785 lines
35 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using ValveKeyValue;
|
|
using DisplayMagician.GameLibraries.SteamAppInfoParser;
|
|
using Microsoft.Win32;
|
|
using System.IO;
|
|
using System.Security;
|
|
using System.Diagnostics;
|
|
|
|
namespace DisplayMagician.GameLibraries
|
|
{
|
|
public sealed class SteamLibrary : GameLibrary
|
|
{
|
|
|
|
private struct SteamAppInfo
|
|
{
|
|
public string GameID;
|
|
public string GameName;
|
|
public List<string> GameExes;
|
|
public string GameInstallDir;
|
|
public string GameIconPath;
|
|
}
|
|
|
|
#region Class Variables
|
|
// Static members are 'eagerly initialized', that is,
|
|
// immediately when class is loaded for the first time.
|
|
// .NET guarantees thread safety for static initialization
|
|
private static SteamLibrary _instance = new SteamLibrary();
|
|
|
|
// Common items to the class
|
|
private List<Game> _allSteamGames = new List<Game>();
|
|
private string steamAppIdRegex = @"/^[0-9A-F]{1,10}$";
|
|
private string _steamExe;
|
|
private string _steamPath;
|
|
private string _steamConfigVdfFile;
|
|
private List<string> _steamProcessList = new List<string>() { "steam"};
|
|
private string _registrySteamKey = @"SOFTWARE\WOW6432Node\Valve\Steam"; // under LocalMachine
|
|
private string _registryAppsKey = $@"SOFTWARE\Valve\Steam\Apps"; // under CurrentUser
|
|
private bool _isSteamInstalled = false;
|
|
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
|
// Other constants that are useful
|
|
#endregion
|
|
|
|
#region Class Constructors
|
|
static SteamLibrary() { }
|
|
|
|
private SteamLibrary()
|
|
{
|
|
try
|
|
{
|
|
logger.Trace($"SteamLibrary/SteamLibrary: Steam launcher registry key = HKLM\\{_registrySteamKey}");
|
|
// Find the SteamExe location, and the SteamPath for later
|
|
using (var steamInstallKey = Registry.LocalMachine.OpenSubKey(_registrySteamKey, RegistryKeyPermissionCheck.ReadSubTree))
|
|
{
|
|
if (steamInstallKey == null)
|
|
{
|
|
logger.Info($"SteamLibrary/SteamLibrary: Steam library is not installed!");
|
|
return;
|
|
}
|
|
_steamPath = steamInstallKey.GetValue("InstallPath", "C:\\Program Files (x86)\\Steam").ToString();
|
|
_steamExe = $"{_steamPath}\\steam.exe";
|
|
}
|
|
if (File.Exists(_steamExe))
|
|
{
|
|
logger.Info($"SteamLibrary/SteamLibrary: Steam library is installed in {_steamPath}. Found {_steamExe}");
|
|
_isSteamInstalled = true;
|
|
}
|
|
else
|
|
{
|
|
logger.Info($"SteamLibrary/SteamLibrary: Steam library is not installed!");
|
|
}
|
|
}
|
|
catch (SecurityException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/SteamLibrary: The user does not have the permissions required to read the Steam registry key.");
|
|
}
|
|
catch (ObjectDisposedException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/SteamLibrary: The Microsoft.Win32.RegistryKey is closed when trying to access the Steam registry key (closed keys cannot be accessed).");
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/SteamLibrary: The Steam registry key has been marked for deletion so we cannot access the value during the SteamLibrary check.");
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/SteamLibrary: The user does not have the necessary registry rights to check whether Steam is installed.");
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Class Properties
|
|
public override List<Game> AllInstalledGames
|
|
{
|
|
get
|
|
{
|
|
// Load the Steam Games from Steam Client if needed
|
|
if (_allSteamGames.Count == 0)
|
|
LoadInstalledGames();
|
|
return _allSteamGames;
|
|
}
|
|
}
|
|
|
|
|
|
public override int InstalledGameCount
|
|
{
|
|
get
|
|
{
|
|
return _allSteamGames.Count;
|
|
}
|
|
}
|
|
|
|
public string SteamRegistryKey
|
|
{
|
|
get
|
|
{
|
|
return _registrySteamKey;
|
|
}
|
|
}
|
|
|
|
public string SteamAppsRegistryKey
|
|
{
|
|
get
|
|
{
|
|
return _registryAppsKey;
|
|
}
|
|
}
|
|
|
|
public override string GameLibraryName
|
|
{
|
|
get
|
|
{
|
|
return "Steam";
|
|
}
|
|
}
|
|
|
|
public override SupportedGameLibraryType GameLibraryType
|
|
{
|
|
get
|
|
{
|
|
return SupportedGameLibraryType.Steam;
|
|
}
|
|
}
|
|
|
|
public override string GameLibraryExe
|
|
{
|
|
get
|
|
{
|
|
return _steamExe;
|
|
}
|
|
}
|
|
|
|
public override string GameLibraryPath
|
|
{
|
|
get
|
|
{
|
|
return _steamPath;
|
|
}
|
|
}
|
|
|
|
public override bool IsGameLibraryInstalled
|
|
{
|
|
get
|
|
{
|
|
return _isSteamInstalled;
|
|
}
|
|
|
|
}
|
|
|
|
public override bool IsRunning
|
|
{
|
|
get
|
|
{
|
|
List<Process> steamLibraryProcesses = new List<Process>();
|
|
|
|
foreach (string steamLibraryProcessName in _steamProcessList)
|
|
{
|
|
// Look for the processes with the ProcessName we sorted out earlier
|
|
steamLibraryProcesses.AddRange(Process.GetProcessesByName(steamLibraryProcessName));
|
|
}
|
|
|
|
// If we have found one or more processes then we should be good to go
|
|
// so let's break, and get to the next step....
|
|
if (steamLibraryProcesses.Count > 0)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
public override bool IsUpdating
|
|
{
|
|
get
|
|
{
|
|
// Not implemeted at present
|
|
// so we just return a false
|
|
// TODO Implement Origin specific detection for updating the game client
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
public override List<string> GameLibraryProcesses
|
|
{
|
|
get
|
|
{
|
|
return _steamProcessList;
|
|
}
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
#region Class Methods
|
|
public static SteamLibrary GetLibrary()
|
|
{
|
|
return _instance;
|
|
}
|
|
|
|
public override bool AddGame(Game steamGame)
|
|
{
|
|
if (!(steamGame is SteamGame))
|
|
return false;
|
|
|
|
// Doublecheck if it already exists
|
|
// Because then we just update the one that already exists
|
|
if (ContainsGame(steamGame))
|
|
{
|
|
logger.Debug($"SteamLibrary/AddSteamGame: Updating Steam game {steamGame.Name} in our Steam library");
|
|
// We update the existing Shortcut with the data over
|
|
SteamGame steamGameToUpdate = (SteamGame)GetGameById(steamGame.Id.ToString());
|
|
steamGame.CopyTo(steamGameToUpdate);
|
|
}
|
|
else
|
|
{
|
|
logger.Debug($"SteamLibrary/AddSteamGame: Adding Steam game {steamGame.Name} to our Steam library");
|
|
// Add the steamGame to the list of steamGames
|
|
_allSteamGames.Add(steamGame);
|
|
}
|
|
|
|
//Doublecheck it's been added
|
|
if (ContainsGame(steamGame))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
}
|
|
|
|
public override bool RemoveGame(Game steamGame)
|
|
{
|
|
if (!(steamGame is SteamGame))
|
|
return false;
|
|
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame: Removing Steam game {steamGame.Name} from our Steam library");
|
|
|
|
// Remove the steamGame from the list.
|
|
int numRemoved = _allSteamGames.RemoveAll(item => item.Id.Equals(steamGame.Id));
|
|
|
|
if (numRemoved == 1)
|
|
{
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame: Removed Steam game with name {steamGame.Name}");
|
|
return true;
|
|
}
|
|
else if (numRemoved == 0)
|
|
{
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame: Didn't remove Steam game with ID {steamGame.Name} from the Steam Library");
|
|
return false;
|
|
}
|
|
else
|
|
throw new SteamLibraryException();
|
|
}
|
|
|
|
public override bool RemoveGameById(string steamGameId)
|
|
{
|
|
if (steamGameId.Equals("0"))
|
|
return false;
|
|
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame2: Removing Steam game with ID {steamGameId} from the Steam library");
|
|
|
|
// Remove the steamGame from the list.
|
|
int numRemoved = _allSteamGames.RemoveAll(item => item.Id.Equals(steamGameId));
|
|
|
|
if (numRemoved == 1)
|
|
{
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame2: Removed Steam game with ID {steamGameId}");
|
|
return true;
|
|
}
|
|
else if (numRemoved == 0)
|
|
{
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame2: Didn't remove Steam game with ID {steamGameId} from the Steam Library");
|
|
return false;
|
|
}
|
|
else
|
|
throw new SteamLibraryException();
|
|
}
|
|
|
|
|
|
public override bool RemoveGame(string steamGameNameOrId)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(steamGameNameOrId))
|
|
return false;
|
|
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame3: Removing Steam game with Name or UUID {steamGameNameOrId} from the Steam library");
|
|
|
|
int numRemoved;
|
|
Match match = Regex.Match(steamGameNameOrId, steamAppIdRegex, RegexOptions.IgnoreCase);
|
|
if (match.Success)
|
|
numRemoved = _allSteamGames.RemoveAll(item => steamGameNameOrId.Equals(item.Id));
|
|
else
|
|
numRemoved = _allSteamGames.RemoveAll(item => steamGameNameOrId.Equals(item.Name));
|
|
|
|
if (numRemoved == 1)
|
|
{
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame3: Removed Steam game with Name or UUID {steamGameNameOrId} ");
|
|
return true;
|
|
}
|
|
else if (numRemoved == 0)
|
|
{
|
|
logger.Debug($"SteamLibrary/RemoveSteamGame3: Didn't remove Steam game with Name or UUID {steamGameNameOrId} from the Steam Library");
|
|
return false;
|
|
}
|
|
else
|
|
throw new SteamLibraryException();
|
|
|
|
}
|
|
|
|
public override bool ContainsGame(Game steamGame)
|
|
{
|
|
if (!(steamGame is SteamGame))
|
|
return false;
|
|
|
|
foreach (SteamGame testSteamGame in _allSteamGames)
|
|
{
|
|
if (testSteamGame.Id.Equals(steamGame.Id))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override bool ContainsGame(string steamGameNameOrUuid)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(steamGameNameOrUuid))
|
|
return false;
|
|
|
|
|
|
Match match = Regex.Match(steamGameNameOrUuid, steamAppIdRegex, RegexOptions.IgnoreCase);
|
|
if (match.Success)
|
|
{
|
|
foreach (SteamGame testSteamGame in _allSteamGames)
|
|
{
|
|
if (steamGameNameOrUuid.Equals(Convert.ToInt32(testSteamGame.Id)))
|
|
return true;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
foreach (SteamGame testSteamGame in _allSteamGames)
|
|
{
|
|
if (steamGameNameOrUuid.Equals(testSteamGame.Name))
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
public override bool ContainsGameById(string steamGameId)
|
|
{
|
|
foreach (SteamGame testSteamGame in _allSteamGames)
|
|
{
|
|
if (steamGameId == testSteamGame.Id)
|
|
return true;
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
public override Game GetGame(string steamGameNameOrUuid)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(steamGameNameOrUuid))
|
|
return null;
|
|
|
|
Match match = Regex.Match(steamGameNameOrUuid, steamAppIdRegex, RegexOptions.IgnoreCase);
|
|
if (match.Success)
|
|
{
|
|
foreach (SteamGame testSteamGame in _allSteamGames)
|
|
{
|
|
if (steamGameNameOrUuid.Equals(Convert.ToInt32(testSteamGame.Id)))
|
|
return testSteamGame;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
foreach (SteamGame testSteamGame in _allSteamGames)
|
|
{
|
|
if (steamGameNameOrUuid.Equals(testSteamGame.Name))
|
|
return testSteamGame;
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
public override Game GetGameById(string steamGameId)
|
|
{
|
|
foreach (SteamGame testSteamGame in _allSteamGames)
|
|
{
|
|
if (steamGameId == testSteamGame.Id)
|
|
return testSteamGame;
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
public override bool LoadInstalledGames()
|
|
{
|
|
try
|
|
{
|
|
|
|
if (!_isSteamInstalled)
|
|
{
|
|
// Steam isn't installed, so we return an empty list.
|
|
logger.Info($"SteamLibrary/LoadInstalledGames: Steam library is not installed");
|
|
return false;
|
|
}
|
|
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Steam Base Registry Key = HKLM\\{_registrySteamKey}");
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Steam Apps Registry Key = HKCU\\{_registryAppsKey}");
|
|
|
|
List<string> steamAppIdsInstalled = new List<string>();
|
|
// Now look for what games app id's are actually installed on this computer
|
|
using (RegistryKey steamAppsKey = Registry.CurrentUser.OpenSubKey(_registryAppsKey, RegistryKeyPermissionCheck.ReadSubTree))
|
|
{
|
|
if (steamAppsKey != null)
|
|
{
|
|
//
|
|
// Loop through the subKeys as they are the Steam Game IDs
|
|
foreach (string steamAppId in steamAppsKey.GetSubKeyNames())
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found SteamGameKeyName = {steamAppId}");
|
|
if (!String.IsNullOrWhiteSpace(steamAppId))
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: SteamGameKeyName is an int, so trying to see if it is an installed app");
|
|
string steamGameKeyFullName = $"{_registryAppsKey}\\{steamAppId}";
|
|
using (RegistryKey steamGameKey = Registry.CurrentUser.OpenSubKey(steamGameKeyFullName, RegistryKeyPermissionCheck.ReadSubTree))
|
|
{
|
|
// If the Installed Value is set to 1, then the game is installed
|
|
// We want to keep track of that for later
|
|
if ((int)steamGameKey.GetValue(@"Installed", 0) == 1)
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: {steamGameKeyFullName} contains an 'Installed' value so is an installed Steam App.");
|
|
// Add this Steam App ID to the list we're keeping for later
|
|
steamAppIdsInstalled.Add(steamAppId);
|
|
}
|
|
else
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: {steamGameKeyFullName} does not contain an 'Installed' value so can't be a Steam App.");
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (steamAppIdsInstalled.Count == 0)
|
|
{
|
|
// There aren't any game ids so return false
|
|
logger.Warn($"SteamLibrary/LoadInstalledGames: No Steam games installed in the Steam library");
|
|
return false;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// There isnt any steam registry key
|
|
logger.Warn($"SteamLibrary/LoadInstalledGames: Couldn't access the Steam Registry Key {_registrySteamKey}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Now we parse the steam appinfo.vdf to get access to things like:
|
|
// - The game name
|
|
// - THe game installation dir
|
|
// - Sometimes the game icon
|
|
// - Sometimes the game executable name (from which we can get the icon)
|
|
Dictionary<string, SteamAppInfo> steamAppInfo = new Dictionary<string, SteamAppInfo>();
|
|
|
|
string appInfoVdfFile = Path.Combine(_steamPath, "appcache", "appinfo.vdf");
|
|
var newAppInfo = new AppInfo();
|
|
newAppInfo.Read(appInfoVdfFile);
|
|
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found {newAppInfo.Apps.Count} apps in the {appInfoVdfFile} VDF file");
|
|
|
|
// Chec through all the apps we've extracted
|
|
foreach (var app in newAppInfo.Apps)
|
|
{
|
|
// We only care about the appIDs we have listed as actual games
|
|
// (The AppIds include all other DLC and Steam specific stuff too)
|
|
string detectedAppID = app.AppID.ToString();
|
|
if (steamAppIdsInstalled.Contains(detectedAppID))
|
|
{
|
|
|
|
try
|
|
{
|
|
|
|
SteamAppInfo steamGameAppInfo = new SteamAppInfo
|
|
{
|
|
GameID = detectedAppID,
|
|
GameExes = new List<string>()
|
|
};
|
|
string steamAppType = "";
|
|
|
|
foreach (KVObject data in app.Data)
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found App: {app.AppID} - Data.Name: {data.Name}");
|
|
|
|
if (data.Name == "common")
|
|
{
|
|
foreach (KVObject common in data.Children)
|
|
{
|
|
|
|
if (common.Name == "name")
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: name: App: {app.AppID} - Common {common.Name}: {common.Value}");
|
|
steamGameAppInfo.GameName = common.Value.ToString();
|
|
}
|
|
else if (common.Name == "clienticon")
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: clienticon: App: {app.AppID} - Common {common.Name}: {common.Value}");
|
|
steamGameAppInfo.GameIconPath = Path.Combine(_steamPath, @"steam", @"games", String.Concat(common.Value, @".ico"));
|
|
}
|
|
else if (common.Name == "type")
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: type: App: {app.AppID} - Common {common.Name}: {common.Value}");
|
|
steamAppType = common.Value.ToString();
|
|
}
|
|
else
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found unrecognised line App: {app.AppID} - Common {common.Name}: {common.Value}");
|
|
}
|
|
}
|
|
}
|
|
else if (data.Name == "config")
|
|
{
|
|
foreach (KVObject config in data.Children)
|
|
{
|
|
//Console.WriteLine($"App: {app.AppID} - Config {config.Name}: {config.Value}");
|
|
|
|
if (config.Name == "installdir")
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found installdir App: {detectedAppID} - Config {config.Name}: {config.Value}");
|
|
steamGameAppInfo.GameInstallDir = config.Value.ToString();
|
|
}
|
|
else if (config.Name == "launch")
|
|
{
|
|
foreach (KVObject launch in config.Children)
|
|
{
|
|
foreach (KVObject launch_num in launch.Children)
|
|
{
|
|
if (launch_num.Name == "executable")
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found launch executable App: {detectedAppID} - Config - Launch {launch.Name} - {launch_num.Name}: {launch_num.Value}");
|
|
steamGameAppInfo.GameExes.Add(launch_num.Value.ToString());
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
// Only store the app if it's a game!
|
|
if (steamAppType.Equals("Game",StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
steamAppInfo.Add(detectedAppID, steamGameAppInfo);
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Adding Game with ID {detectedAppID} '{steamGameAppInfo.GameName}' to the list of games");
|
|
}
|
|
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
logger.Warn(ex, $"SteamLibrary/LoadInstalledGames: ArgumentException while processing the {appInfoVdfFile} VDF file");
|
|
//we just want to ignore it if we try to add it twice....
|
|
}
|
|
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found end of loop App: {detectedAppID} - Token: {app.Token}");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now we access the config.vdf that lives in the Steam Config file, as that lists all
|
|
// the SteamLibraries. We need to find out where they areso we can interrogate them
|
|
_steamConfigVdfFile = Path.Combine(_steamPath, "config", "config.vdf");
|
|
string steamConfigVdfText = File.ReadAllText(_steamConfigVdfFile, Encoding.UTF8);
|
|
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Processing the {_steamConfigVdfFile} VDF file");
|
|
|
|
List<string> steamLibrariesPaths = new List<string>();
|
|
// We add the default library which is based on where Steam was installed
|
|
logger.Info($"SteamLibrary/LoadInstalledGames: Found original steam library {_steamPath}");
|
|
steamLibrariesPaths.Add(_steamPath);
|
|
|
|
// Now we have to parse the config.vdf looking for the location of any additional SteamLibraries
|
|
// We look for lines similar to this: "BaseInstallFolder_1" "E:\\SteamLibrary"
|
|
// There may be multiple so we need to check the whole file
|
|
Regex steamLibrariesRegex = new Regex(@"""BaseInstallFolder_\d+""\s+""(.*)""", RegexOptions.IgnoreCase);
|
|
// Try to match all lines against the Regex.
|
|
MatchCollection steamLibrariesMatches = steamLibrariesRegex.Matches(steamConfigVdfText);
|
|
// If at least one of them matched!
|
|
foreach (Match steamLibraryMatch in steamLibrariesMatches)
|
|
{
|
|
if (steamLibraryMatch.Success)
|
|
{
|
|
string steamLibraryPath = Regex.Unescape(steamLibraryMatch.Groups[1].Value);
|
|
logger.Info($"SteamLibrary/LoadInstalledGames: Found additional steam library {steamLibraryPath}");
|
|
steamLibrariesPaths.Add(steamLibraryPath);
|
|
}
|
|
}
|
|
// Now we go off and find the details for the games in each Steam Library
|
|
foreach (string steamLibraryPath in steamLibrariesPaths)
|
|
{
|
|
// Work out the path to the appmanifests for this steamLibrary
|
|
string steamLibraryAppManifestPath = Path.Combine(steamLibraryPath, @"steamapps");
|
|
// Get the names of the App Manifests for the games installed in this SteamLibrary
|
|
string[] steamLibraryAppManifestFilenames = Directory.GetFiles(steamLibraryAppManifestPath, "appmanifest_*.acf");
|
|
// Go through each app and extract it's details
|
|
foreach (string steamLibraryAppManifestFilename in steamLibraryAppManifestFilenames)
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found {steamLibraryAppManifestFilename} app manifest within steam library {steamLibraryPath}");
|
|
// Read in the contents of the file
|
|
string steamLibraryAppManifestText = File.ReadAllText(steamLibraryAppManifestFilename);
|
|
// Grab the appid from the file
|
|
Regex appidRegex = new Regex(@"""appid""\s+""(\d+)""", RegexOptions.IgnoreCase);
|
|
Match appidMatches = appidRegex.Match(steamLibraryAppManifestText);
|
|
if (appidMatches.Success)
|
|
{
|
|
if (!String.IsNullOrWhiteSpace(appidMatches.Groups[1].Value))
|
|
{
|
|
string steamGameId = appidMatches.Groups[1].Value;
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Found Steam Game ID {steamGameId} within {steamLibraryAppManifestFilename} steam app manifest within steam library {steamLibraryPath}");
|
|
// Check if this game is one that was installed
|
|
if (steamAppInfo.ContainsKey(steamGameId))
|
|
{
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Steam Game ID {steamGameId} is installed within steam library {steamLibraryPath}!");
|
|
// This game is an installed game! so we start to populate it with data!
|
|
string steamGameExe = "";
|
|
|
|
string steamGameName = steamAppInfo[steamGameId].GameName;
|
|
|
|
// Construct the full path to the game dir from the appInfo and libraryAppManifest data
|
|
string steamGameInstallDir = Path.Combine(steamLibraryPath, @"steamapps", @"common", steamAppInfo[steamGameId].GameInstallDir);
|
|
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game ID {steamGameId} at {steamGameInstallDir }");
|
|
|
|
// And finally we try to populate the 'where', to see what gets run
|
|
// And so we can extract the process name
|
|
if (steamAppInfo[steamGameId].GameExes.Count > 0)
|
|
{
|
|
foreach (string gameExe in steamAppInfo[steamGameId].GameExes)
|
|
{
|
|
steamGameExe = Path.Combine(steamGameInstallDir, gameExe);
|
|
logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
|
|
// If the game executable exists, then we can proceed
|
|
if (File.Exists(steamGameExe))
|
|
{
|
|
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Next, we need to get the Icons we want to use, and make sure it's the latest one.
|
|
string steamGameIconPath = "";
|
|
// First of all, we attempt to use the Icon that Steam has cached, if it's available, as that will be updated to the latest
|
|
if (File.Exists(steamAppInfo[steamGameId].GameIconPath) && steamAppInfo[steamGameId].GameIconPath.EndsWith(".ico"))
|
|
{
|
|
steamGameIconPath = steamAppInfo[steamGameId].GameIconPath;
|
|
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
|
|
|
|
}
|
|
// If there isn't an icon for us to use, then we need to extract one from the Game Executables
|
|
else if (!String.IsNullOrEmpty(steamGameExe))
|
|
{
|
|
steamGameIconPath = steamGameExe;
|
|
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
|
|
}
|
|
// The absolute worst case means we don't have an icon to use. SO we use the Steam one.
|
|
else
|
|
{
|
|
// And we have to make do with a Steam Icon
|
|
logger.Debug($"SteamLibrary/LoadInstalledGames: Couldn't find Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} so using default Steam Icon");
|
|
steamGameIconPath = _steamPath;
|
|
}
|
|
|
|
// And we add the Game to the list of games we have!
|
|
_allSteamGames.Add(new SteamGame(steamGameId, steamGameName, steamGameExe, steamGameIconPath));
|
|
logger.Debug($"SteamLibrary/LoadInstalledGames: Adding Steam Game with game id {steamGameId}, name {steamGameName}, game exe {steamGameExe} and icon path {steamGameIconPath}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
logger.Info($"SteamLibrary/LoadInstalledGames: Found {_allSteamGames.Count} installed Steam games");
|
|
}
|
|
catch (ArgumentNullException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/GetAllInstalledGames: An argument supplied to the function is null.");
|
|
}
|
|
catch (NotSupportedException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/GetAllInstalledGames: The invoked method is not supported or reading, seeking or writing tp a stream that isn't supported.");
|
|
}
|
|
catch (PathTooLongException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/GetAllInstalledGames: The path is longer than the maximum allowed by the operating system.");
|
|
}
|
|
catch (SecurityException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/GetAllInstalledGames: The user does not have the permissions required to read the Uplay InstallDir registry key.");
|
|
}
|
|
catch (ObjectDisposedException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/GetAllInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Uplay InstallDir registry key (closed keys cannot be accessed).");
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/GetAllInstalledGames: The Uplay InstallDir registry key has been marked for deletion so we cannot access the value dueing the UplayLibrary check.");
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
logger.Warn(ex, "SteamLibrary/GetAllInstalledGames: The user does not have the necessary registry rights to check whether Uplay is installed.");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal)
|
|
{
|
|
string address = $@"steam://rungameid/{game.Id}";
|
|
if (!String.IsNullOrWhiteSpace(gameArguments))
|
|
{
|
|
address += @"//" + gameArguments;
|
|
}
|
|
Process gameProcess = Process.Start(address);
|
|
gameProcess.PriorityClass = processPriority;
|
|
return gameProcess;
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
|
|
[global::System.Serializable]
|
|
public class SteamLibraryException : GameLibraryException
|
|
{
|
|
public SteamLibraryException() { }
|
|
public SteamLibraryException(string message) : base(message) { }
|
|
public SteamLibraryException(string message, Exception inner) : base(message, inner) { }
|
|
protected SteamLibraryException(
|
|
System.Runtime.Serialization.SerializationInfo info,
|
|
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
|
|
}
|
|
|
|
}
|