diff --git a/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/App.cs b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/App.cs new file mode 100644 index 0000000..d68b33a --- /dev/null +++ b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/App.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ValveKeyValue; + +namespace HeliosPlus.GameLibraries.SteamAppInfoParser +{ + public class App + { + public uint AppID { get; set; } + + public uint Size { get; set; } + + public uint InfoState { get; set; } + + public DateTime LastUpdated { get; set; } + + public ulong Token { get; set; } + + public ReadOnlyCollection Hash { get; set; } + + public uint ChangeNumber { get; set; } + + public KVObject Data { get; set; } + } +} diff --git a/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/AppInfo.cs b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/AppInfo.cs new file mode 100644 index 0000000..caf1144 --- /dev/null +++ b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/AppInfo.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ValveKeyValue; + +namespace HeliosPlus.GameLibraries.SteamAppInfoParser +{ + class AppInfo + { + private const uint Magic = 0x07_56_44_27; + + public EUniverse Universe { get; set; } + + public List Apps { get; set; } = new List(); + + /// + /// Opens and reads the given filename. + /// + /// The file to open and read. + public void Read(string filename) + { + var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + Read(fs); + } + + /// + /// Reads the given . + /// + /// The input to read from. + public void Read(Stream input) + { + var reader = new BinaryReader(input); + var magic = reader.ReadUInt32(); + + if (magic != Magic) + { + throw new InvalidDataException($"Unknown magic header: {magic}"); + } + + Universe = (EUniverse)reader.ReadUInt32(); + + var deserializer = KVSerializer.Create(KVSerializationFormat.KeyValues1Binary); + + do + { + var appid = reader.ReadUInt32(); + + if (appid == 0) + { + break; + } + + var app = new App + { + AppID = appid, + Size = reader.ReadUInt32(), + InfoState = reader.ReadUInt32(), + LastUpdated = DateTimeFromUnixTime(reader.ReadUInt32()), + Token = reader.ReadUInt64(), + Hash = new ReadOnlyCollection(reader.ReadBytes(20)), + ChangeNumber = reader.ReadUInt32(), + Data = deserializer.Deserialize(input), + }; + + Apps.Add(app); + } while (true); + } + + public static DateTime DateTimeFromUnixTime(uint unixTime) + { + return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTime); + } + } +} diff --git a/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/EUniverse.cs b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/EUniverse.cs new file mode 100644 index 0000000..740da49 --- /dev/null +++ b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/EUniverse.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HeliosPlus.GameLibraries.SteamAppInfoParser +{ + public enum EUniverse + { + Invalid = 0, + Public = 1, + Beta = 2, + Internal = 3, + Dev = 4, + Max = 5, + } +} diff --git a/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/Package.cs b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/Package.cs new file mode 100644 index 0000000..58e9337 --- /dev/null +++ b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/Package.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ValveKeyValue; + +namespace HeliosPlus.GameLibraries.SteamAppInfoParser +{ + public class Package + { + public uint SubID { get; set; } + + public ReadOnlyCollection Hash { get; set; } + + public uint ChangeNumber { get; set; } + + public ulong Token { get; set; } + + public KVObject Data { get; set; } + } +} diff --git a/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/PackageInfo.cs b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/PackageInfo.cs new file mode 100644 index 0000000..dfedd7f --- /dev/null +++ b/HeliosDisplayManagement/GameLibraries/SteamAppInfoParser/PackageInfo.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ValveKeyValue; + +namespace HeliosPlus.GameLibraries.SteamAppInfoParser +{ + class PackageInfo + { + private const uint Magic = 0x06_56_55_28; + private const uint Magic27 = 0x06_56_55_27; + + public EUniverse Universe { get; set; } + + public List Packages { get; set; } = new List(); + + /// + /// Opens and reads the given filename. + /// + /// The file to open and read. + public void Read(string filename) + { + var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + Read(fs); + } + + /// + /// Reads the given . + /// + /// The input to read from. + public void Read(Stream input) + { + var reader = new BinaryReader(input); + var magic = reader.ReadUInt32(); + + if (magic != Magic && magic != Magic27) + { + throw new InvalidDataException($"Unknown magic header: {magic}"); + } + + Universe = (EUniverse)reader.ReadUInt32(); + + var deserializer = KVSerializer.Create(KVSerializationFormat.KeyValues1Binary); + + do + { + var subid = reader.ReadUInt32(); + + if (subid == 0xFFFFFFFF) + { + break; + } + + var package = new Package + { + SubID = subid, + Hash = new ReadOnlyCollection(reader.ReadBytes(20)), + ChangeNumber = reader.ReadUInt32(), + }; + + if (magic != Magic27) + { + package.Token = reader.ReadUInt64(); + } + + package.Data = deserializer.Deserialize(input); + + Packages.Add(package); + } while (true); + } + } +} diff --git a/HeliosDisplayManagement/GameLibraries/SteamGame.cs b/HeliosDisplayManagement/GameLibraries/SteamGame.cs new file mode 100644 index 0000000..dd20086 --- /dev/null +++ b/HeliosDisplayManagement/GameLibraries/SteamGame.cs @@ -0,0 +1,495 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Security; +using System.Drawing; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using HeliosPlus.Resources; +using HeliosPlus.Shared; +using HtmlAgilityPack; +using Microsoft.Win32; +using Newtonsoft.Json; +//using VdfParser; +using Gameloop.Vdf; +using System.Collections.ObjectModel; +using ValveKeyValue; +using System.Security.Cryptography; +using System.ServiceModel.Configuration; +using HeliosPlus.GameLibraries.SteamAppInfoParser; + + +namespace HeliosPlus.GameLibraries +{ + public class SteamGame + { + private static string _steamExe; + private static string _steamPath; + private static string _steamConfigVdfFile; + private static string _registrySteamKey = @"SOFTWARE\\Valve\\Steam"; + private static string _registryAppsKey = $@"{_registrySteamKey}\\Apps"; + private static SupportedGameLibrary _library = SupportedGameLibrary.Steam; + private string _gameRegistryKey; + private uint _steamGameId; + private string _steamGameName; + private string _steamGamePath; + private string _steamGameExe; + private Icon _steamGameIcon; + private static List _allSteamGames; + + private struct SteamAppInfo + { + public uint GameID; + public string GameName; + public List GameExes; + public string GameInstallDir; + public string GameSteamIconPath; + } + + static SteamGame() + { + ServicePointManager.ServerCertificateValidationCallback += + (send, certificate, chain, sslPolicyErrors) => true; + } + + + public SteamGame(uint steamGameId, string steamGameName, string steamGamePath, string steamGameExe, Icon steamGameIcon) + { + + _gameRegistryKey = $@"{_registryAppsKey}\\{steamGameId}"; + _steamGameId = steamGameId; + _steamGameName = steamGameName; + _steamGamePath = steamGamePath; + _steamGameExe = steamGameExe; + _steamGameIcon = steamGameIcon; + + } + + public uint GameId { get => _steamGameId; } + + public SupportedGameLibrary GameLibrary { get => SupportedGameLibrary.Steam; } + + public Icon GameIcon { get => _steamGameIcon; } + + public bool IsRunning + { + get + { + try + { + using ( + var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree)) + { + if ((int)key?.GetValue(@"Running", 0) == 1) + { + return true; + } + return false; + } + } + catch (SecurityException e) + { + if (e.Source != null) + Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + catch (IOException e) + { + // Extract some information from this exception, and then + // throw it to the parent method. + if (e.Source != null) + Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + } + } + + public bool IsUpdating + { + get + { + try + { + using ( + var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree)) + { + if ((int)key?.GetValue(@"Updating", 0) == 1) + { + return true; + } + return false; + } + } + catch (SecurityException e) + { + if (e.Source != null) + Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + catch (IOException e) + { + // Extract some information from this exception, and then + // throw it to the parent method. + if (e.Source != null) + Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + } + } + + public string GameName { get => _steamGameName; } + + public static string SteamExe { get => _steamExe; } + + public string GamePath { get => _steamGamePath; } + + public static List AllGames { get => _allSteamGames; } + + public static bool SteamInstalled + { + get + { + if (!string.IsNullOrWhiteSpace(SteamGame._steamExe) && File.Exists(SteamGame._steamExe)) + { + return true; + } + return false; + } + + } + + public static List GetAllInstalledGames() + { + List steamGameList = new List(); + _allSteamGames = steamGameList; + + try + { + + // Find the SteamExe location, and the SteamPath for later + using (var key = Registry.CurrentUser.OpenSubKey(_registrySteamKey, RegistryKeyPermissionCheck.ReadSubTree)) + { + _steamExe = (string)key?.GetValue(@"SteamExe", string.Empty) ?? string.Empty; + _steamExe = _steamExe.Replace('/','\\'); + _steamPath = (string)key?.GetValue(@"SteamPath", string.Empty) ?? string.Empty; + _steamPath = _steamPath.Replace('/', '\\'); + } + + if (_steamExe == string.Empty || !File.Exists(_steamExe)) + { + // Steam isn't installed, so we return an empty list. + return steamGameList; + } + + Icon _steamIcon = Icon.ExtractAssociatedIcon(_steamExe); + + List steamAppIdsInstalled = new List(); + // 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 steamGameKeyName in steamAppsKey.GetSubKeyNames()) + { + uint steamAppId = 0; + if (uint.TryParse(steamGameKeyName, out steamAppId)) + { + string steamGameKeyFullName = $"{_registryAppsKey}\\{steamGameKeyName}"; + 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) + { + // Add this Steam App ID to the list we're keeping for later + steamAppIdsInstalled.Add(steamAppId); + } + + } + + } + } + } + } + + // 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 steamAppInfo = new Dictionary(); + + string appInfoVdfFile = Path.Combine(_steamPath, "appcache", "appinfo.vdf"); + var newAppInfo = new AppInfo(); + newAppInfo.Read(appInfoVdfFile); + + Console.WriteLine($"{newAppInfo.Apps.Count} apps"); + + // 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) + if ( steamAppIdsInstalled.Contains(app.AppID)) + { + + try + { + + SteamAppInfo steamGameAppInfo = new SteamAppInfo(); + steamGameAppInfo.GameID = app.AppID; + steamGameAppInfo.GameExes = new List(); + + foreach (KVObject data in app.Data) + { + //Console.WriteLine($"App: {app.AppID} - Data.Name: {data.Name}"); + + if (data.Name == "common") + { + foreach (KVObject common in data.Children) { + + //Console.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}"); + + if (common.Name == "name") + { + Console.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}"); + steamGameAppInfo.GameName = common.Value.ToString(); + } + else if (common.Name == "clienticon") + { + Console.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}"); + steamGameAppInfo.GameSteamIconPath = Path.Combine(_steamPath, @"steam", @"games", String.Concat(common.Value, @".ico")); + } + else if (common.Name == "type") + { + Console.WriteLine($"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") + { + Console.WriteLine($"App: {app.AppID} - 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") + { + Console.WriteLine($"App: {app.AppID} - Config - Launch {launch.Name} - {launch_num.Name}: {launch_num.Value}"); + steamGameAppInfo.GameExes.Add(launch_num.Value.ToString()); + } + + } + } + } + } + } + + } + steamAppInfo.Add(app.AppID, steamGameAppInfo); + } + catch (ArgumentException e) + { + //we just want to ignore it if we try to add it twice.... + } + + Console.WriteLine($"App: {app.AppID} - 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); + + List steamLibrariesPaths = new List(); + // Now we have to parse the config.vdf looking for the location of the 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. + Match steamLibrariesMatches = steamLibrariesRegex.Match(steamConfigVdfText); + // If at least one of them matched! + if (steamLibrariesMatches.Success) + { + // Loop throug the results and add to an array + for (int i = 1; i < steamLibrariesMatches.Groups.Count; i++) + { + string steamLibraryPath = Regex.Unescape(steamLibrariesMatches.Groups[i].Value); + Console.WriteLine($"Found 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) + { + // 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) + { + + uint steamGameId = 0; + if (uint.TryParse(appidMatches.Groups[1].Value, out steamGameId)) + { + // Check if this game is one that was installed + if (steamAppInfo.ContainsKey(steamGameId)) { + // 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); + + // Next, we need to get the Icons we want to use, and make sure it's the latest one. + Icon steamGameIcon = null; + // 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].GameSteamIconPath)) + { + steamGameIcon = Icon.ExtractAssociatedIcon(steamAppInfo[steamGameId].GameSteamIconPath); + } + // If there isn't an icon for us to use, then we need to extract one from the Game Executables + else if (steamAppInfo[steamGameId].GameExes.Count > 0) + { + foreach (string gameExe in steamAppInfo[steamGameId].GameExes) + { + steamGameExe = Path.Combine(steamGameInstallDir, gameExe); + // If the game executable exists, then we can proceed + if (File.Exists(steamGameExe)) + { + // Now we need to get the Icon from the app if possible if it's not in the games folder + try + { + + steamGameIcon = Icon.ExtractAssociatedIcon(steamGameExe); + break; + } + catch (ArgumentException e) + { + // We drop out here if the executable didn't have an Icon + // That's fine, let's just try the next one! + } + } + } + + } + // 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 + steamGameIcon = _steamIcon; + } + + // 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); + // If the game executable exists, then we can proceed + if (File.Exists(steamGameExe)) + { + break; + } + } + + } + + // And we add the Game to the list of games we have! + steamGameList.Add(new SteamGame(steamGameId, steamGameName, steamGameInstallDir, steamGameExe, steamGameIcon)); + + } + } + } + } + } + } + catch (SecurityException e) + { + if (e.Source != null) + Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + catch (UnauthorizedAccessException e) + { + if (e.Source != null) + Console.WriteLine("UnauthorizedAccessException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + catch (ObjectDisposedException e) + { + if (e.Source != null) + Console.WriteLine("ObjectDisposedException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + catch (IOException e) + { + // Extract some information from this exception, and then + // throw it to the parent method. + if (e.Source != null) + Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + + return steamGameList; + } + + public override string ToString() + { + var name = _steamGameName; + + if (string.IsNullOrWhiteSpace(name)) + { + name = Language.Unknown; + } + + if (IsRunning) + { + return name + " " + Language.Running; + } + + if (IsUpdating) + { + return name + " " + Language.Updating; + } + + /*if (IsInstalled) + { + return name + " " + Language.Installed; + }*/ + + return name + " " + Language.Not_Installed; + } + + } +} \ No newline at end of file diff --git a/HeliosDisplayManagement/GameLibraries/UplayGame.cs b/HeliosDisplayManagement/GameLibraries/UplayGame.cs new file mode 100644 index 0000000..22a0d12 --- /dev/null +++ b/HeliosDisplayManagement/GameLibraries/UplayGame.cs @@ -0,0 +1,539 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Security; +using System.Drawing; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using HeliosPlus.Resources; +using HeliosPlus.Shared; +using HtmlAgilityPack; +using Microsoft.Win32; +using Newtonsoft.Json; + +namespace HeliosPlus.GameLibraries +{ + public class UplayGame + { + private static string _uplayExe; + private static string _uplayPath; + private static string _uplayConfigVdfFile; + private static string _registryUplayKey = @"SOFTWARE\\Valve\\Uplay"; + private static string _registryAppsKey = $@"{_registryUplayKey}\\Apps"; + private static SupportedGameLibrary _library = SupportedGameLibrary.Uplay; + //private static string _iconCachePath; + private string _gameRegistryKey; + private uint _uplayGameId; + private string _uplayGameName; + private string _uplayGamePath; + private string _uplayGameExe; + private Icon _uplayGameIcon; + private static List _allUplayGames; + + + + static UplayGame() + { + ServicePointManager.ServerCertificateValidationCallback += + (send, certificate, chain, sslPolicyErrors) => true; + } + + + public UplayGame(uint uplayGameId, string uplayGameName, string uplayGamePath, string uplayGameExe, Icon uplayGameIcon) + { + + _gameRegistryKey = $@"{_registryAppsKey}\\{uplayGameId}"; + _uplayGameId = uplayGameId; + _uplayGameName = uplayGameName; + //_iconCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + // Assembly.GetExecutingAssembly().GetName().Name, @"UplayIconCache"); + _uplayGamePath = uplayGamePath; + _uplayGameExe = uplayGameExe; + _uplayGameIcon = uplayGameIcon; + + } + + public uint GameId { get => _uplayGameId; } + + public SupportedGameLibrary GameLibrary { get => SupportedGameLibrary.Uplay; } + + public Icon GameIcon { get => _uplayGameIcon; } + + + /* public static string GameIdCacheFilePath + { + get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + Assembly.GetExecutingAssembly().GetName().Name, @"UplayGamesCache.json"); + } + */ + + public bool IsRunning + { + get + { + try + { + using ( + var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree)) + { + if ((int)key?.GetValue(@"Running", 0) == 1) + { + return true; + } + return false; + } + } + catch (SecurityException e) + { + if (e.Source != null) + Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + catch (IOException e) + { + // Extract some information from this exception, and then + // throw it to the parent method. + if (e.Source != null) + Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + } + } + + public bool IsUpdating + { + get + { + try + { + using ( + var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree)) + { + if ((int)key?.GetValue(@"Updating", 0) == 1) + { + return true; + } + return false; + } + } + catch (SecurityException e) + { + if (e.Source != null) + Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + catch (IOException e) + { + // Extract some information from this exception, and then + // throw it to the parent method. + if (e.Source != null) + Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + } + } + + public string GameName { get => _uplayGameName; } + + public static string UplayExe { get => _uplayExe; } + + public string GamePath { get => _uplayGamePath; } + + public static List AllGames { get => _allUplayGames; } + + public static bool UplayInstalled + { + get + { + if (!string.IsNullOrWhiteSpace(UplayGame._uplayExe) && File.Exists(UplayGame._uplayExe)) + { + return true; + } + return false; + } + + } + + /*public static List GetAllGames() + { + lock (AllGamesLock) + { + if (_allGames == null) + { + _allGames = GetCachedGameIds()?.ToList(); + } + } + + // Update only once + if (!_allGamesUpdated) + { + if (_allGames?.Count > 0) + { + UpdateGamesFromWeb(); + } + else + { + UpdateGamesFromWeb()?.Join(); + } + } + + return _allGames; + }*/ + + public static List GetAllInstalledGames() + { + List uplayGameList = new List(); + _allUplayGames = uplayGameList; + + try + { + + // Find the UplayExe location, and the UplayPath for later + using (var key = Registry.CurrentUser.OpenSubKey(_registryUplayKey, RegistryKeyPermissionCheck.ReadSubTree)) + { + _uplayExe = (string)key?.GetValue(@"UplayExe", string.Empty) ?? string.Empty; + _uplayPath = (string)key?.GetValue(@"UplayPath", string.Empty) ?? string.Empty; + } + + if (_uplayExe == string.Empty) + { + // Uplay isn't installed, so we return an empty list. + return uplayGameList; + } + + List uplayAppIdsInstalled = new List(); + // Now look for what games app id's are actually installed on this computer + using (RegistryKey uplayAppsKey = Registry.CurrentUser.OpenSubKey(_registryAppsKey, RegistryKeyPermissionCheck.ReadSubTree)) + { + if (uplayAppsKey != null) + { + // Loop through the subKeys as they are the Uplay Game IDs + foreach (string uplayGameKeyName in uplayAppsKey.GetSubKeyNames()) + { + uint uplayAppId = 0; + if (uint.TryParse(uplayGameKeyName, out uplayAppId)) + { + using (RegistryKey uplayGameKey = Registry.CurrentUser.OpenSubKey(uplayGameKeyName, 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 ((bool)uplayGameKey.GetValue(@"Installed", 0)) + { + // Add this Uplay App ID to the list we're keeping for later + uplayAppIdsInstalled.Add(uplayAppId); + } + + } + } + } + } + } + + // Now we access the config.vdf that lives in the Uplay Config file, as that lists all + // the UplayLibraries. We need to find out where they areso we can interrogate them + _uplayConfigVdfFile = Path.Combine(_uplayPath, @"config", @"config.vdf"); + string uplayConfigVdfText = File.ReadAllText(_uplayConfigVdfFile); + + List uplayLibrariesPaths = new List(); + // Now we have to parse the config.vdf looking for the location of the UplayLibraries + // We look for lines similar to this: "BaseInstallFolder_1" "E:\\UplayLibrary" + // There may be multiple so we need to check the whole file + Regex uplayLibrariesRegex = new Regex(@"""BaseInstallFolder_\d+""\s+""([^""])"")", RegexOptions.IgnoreCase); + // Try to match all lines against the Regex. + Match uplayLibrariesMatches = uplayLibrariesRegex.Match(uplayConfigVdfText); + // If at least one of them matched! + if (uplayLibrariesMatches.Success) + { + // Loop throug the results and add to an array + for (int i = 1; i <= uplayLibrariesMatches.Groups.Count; i++) + { + Console.WriteLine($"Found uplay library: {uplayLibrariesMatches.Groups[i].Value}"); + uplayLibrariesPaths.Add(uplayLibrariesMatches.Groups[i].Value); + } + } + + // Now we go off and find the details for the games in each Uplay Library + foreach (string uplayLibraryPath in uplayLibrariesPaths) + { + // Work out the path to the appmanifests for this uplayLibrary + string uplayLibraryAppManifestPath = Path.Combine(uplayLibraryPath, @"uplayapps"); + // Get the names of the App Manifests for the games installed in this UplayLibrary + string[] uplayLibraryAppManifestFilenames = Directory.GetFiles(uplayLibraryAppManifestPath, "appmanifest_*.acf"); + // Go through each app and extract it's details + foreach (string uplayLibraryAppManifestFilename in uplayLibraryAppManifestFilenames) + { + // Read in the contents of the file + string uplayLibraryAppManifestText = File.ReadAllText(uplayLibraryAppManifestFilename); + // Grab the appid from the file + Regex appidRegex = new Regex(@"""appid""\s+""(\d+)"")", RegexOptions.IgnoreCase); + Match appidMatches = appidRegex.Match(uplayLibraryAppManifestText); + if (appidMatches.Success) + { + + uint uplayGameId = 0; + string uplayGameName = String.Empty; + if (uint.TryParse(appidMatches.Groups[1].Value, out uplayGameId)) + { + // Check if this game is one that was installed + if (uplayAppIdsInstalled.Contains(uplayGameId)) + { + // This game is an installed game! so we start to populate it with data! + // Grab the Uplay game name from the app manifeest file + Regex nameRegex = new Regex(@"""name""\s+""([^""])"")", RegexOptions.IgnoreCase); + Match nameMatches = nameRegex.Match(uplayLibraryAppManifestText); + if (nameMatches.Success) + { + uplayGameName = nameMatches.Groups[1].Value; + + // We need to also get the installdir from the app manifeest file + Regex installDirRegex = new Regex(@"""installdir""\s+""([^""])"")", RegexOptions.IgnoreCase); + Match installDirMatches = installDirRegex.Match(uplayLibraryAppManifestText); + if (installDirMatches.Success) + { + // Construct the full path to the game dir + string uplayGameInstallDir = Path.Combine(uplayLibraryPath, @"uplayapps", @"common", installDirMatches.Groups[1].Value); + // Get the names of the *.config within the gamesdir. There is one per game, and it is the main game exe + // This is the one we want to get the icon from. + string[] uplayGameConfigs = Directory.GetFiles(uplayGameInstallDir, "*.config"); + // Pick the first one, and use that (as there should only be one). Derive the exe name from it + //string uplayGameExe = Path.Combine(uplayGameInstallDir, uplayGameConfigs[0].Remove(uplayGameConfigs[0].LastIndexOf(".config"))); + string uplayGameExe = Path.Combine(uplayGameInstallDir, Path.GetFileNameWithoutExtension(uplayGameConfigs[0])); + // Now we need to get the Icon + Icon uplayGameIcon = Icon.ExtractAssociatedIcon(uplayGameExe); + uplayGameList.Add(new UplayGame(uplayGameId, uplayGameName, uplayGameInstallDir, uplayGameExe, uplayGameIcon)); + } + + } + } + } + } + } + } + + + + } + catch (SecurityException e) + { + if (e.Source != null) + Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + catch (IOException e) + { + // Extract some information from this exception, and then + // throw it to the parent method. + if (e.Source != null) + Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message); + throw; + } + + return uplayGameList; + } + + /* public static string GetAppName(uint appId) + { + return GetAllGames()?.FirstOrDefault(g => g.AppId == appId)?.Name; + }*/ + + /* private static void CacheGameIds(IEnumerable gameIds) + { + try + { + var json = JsonConvert.SerializeObject(gameIds, Formatting.Indented); + + if (!string.IsNullOrWhiteSpace(json)) + { + var dir = Path.GetDirectoryName(GameIdCacheFilePath); + + if (dir != null) + { + Directory.CreateDirectory(dir); + File.WriteAllText(GameIdCacheFilePath, json, Encoding.Unicode); + } + } + } + catch + { + // ignored + } + }*/ + /* + private static GameLibraryAppDetails[] GetCachedGameIds() + { + try + { + if (File.Exists(GameIdCacheFilePath)) + { + var json = File.ReadAllText(GameIdCacheFilePath, Encoding.Unicode); + + if (!string.IsNullOrWhiteSpace(json)) + { + return JsonConvert.DeserializeObject(json); + } + } + } + catch + { + // ignored + } + + return null; + }*/ + + /* private static Thread UpdateGamesFromWeb() + { + if (_allGamesUpdated) + { + return null; + } + + _allGamesUpdated = true; + var thread = new Thread(() => + { + try + { + var newGames = new List(); + + using (var webClient = new WebClient()) + { + webClient.Headers.Add(@"User-Agent", + @"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0"); + webClient.Headers.Add(@"Accept", @"text/html,application/xhtml+xml,application/xml;"); + var response = webClient.OpenRead(@"https://uplaydb.info/api/GetAppList/"); + + if (response != null) + { + using (response) + { + using (var reader = new StreamReader(response)) + { + var content = reader.ReadToEnd(); + + if (!string.IsNullOrWhiteSpace(content)) + { + dynamic appids = JsonConvert.DeserializeObject(content); + + if (appids != null && appids.success == true) + { + foreach (var app in appids.data) + { + try + { + newGames.Add(new GameLibraryAppDetails(SupportedGameLibrary.Uplay, uint.Parse(app.Name), app.Value.Value)); + } + catch + { + // ignored + } + } + } + } + + reader.Close(); + } + + response.Close(); + } + } + } + + *//* if (newGames.Count > 0) + { + lock (AllGamesLock) + { + _allGames = newGames; + CacheGameIds(_allGames); + } + }*//* + } + catch + { + // ignored + } + }); + thread.Start(); + + return thread; + }*/ + + public override string ToString() + { + var name = _uplayGameName; + + if (string.IsNullOrWhiteSpace(name)) + { + name = Language.Unknown; + } + + if (IsRunning) + { + return name + " " + Language.Running; + } + + if (IsUpdating) + { + return name + " " + Language.Updating; + } + + /*if (IsInstalled) + { + return name + " " + Language.Installed; + }*/ + + return name + " " + Language.Not_Installed; + } + + /* public Task DownloadIcon() + { + return Task.Run(() => + { + if (!Directory.Exists(IconCachePath)) + { + try + { + Directory.CreateDirectory(IconCachePath); + } + catch + { + return null; + } + } + + var localPath = Path.Combine(IconCachePath, GameId + ".ico"); + + if (File.Exists(localPath)) + { + return localPath; + } + + var iconUrl = new HtmlWeb().Load("https://uplaydb.info/app/" + GameId) + .DocumentNode.SelectNodes("//a[@href]") + .Select(node => node.Attributes["href"].Value) + .FirstOrDefault(attribute => attribute.EndsWith(".ico") && attribute.Contains("/" + GameId + "/")); + + if (!string.IsNullOrWhiteSpace(iconUrl)) + { + try + { + using (var client = new WebClient()) + { + client.DownloadFile(iconUrl, localPath); + } + } + catch + { + return null; + } + } + + return File.Exists(localPath) ? localPath : null; + }); + }*/ + } +} \ No newline at end of file diff --git a/HeliosDisplayManagement/HeliosPlus.csproj b/HeliosDisplayManagement/HeliosPlus.csproj index a613766..8387ea3 100644 --- a/HeliosDisplayManagement/HeliosPlus.csproj +++ b/HeliosDisplayManagement/HeliosPlus.csproj @@ -68,6 +68,11 @@ + + + + + @@ -79,10 +84,8 @@ True Language.resx - - - - + + Form @@ -180,6 +183,9 @@ 2.7.0.7 + + 0.5.0 + 1.11.23 @@ -192,6 +198,9 @@ 12.0.3 + + 0.3.0.144 + 1.3.0.13 @@ -199,6 +208,18 @@ 1.6.0.4 + + + + + + + + + + + +