diff --git a/HeliosPlus/GameLibraries/SteamLibrary.cs b/HeliosPlus/GameLibraries/SteamLibrary.cs index cad06e9..7c6883a 100644 --- a/HeliosPlus/GameLibraries/SteamLibrary.cs +++ b/HeliosPlus/GameLibraries/SteamLibrary.cs @@ -295,13 +295,13 @@ namespace HeliosPlus.GameLibraries { // Find the SteamExe location, and the SteamPath for later - using (var key = Registry.CurrentUser.OpenSubKey(_registrySteamKey, RegistryKeyPermissionCheck.ReadSubTree)) + /*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)) { @@ -312,8 +312,8 @@ namespace HeliosPlus.GameLibraries //Icon _steamIcon = Icon.ExtractAssociatedIcon(_steamExe); //IconExtractor steamIconExtractor = new IconExtractor(_steamExe); //Icon _steamIcon = steamIconExtractor.GetIcon(0); - MultiIcon _steamIcon = new MultiIcon(); - _steamIcon.Load(_steamExe); + //MultiIcon _steamIcon = new MultiIcon(); + //_steamIcon.Load(_steamExe); List steamAppIdsInstalled = new List(); // Now look for what games app id's are actually installed on this computer diff --git a/HeliosPlus/GameLibraries/UplayConfigurationParser/UplayConfigurationParser.cs b/HeliosPlus/GameLibraries/UplayConfigurationParser/UplayConfigurationParser.cs new file mode 100644 index 0000000..5dfaf7d --- /dev/null +++ b/HeliosPlus/GameLibraries/UplayConfigurationParser/UplayConfigurationParser.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HeliosPlus.GameLibraries.UplayConfigurationParser +{ + class UplayConfigurationParser + { + + /*def _convert_data(self, data): + # calculate object size (konrad's formula) + if data > 256 * 256: + data = data - (128 * 256 * math.ceil(data / (256 * 256))) + data = data - (128 * math.ceil(data / 256)) + else: + if data > 256: + data = data - (128 * math.ceil(data / 256)) + return data*/ + + internal static decimal ConvertData (decimal data) + { + if (data > 65536) + { + data = data - (128 * 256 * Math.Ceiling(data / 65536)); + } + else if (data > 256) + { + data = data - (128 * Math.Ceiling(data / 256)); + } + + return data; + + } + + + + /*def _parse_configuration_header(self, header, second_eight= False): + try: + offset = 1 + multiplier = 1 + record_size = 0 + tmp_size = 0 + + if second_eight: + while header[offset] != 0x08 or(header[offset] == 0x08 and header[offset + 1] == 0x08) : + record_size += header[offset] * multiplier + multiplier *= 256 + offset += 1 + tmp_size += 1 + else: + while header[offset] != 0x08 or record_size == 0: + record_size += header[offset] * multiplier + multiplier *= 256 + offset += 1 + tmp_size += 1 + + record_size = self._convert_data(record_size) + + offset += 1 # skip 0x08 + + # look for launch_id + multiplier = 1 + launch_id = 0 + + while header[offset] != 0x10 or header[offset + 1] == 0x10: + launch_id += header[offset] * multiplier + multiplier *= 256 + offset += 1 + + launch_id = self._convert_data(launch_id) + + offset += 1 # skip 0x10 + + multiplier = 1 + launch_id_2 = 0 + while header[offset] != 0x1A or(header[offset] == 0x1A and header[offset + 1] == 0x1A) : + launch_id_2 += header[offset] * multiplier + multiplier *= 256 + offset += 1 + + launch_id_2 = self._convert_data(launch_id_2) + + #if object size is smaller than 128b, there might be a chance that secondary size will not occupy 2b + if record_size - offset < 128 <= record_size: + tmp_size -= 1 + record_size += 1 + +# we end up in the middle of header, return values normalized +# to end of record as well real yaml size and game launch_id + return record_size - offset, launch_id, launch_id_2, offset + tmp_size + 1 + except: +# something went horribly wrong, do not crash it, +# just return 0s, this way it will be handled later in the code +# 10 is to step a little in configuration file in order to find next game + return 0, 0, 0, 10*/ + + //internal static decimal ParseConfigurationHeader(decimal data); + } +} diff --git a/HeliosPlus/GameLibraries/UplayGame.cs b/HeliosPlus/GameLibraries/UplayGame.cs index 2d3111d..3fef5e1 100644 --- a/HeliosPlus/GameLibraries/UplayGame.cs +++ b/HeliosPlus/GameLibraries/UplayGame.cs @@ -21,36 +21,39 @@ using System.Collections.ObjectModel; using ValveKeyValue; using System.Security.Cryptography; using System.ServiceModel.Configuration; -using HeliosPlus.GameLibraries.SteamAppInfoParser; +//using HeliosPlus.GameLibraries.UplayAppInfoParser; using TsudaKageyu; using System.Drawing.IconLib; using System.Drawing.IconLib.Exceptions; +using System.Diagnostics; namespace HeliosPlus.GameLibraries { - public class UplayGame + public class UplayGame : Game { - private static string _uplayExe; - private static string _uplayPath; + /*private static string UplayLibrary.UplayExe; + private static string UplayLibrary.UplayPath; private static string _uplayConfigVdfFile; private static string _registryUplayKey = @"SOFTWARE\\Valve\\Uplay"; - private static string _registryAppsKey = $@"{_registryUplayKey}\\Apps"; + private static string _registryAppsKey = $@"{_registryUplayKey}\\Apps";*/ private string _gameRegistryKey; private uint _uplayGameId; private string _uplayGameName; - private string _uplayGamePath; + private string _uplayGameExePath; + private string _uplayGameDir; private string _uplayGameExe; + private string _uplayGameProcessName; private string _uplayGameIconPath; - private static List _allUplayGames; + private static List _allInstalledUplayGames = null; - private struct UplayAppInfo + /*private struct UplayAppInfo { - public uint GameID; + public uint GameID; public string GameName; public List GameExes; public string GameInstallDir; public string GameUplayIconPath; - } + }*/ static UplayGame() { @@ -59,38 +62,60 @@ namespace HeliosPlus.GameLibraries } - public UplayGame(uint uplayGameId, string uplayGameName, string uplayGamePath, string uplayGameExe, string uplayGameIconPath) + public UplayGame(uint uplayGameId, string uplayGameName, string uplayGameExePath, string uplayGameIconPath) { - _gameRegistryKey = $@"{_registryAppsKey}\\{uplayGameId}"; + _gameRegistryKey = $@"{UplayLibrary.registryUplayInstallsKey}\\{uplayGameId}"; _uplayGameId = uplayGameId; _uplayGameName = uplayGameName; - _uplayGamePath = uplayGamePath; - _uplayGameExe = uplayGameExe; + _uplayGameExePath = uplayGameExePath; + _uplayGameDir = Path.GetDirectoryName(uplayGameExePath); + _uplayGameExe = Path.GetFileName(_uplayGameExePath); + _uplayGameProcessName = Path.GetFileNameWithoutExtension(_uplayGameExePath); _uplayGameIconPath = uplayGameIconPath; - // 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; - _uplayExe = _uplayExe.Replace('/', '\\'); - _uplayPath = (string)key?.GetValue(@"UplayPath", string.Empty) ?? string.Empty; - _uplayPath = _uplayPath.Replace('/', '\\'); - } - } - public uint GameId { get => _uplayGameId; } + public override uint Id + { + get => _uplayGameId; + set => _uplayGameId = value; + } - public static SupportedGameLibrary GameLibrary { get => SupportedGameLibrary.Uplay; } + public override string Name + { + get => _uplayGameName; + set => _uplayGameName = value; + } - public string GameIconPath { get => _uplayGameIconPath; } + public override SupportedGameLibrary GameLibrary + { + get => SupportedGameLibrary.Uplay; + } - public bool IsRunning + public override string IconPath + { + get => _uplayGameIconPath; + set => _uplayGameIconPath = value; + } + + public override string ExePath + { + get => _uplayGameExePath; + set => _uplayGameExePath = value; + } + + public override string Directory + { + get => _uplayGameExePath; + set => _uplayGameExePath = value; + } + + public override bool IsRunning { get { - try + /*try { using ( var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree)) @@ -104,7 +129,7 @@ namespace HeliosPlus.GameLibraries } catch (SecurityException ex) { - Console.WriteLine($"SteamGame/IsUpdating securityexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); + Console.WriteLine($"UplayGame/IsRunning securityexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); if (ex.Source != null) Console.WriteLine("SecurityException source: {0} - Message: {1}", ex.Source, ex.Message); throw; @@ -113,15 +138,20 @@ namespace HeliosPlus.GameLibraries { // Extract some information from this exception, and then // throw it to the parent method. - Console.WriteLine($"SteamGame/IsUpdating ioexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); + Console.WriteLine($"UplayGame/IsRunning ioexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); if (ex.Source != null) Console.WriteLine("IOException source: {0} - Message: {1}", ex.Source, ex.Message); throw; - } + }*/ + + bool isRunning = Process.GetProcessesByName(_uplayGameProcessName) + .FirstOrDefault(p => p.MainModule.FileName + .StartsWith(ExePath, StringComparison.OrdinalIgnoreCase)) != default(Process); + return isRunning; } } - public bool IsUpdating + public override bool IsUpdating { get { @@ -139,7 +169,7 @@ namespace HeliosPlus.GameLibraries } catch (SecurityException ex) { - Console.WriteLine($"SteamGame/IsUpdating securityexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); + Console.WriteLine($"UplayGame/IsUpdating securityexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); if (ex.Source != null) Console.WriteLine("SecurityException source: {0} - Message: {1}", ex.Source, ex.Message); throw; @@ -148,7 +178,7 @@ namespace HeliosPlus.GameLibraries { // Extract some information from this exception, and then // throw it to the parent method. - Console.WriteLine($"SteamGame/IsUpdating ioexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); + Console.WriteLine($"UplayGame/IsUpdating ioexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); if (ex.Source != null) Console.WriteLine("IOException source: {0} - Message: {1}", ex.Source, ex.Message); throw; @@ -156,324 +186,17 @@ namespace HeliosPlus.GameLibraries } } - 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 + public bool CopyTo(UplayGame uplayGame) { - get - { - if (!string.IsNullOrWhiteSpace(UplayGame._uplayExe) && File.Exists(UplayGame._uplayExe)) - { - return true; - } + if (!(uplayGame is UplayGame)) return false; - } - } - - 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; - _uplayExe = _uplayExe.Replace('/', '\\'); - _uplayPath = (string)key?.GetValue(@"UplayPath", string.Empty) ?? string.Empty; - _uplayPath = _uplayPath.Replace('/', '\\'); - } - - if (_uplayExe == string.Empty || !File.Exists(_uplayExe)) - { - // Uplay isn't installed, so we return an empty list. - return uplayGameList; - } - - //Icon _uplayIcon = Icon.ExtractAssociatedIcon(_uplayExe); - //IconExtractor uplayIconExtractor = new IconExtractor(_uplayExe); - //Icon _uplayIcon = uplayIconExtractor.GetIcon(0); - MultiIcon _uplayIcon = new MultiIcon(); - _uplayIcon.Load(_uplayExe); - - 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)) - { - string uplayGameKeyFullName = $"{_registryAppsKey}\\{uplayGameKeyName}"; - using (RegistryKey uplayGameKey = Registry.CurrentUser.OpenSubKey(uplayGameKeyFullName, 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)uplayGameKey.GetValue(@"Installed", 0) == 1) - { - // Add this Uplay App ID to the list we're keeping for later - uplayAppIdsInstalled.Add(uplayAppId); - } - - } - - } - } - } - } - - // Now we parse the uplay 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 uplayAppInfo = new Dictionary(); - - string appInfoVdfFile = Path.Combine(_uplayPath, "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 Uplay specific stuff too) - if (uplayAppIdsInstalled.Contains(app.AppID)) - { - - try - { - - UplayAppInfo uplayGameAppInfo = new UplayAppInfo(); - uplayGameAppInfo.GameID = app.AppID; - uplayGameAppInfo.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}"); - uplayGameAppInfo.GameName = common.Value.ToString(); - } - else if (common.Name == "clienticon") - { - Console.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}"); - uplayGameAppInfo.GameUplayIconPath = Path.Combine(_uplayPath, @"uplay", @"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}"); - uplayGameAppInfo.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}"); - uplayGameAppInfo.GameExes.Add(launch_num.Value.ToString()); - } - - } - } - } - } - } - - } - uplayAppInfo.Add(app.AppID, uplayGameAppInfo); - } - catch (ArgumentException ex) - { - Console.WriteLine($"UplayGame/GetAllInstalledGames argumentexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); - //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 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, Encoding.UTF8); - - 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++) - { - string uplayLibraryPath = Regex.Unescape(uplayLibrariesMatches.Groups[i].Value); - Console.WriteLine($"Found uplay library: {uplayLibraryPath}"); - uplayLibrariesPaths.Add(uplayLibraryPath); - } - } - - // 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; - if (uint.TryParse(appidMatches.Groups[1].Value, out uplayGameId)) - { - // Check if this game is one that was installed - if (uplayAppInfo.ContainsKey(uplayGameId)) - { - // This game is an installed game! so we start to populate it with data! - string uplayGameExe = ""; - - string uplayGameName = uplayAppInfo[uplayGameId].GameName; - - // Construct the full path to the game dir from the appInfo and libraryAppManifest data - string uplayGameInstallDir = Path.Combine(uplayLibraryPath, @"uplayapps", @"common", uplayAppInfo[uplayGameId].GameInstallDir); - - // Next, we need to get the Icons we want to use, and make sure it's the latest one. - string uplayGameIconPath = ""; - // First of all, we attempt to use the Icon that Uplay has cached, if it's available, as that will be updated to the latest - if (File.Exists(uplayAppInfo[uplayGameId].GameUplayIconPath)) - { - uplayGameIconPath = uplayAppInfo[uplayGameId].GameUplayIconPath; - } - // If there isn't an icon for us to use, then we need to extract one from the Game Executables - else if (uplayAppInfo[uplayGameId].GameExes.Count > 0) - { - foreach (string gameExe in uplayAppInfo[uplayGameId].GameExes) - { - uplayGameExe = Path.Combine(uplayGameInstallDir, gameExe); - // If the game executable exists, then we can proceed - if (File.Exists(uplayGameExe)) - { - // Now we need to get the Icon from the app if possible if it's not in the games folder - uplayGameIconPath = uplayGameExe; - } - } - - } - // The absolute worst case means we don't have an icon to use. SO we use the Uplay one. - else - { - // And we have to make do with a Uplay Icon - uplayGameIconPath = _uplayPath; - - } - - // And finally we try to populate the 'where', to see what gets run - // And so we can extract the process name - if (uplayAppInfo[uplayGameId].GameExes.Count > 0) - { - foreach (string gameExe in uplayAppInfo[uplayGameId].GameExes) - { - uplayGameExe = Path.Combine(uplayGameInstallDir, gameExe); - // If the game executable exists, then we can proceed - if (File.Exists(uplayGameExe)) - { - break; - } - } - - } - - // And we add the Game to the list of games we have! - uplayGameList.Add(new UplayGame(uplayGameId, uplayGameName, uplayGameInstallDir, uplayGameExe, uplayGameIconPath)); - - } - } - } - } - } - } - 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 uplayGameList; + // Copy all the game data over to the other game + uplayGame.IconPath = IconPath; + uplayGame.Id = Id; + uplayGame.Name = Name; + uplayGame.ExePath = ExePath; + return true; } public override string ToString() @@ -495,12 +218,7 @@ namespace HeliosPlus.GameLibraries return name + " " + Language.Updating; } - /*if (IsInstalled) - { - return name + " " + Language.Installed; - }*/ - - return name + " " + Language.Not_Installed; + return name; } } diff --git a/HeliosPlus/GameLibraries/UplayLibrary.cs b/HeliosPlus/GameLibraries/UplayLibrary.cs new file mode 100644 index 0000000..97f42da --- /dev/null +++ b/HeliosPlus/GameLibraries/UplayLibrary.cs @@ -0,0 +1,436 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using ValveKeyValue; +//using HeliosPlus.GameLibraries.UplayAppInfoParser; +using Microsoft.Win32; +using System.IO; +using System.Drawing.IconLib; +using System.Security; +using System.Diagnostics; +using EDIDParser; +using System.ComponentModel; + +namespace HeliosPlus.GameLibraries +{ + public static class UplayLibrary + { + #region Class Variables + // Common items to the class + private static List _allUplayGames = new List(); + private static string uplayAppIdRegex = @"/^[0-9A-F]{1,10}$"; + private static string _uplayExe; + private static string _uplayPath; + private static string _uplayConfigVdfFile; + internal static string registryUplayLauncherKey = @"SOFTWARE\WOW6432Node\Ubisoft\Launcher"; + internal static string registryUplayInstallsKey = @"SOFTWARE\WOW6432Node\Ubisoft\Launcher\Installs"; + internal static string registryUplayOpenCmdKey = @"SOFTWARE\Classes\uplay\Shell\Open\Command"; + + // Other constants that are useful + #endregion + + private struct UplayAppInfo + { + public uint GameID; + public string GameName; + public List GameExes; + public string GameInstallDir; + public string GameUplayIconPath; + } + + #region Class Constructors + static UplayLibrary() + { + // Find the UplayExe location, and the UplayPath for later + RegistryKey uplayInstallKey = Registry.LocalMachine.OpenSubKey(registryUplayLauncherKey, RegistryKeyPermissionCheck.ReadSubTree); + _uplayPath = uplayInstallKey.GetValue("InstallDir", "C:\\Program Files (x86)\\Ubisoft\\Ubisoft Game Launcher\\").ToString(); + _uplayExe = $"{_uplayPath}upc.exe"; + } + #endregion + + #region Class Properties + public static List AllInstalledGames + { + get + { + // Load the Uplay Games from Uplay Client if needed + if (_allUplayGames == null) + LoadInstalledGames(); + return _allUplayGames; + } + } + + + public static int InstalledUplayGameCount + { + get + { + return _allUplayGames.Count; + } + } + + public static string UplayExe + { + get + { + return _uplayExe; + } + } + + public static string UplayPath + { + get + { + return _uplayPath; + } + } + + public static bool IsUplayInstalled + { + get + { + if (!string.IsNullOrWhiteSpace(UplayExe) && File.Exists(UplayExe)) + return true; + + return false; + } + + } + + + #endregion + + #region Class Methods + public static bool AddUplayGame(UplayGame uplayGame) + { + if (!(uplayGame is UplayGame)) + return false; + + // Doublecheck if it already exists + // Because then we just update the one that already exists + if (ContainsUplayGame(uplayGame)) + { + // We update the existing Shortcut with the data over + UplayGame uplayGameToUpdate = GetUplayGame(uplayGame.Id.ToString()); + uplayGame.CopyTo(uplayGameToUpdate); + } + else + { + // Add the uplayGame to the list of uplayGames + _allUplayGames.Add(uplayGame); + } + + //Doublecheck it's been added + if (ContainsUplayGame(uplayGame)) + { + return true; + } + else + return false; + + } + + public static bool RemoveUplayGame(UplayGame uplayGame) + { + if (!(uplayGame is UplayGame)) + return false; + + // Remove the uplayGame from the list. + int numRemoved = _allUplayGames.RemoveAll(item => item.Id.Equals(uplayGame.Id)); + + if (numRemoved == 1) + { + return true; + } + else if (numRemoved == 0) + return false; + else + throw new UplayLibraryException(); + } + + + public static bool RemoveUplayGame(string uplayGameNameOrUuid) + { + if (String.IsNullOrWhiteSpace(uplayGameNameOrUuid)) + return false; + + int numRemoved; + Match match = Regex.Match(uplayGameNameOrUuid, uplayAppIdRegex, RegexOptions.IgnoreCase); + if (match.Success) + numRemoved = _allUplayGames.RemoveAll(item => uplayGameNameOrUuid.Equals(Convert.ToUInt32(item.Id))); + else + numRemoved = _allUplayGames.RemoveAll(item => uplayGameNameOrUuid.Equals(item.Name)); + + if (numRemoved == 1) + return true; + else if (numRemoved == 0) + return false; + else + throw new UplayLibraryException(); + + } + + + public static bool ContainsUplayGame(UplayGame uplayGame) + { + if (!(uplayGame is UplayGame)) + return false; + + foreach (UplayGame testUplayGame in _allUplayGames) + { + if (testUplayGame.Id.Equals(uplayGame.Id)) + return true; + } + + return false; + } + + public static bool ContainsUplayGame(string uplayGameNameOrUuid) + { + if (String.IsNullOrWhiteSpace(uplayGameNameOrUuid)) + return false; + + + Match match = Regex.Match(uplayGameNameOrUuid, uplayAppIdRegex, RegexOptions.IgnoreCase); + if (match.Success) + { + foreach (UplayGame testUplayGame in _allUplayGames) + { + if (uplayGameNameOrUuid.Equals(Convert.ToUInt32(testUplayGame.Id))) + return true; + } + + } + else + { + foreach (UplayGame testUplayGame in _allUplayGames) + { + if (uplayGameNameOrUuid.Equals(testUplayGame.Name)) + return true; + } + + } + + return false; + + } + + public static bool ContainsUplayGame(uint uplayGameId) + { + foreach (UplayGame testUplayGame in _allUplayGames) + { + if (uplayGameId == testUplayGame.Id) + return true; + } + + + return false; + + } + + + public static UplayGame GetUplayGame(string uplayGameNameOrUuid) + { + if (String.IsNullOrWhiteSpace(uplayGameNameOrUuid)) + return null; + + Match match = Regex.Match(uplayGameNameOrUuid, uplayAppIdRegex, RegexOptions.IgnoreCase); + if (match.Success) + { + foreach (UplayGame testUplayGame in _allUplayGames) + { + if (uplayGameNameOrUuid.Equals(Convert.ToUInt32(testUplayGame.Id))) + return testUplayGame; + } + + } + else + { + foreach (UplayGame testUplayGame in _allUplayGames) + { + if (uplayGameNameOrUuid.Equals(testUplayGame.Name)) + return testUplayGame; + } + + } + + return null; + + } + + public static UplayGame GetUplayGame(uint uplayGameId) + { + foreach (UplayGame testUplayGame in _allUplayGames) + { + if (uplayGameId == testUplayGame.Id) + return testUplayGame; + } + + return null; + + } + + public static bool LoadInstalledGames() + { + try + { + + if (_uplayExe == string.Empty || !File.Exists(_uplayExe)) + { + // Uplay isn't installed, so we return an empty list. + return false; + } + + + // Look in HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Ubisoft\\Launcher and check the InstallDir key + // That returns the location of the install dir : E:\Program Files (x86)\Ubisoft\Ubisoft Game Launcher\ + + //RegistryKey uplayInstallKey = Registry.CurrentUser.OpenSubKey(registryUplayInstallsKey, RegistryKeyPermissionCheck.ReadSubTree); + //string uplayInstallDir = uplayInstallKey.GetValue("InstallDir", "C:\\Program Files (x86)\\Ubisoft\\Ubisoft Game Launcher\\").ToString(); + + // Access {installdir}\\cache\\configuration\\configurations file + string mypath = _uplayPath + @"cache\\configuration\\configurations"; + string uplayConfigFileString = File.ReadAllText(mypath); + uplayConfigFileString = uplayConfigFileString.Remove(0, 12); + // Split the file into records at the SOH unicode character + List uplayConfigFile = uplayConfigFileString.Split((Char)1).ToList(); + + // Go through every record and attempt to parse it + foreach (string uplayEntry in uplayConfigFile) { + // Skip any Uplay entry records that don't start with 'version:' + if (!uplayEntry.StartsWith("version:",StringComparison.InvariantCultureIgnoreCase)) + continue; + + //Split the record into entrylines + List uplayEntryLines = uplayEntry.Split('\n').ToList(); + + // Skip any records NOT containing an entryline with ' start_game:' (note 2 leading spaces) + // All games contain a start_game entry + if (!uplayEntryLines.Exists(a => a.StartsWith(" start_game:"))) + continue; + + // Skip any records containing an entryline with ' third_party_platform:' (note 2 leading spaces) + // We only want the native uplay games.... + if (uplayEntryLines.Exists(a => a.StartsWith(" third_party_platform:"))) + continue; + + // if we get here then we have a real game to parse! + // Yay us :). + + // First we check if there are any localization CONSTANTS that we will need to map later. + Dictionary localizations = new Dictionary(); + int localizationsIndex = uplayEntryLines.FindIndex(a => a == "localizations:"); + // If there are localizations, then we need to store them for later + if (localizationsIndex != -1) + { + // grab the localizations: -> default: entries to use as a lookup table for the info we need + int defaultIndex = localizationsIndex + 1; + int currentIndex = defaultIndex + 1; + + // Grab all EntryLines with 4 leading spaces (these are all the localizations) + while (uplayEntryLines[currentIndex].StartsWith(" ")){ + string[] split = uplayEntryLines[currentIndex].Trim().Split(':'); + localizations.Add(split[0], split[1]); + currentIndex++; + } + + } + + // for each game record grab: + // name: (lookup the id in lookup table to find the name if needed) + + + // thumb_image: (lookup the id in lookup table to find the thumbnail) + // icon_image: (lookup the id in lookup table to find the ICON) + // find the exe name looking at root: -> start_game: -> online: -> executables: -> path: -> relative: (get ACU.exe) + // Lookup the Game registry key from looking at root: -> start_game: -> online: -> executables: -> working_directory: -> register: (get HKEY_LOCAL_MACHINE\SOFTWARE\Ubisoft\Launcher\Installs\720\InstallDir) + // Extract the GameAppID from the number in the working directory (e.g. 720) + // Lookup the Game install path by reading the game registry key: D:/Ubisoft Game Launcher/Assassin's Creed Unity/ + // join the Game install path and the exe name to get the full game exe path: D:/Ubisoft Game Launcher/Assassin's Creed Unity/ACU.exe + // Then we have the gameID, the thumbimage, the icon, the name, the exe path + } + + + List uplayAppIdsInstalled = new List(); + // Now look for what games app id's are actually installed on this computer + using (RegistryKey uplayAppsKey = Registry.CurrentUser.OpenSubKey(registryUplayInstallsKey, 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)) + { + string uplayGameKeyFullName = $"{ registryUplayInstallsKey}\\{uplayGameKeyName}"; + using (RegistryKey uplayGameKey = Registry.CurrentUser.OpenSubKey(uplayGameKeyFullName, 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)uplayGameKey.GetValue(@"Installed", 0) == 1) + { + // Add this Uplay App ID to the list we're keeping for later + uplayAppIdsInstalled.Add(uplayAppId); + } + + } + + } + } + } + } + + } + catch (SecurityException ex) + { + Console.WriteLine($"UplayGame/GetAllInstalledGames securityexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); + if (ex.Source != null) + Console.WriteLine("SecurityException source: {0} - Message: {1}", ex.Source, ex.Message); + throw; + } + catch (UnauthorizedAccessException ex) + { + Console.WriteLine($"UplayGame/GetAllInstalledGames unauthorizedaccessexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); + if (ex.Source != null) + Console.WriteLine("UnauthorizedAccessException source: {0} - Message: {1}", ex.Source, ex.Message); + throw; + } + catch (ObjectDisposedException ex) + { + Console.WriteLine($"UplayGame/GetAllInstalledGames objectdisposedexceptions: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); + if (ex.Source != null) + Console.WriteLine("ObjectDisposedException source: {0} - Message: {1}", ex.Source, ex.Message); + throw; + } + catch (IOException ex) + { + Console.WriteLine($"UplayGame/GetAllInstalledGames ioexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); + // Extract some information from this exception, and then + // throw it to the parent method. + if (ex.Source != null) + Console.WriteLine("IOException source: {0} - Message: {1}", ex.Source, ex.Message); + throw; + } + + return true; + } + + #endregion + + } + + [global::System.Serializable] + public class UplayLibraryException : Exception + { + public UplayLibraryException() { } + public UplayLibraryException(string message) : base(message) { } + public UplayLibraryException(string message, Exception inner) : base(message, inner) { } + protected UplayLibraryException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } + +} diff --git a/HeliosPlus/HeliosPlus.csproj b/HeliosPlus/HeliosPlus.csproj index 452310f..33c0e0a 100644 --- a/HeliosPlus/HeliosPlus.csproj +++ b/HeliosPlus/HeliosPlus.csproj @@ -84,6 +84,8 @@ + + diff --git a/HeliosPlus/Program.cs b/HeliosPlus/Program.cs index 0b697de..03fece1 100644 --- a/HeliosPlus/Program.cs +++ b/HeliosPlus/Program.cs @@ -443,13 +443,11 @@ namespace HeliosPlus { { // Load Uplay library games Console.WriteLine("Program/LoadGamesInBackground : Loading Installed Uplay Games "); - /* if (!HeliosPlus.GameLibraries.UplayLibrary.LoadInstalledGames()) + if (!HeliosPlus.GameLibraries.UplayLibrary.LoadInstalledGames()) { // Somehow return that this profile topology didn't apply throw new LoadingInstalledGamesException("Program/LoadGamesInBackground: Cannot load installed Uplay Games!"); } - */ - }); // Store all the tasks in an array so we can wait on them later