diff --git a/DisplayMagician/DisplayMagician.csproj b/DisplayMagician/DisplayMagician.csproj index 2458831..3cec485 100644 --- a/DisplayMagician/DisplayMagician.csproj +++ b/DisplayMagician/DisplayMagician.csproj @@ -84,6 +84,7 @@ + @@ -100,9 +101,9 @@ + - diff --git a/DisplayMagician/GameLibraries/OriginGame.cs b/DisplayMagician/GameLibraries/OriginGame.cs index a66b115..3779dfb 100644 --- a/DisplayMagician/GameLibraries/OriginGame.cs +++ b/DisplayMagician/GameLibraries/OriginGame.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; -using System.Security; using DisplayMagician.Resources; -using Microsoft.Win32; using System.Diagnostics; namespace DisplayMagician.GameLibraries @@ -29,21 +27,22 @@ namespace DisplayMagician.GameLibraries } - public OriginGame(string originGameId, string originGameName, string originGameExePath, string originGameIconPath) + public OriginGame(string OriginGameId, string OriginGameName, string OriginGameExePath, string OriginGameIconPath) { - _gameRegistryKey = $@"{OriginLibrary.OriginAppsRegistryKey}\\{originGameId}"; - _originGameId = originGameId; - _originGameName = originGameName; - _originGameExePath = originGameExePath; - _originGameDir = Path.GetDirectoryName(originGameExePath); + //_gameRegistryKey = $@"{OriginLibrary.registryOriginInstallsKey}\\{OriginGameId}"; + _originGameId = OriginGameId; + _originGameName = OriginGameName; + _originGameExePath = OriginGameExePath; + _originGameDir = Path.GetDirectoryName(OriginGameExePath); _originGameExe = Path.GetFileName(_originGameExePath); _originGameProcessName = Path.GetFileNameWithoutExtension(_originGameExePath); - _originGameIconPath = originGameIconPath; + _originGameIconPath = OriginGameIconPath; } - public override string Id { + public override string Id + { get => _originGameId; set => _originGameId = value; } @@ -54,12 +53,14 @@ namespace DisplayMagician.GameLibraries set => _originGameName = value; } - public override SupportedGameLibrary GameLibrary { - get => SupportedGameLibrary.Origin; + public override SupportedGameLibrary GameLibrary + { + get => SupportedGameLibrary.Origin; } - public override string IconPath { - get => _originGameIconPath; + public override string IconPath + { + get => _originGameIconPath; set => _originGameIconPath = value; } @@ -84,21 +85,22 @@ namespace DisplayMagician.GameLibraries foreach (Process gameProcess in gameProcesses) { try - { - if (gameProcess.MainModule.FileName.StartsWith(_originGameExePath)) + { + if (gameProcess.ProcessName.Equals(_originGameProcessName)) numGameProcesses++; } catch (Exception ex) { - logger.Debug(ex, $"OriginGame/IsRunning: Accessing Process.MainModule caused exception. Trying GameUtils.GetMainModuleFilepath instead"); - + logger.Debug(ex, $"OriginGame/IsRunning: Accessing Process.ProcessName caused exception. Trying GameUtils.GetMainModuleFilepath instead"); // If there is a race condition where MainModule isn't available, then we // instead try the much slower GetMainModuleFilepath (which does the same thing) string filePath = GameUtils.GetMainModuleFilepath(gameProcess.Id); if (filePath == null) { // if we hit this bit then GameUtils.GetMainModuleFilepath failed, - // so we just skip that process + // so we just assume that the process is a game process + // as it matched the original process search + numGameProcesses++; continue; } else @@ -106,6 +108,7 @@ namespace DisplayMagician.GameLibraries if (filePath.StartsWith(_originGameExePath)) numGameProcesses++; } + } } if (numGameProcesses > 0) @@ -115,7 +118,8 @@ namespace DisplayMagician.GameLibraries } } - public override bool IsUpdating + // Have to do much more research to figure out how to detect when Origin is updating a game + /*public override bool IsUpdating { get { @@ -148,19 +152,19 @@ namespace DisplayMagician.GameLibraries throw; } } - } + }*/ - public bool CopyInto(OriginGame originGame) + public bool CopyTo(OriginGame OriginGame) { - if (!(originGame is OriginGame)) + if (!(OriginGame is OriginGame)) return false; // Copy all the game data over to the other game - originGame.IconPath = IconPath; - originGame.Id = Id; - originGame.Name = Name; - originGame.ExePath = ExePath; - originGame.Directory = Directory; + OriginGame.IconPath = IconPath; + OriginGame.Id = Id; + OriginGame.Name = Name; + OriginGame.ExePath = ExePath; + OriginGame.Directory = Directory; return true; } @@ -178,10 +182,10 @@ namespace DisplayMagician.GameLibraries return name + " " + Language.Running; } - if (IsUpdating) + /*if (IsUpdating) { return name + " " + Language.Updating; - } + }*/ return name; } diff --git a/DisplayMagician/GameLibraries/OriginLibrary.cs b/DisplayMagician/GameLibraries/OriginLibrary.cs index 150a291..36a6a18 100644 --- a/DisplayMagician/GameLibraries/OriginLibrary.cs +++ b/DisplayMagician/GameLibraries/OriginLibrary.cs @@ -1,15 +1,14 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; using System.Text.RegularExpressions; -//using ValveKeyValue; using Microsoft.Win32; -using Microsoft.QueryStringDotNET; using System.IO; using System.Security; -using System.Diagnostics; -using System.Net; -using System.Linq; +using System.Xml; +using System.Xml.Linq; +using System.Xml.XPath; +using System.Web; namespace DisplayMagician.GameLibraries { @@ -18,55 +17,70 @@ namespace DisplayMagician.GameLibraries #region Class Variables // Common items to the class private static List _allOriginGames = new List(); - private static string originAppIdRegex = @"/^[0-9A-F]{1,10}$"; + private static string OriginAppIdRegex = @"/^[0-9A-F]{1,10}$"; private static string _originExe; private static string _originPath; - private static string _originLocalContent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),"Origin"); + private static string _originLocalContent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Origin"); private static bool _isOriginInstalled = false; + + //private static string _originConfigVdfFile; + internal static string registryOriginLauncherKey = @"SOFTWARE\WOW6432Node\Origin"; + //internal static string registryOriginInstallsKey = @"SOFTWARE\WOW6432Node\Ubisoft\Launcher\Installs"; + //internal static string registryOriginOpenCmdKey = @"SOFTWARE\Classes\Origin\Shell\Open\Command"; private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + // Other constants that are useful #endregion private struct OriginAppInfo { - public int GameID; + public string GameID; public string GameName; - public List GameExes; + public string GameExePath; public string GameInstallDir; public string GameOriginIconPath; } - + #region Class Constructors static OriginLibrary() { try { + logger.Trace($"OriginLibrary/OriginLibrary: Origin launcher registry key = HKLM\\{registryOriginLauncherKey}"); // Find the OriginExe location, and the OriginPath for later - using (var key = Registry.CurrentUser.OpenSubKey(OriginLibrary.OriginRegistryKey, RegistryKeyPermissionCheck.ReadSubTree)) - { - _originExe = (string)key?.GetValue(@"OriginExe", string.Empty) ?? string.Empty; - _originExe = _originExe.Replace('/', '\\'); - _originPath = (string)key?.GetValue(@"OriginPath", string.Empty) ?? string.Empty; - _originPath = _originPath.Replace('/', '\\'); - } + RegistryKey OriginInstallKey = Registry.LocalMachine.OpenSubKey(registryOriginLauncherKey, RegistryKeyPermissionCheck.ReadSubTree); + if (OriginInstallKey == null) + return; + _originExe = OriginInstallKey.GetValue("ClientPath", @"C:\Program Files (x86)\Origin\Origin.exe").ToString(); + _originPath = _originExe; + _originPath = _originPath.Replace(@"\Origin.exe", ""); if (File.Exists(_originExe)) + { + logger.Info($"OriginLibrary/OriginLibrary: Origin library is installed in {_originPath}. Found {_originExe}"); _isOriginInstalled = true; + } + else + { + logger.Info($"OriginLibrary/OriginLibrary: Origin library is not installed!"); + } + } catch (SecurityException ex) { - logger.Warn(ex,"The user does not have the permissions required to read the Origin registry key."); + logger.Warn(ex, "OriginLibrary/OriginLibrary: The user does not have the permissions required to read the Origin ClientPath registry key."); } - catch (ObjectDisposedException ex) + catch(ObjectDisposedException ex) { - logger.Warn(ex, "The Microsoft.Win32.RegistryKey is closed when trying to access theOrigin registry key (closed keys cannot be accessed)."); + logger.Warn(ex, "OriginLibrary/OriginLibrary: The Microsoft.Win32.RegistryKey is closed when trying to access the Origin ClientPath registry key (closed keys cannot be accessed)."); } catch (IOException ex) { - logger.Warn(ex, "The Origin registry key has been marked for deletion so we cannot access the value during the OriginLibrary check."); + logger.Warn(ex, "OriginLibrary/OriginLibrary: The Origin ClientPath registry key has been marked for deletion so we cannot access the value dueing the OriginLibrary check."); } catch (UnauthorizedAccessException ex) { - logger.Warn(ex, "The user does not have the necessary registry rights to check whether Origin is installed."); + logger.Warn(ex, "OriginLibrary/OriginLibrary: The user does not have the necessary registry rights to check whether Origin is installed."); } } #endregion @@ -92,22 +106,6 @@ namespace DisplayMagician.GameLibraries } } - /* public static string OriginRegistryKey - { - get - { - return _registryOriginKey; - } - } - - public static string OriginAppsRegistryKey - { - get - { - return _registryAppsKey; - } - } -*/ public static string OriginExe { get @@ -141,18 +139,20 @@ namespace DisplayMagician.GameLibraries { if (!(originGame is OriginGame)) return false; - + // Doublecheck if it already exists // Because then we just update the one that already exists if (ContainsOriginGame(originGame)) { + logger.Debug($"OriginLibrary/AddOriginGame: Updating Origin game {originGame.Name} in our Origin library"); // We update the existing Shortcut with the data over OriginGame originGameToUpdate = GetOriginGame(originGame.Id.ToString()); - originGame.CopyInto(originGameToUpdate); + originGame.CopyTo(originGameToUpdate); } else { - // Add the originGame to the list of originGames + logger.Debug($"OriginLibrary/AddOriginGame: Adding Origin game {originGame.Name} to our Origin library"); + // Add the OriginGame to the list of OriginGames _allOriginGames.Add(originGame); } @@ -171,54 +171,73 @@ namespace DisplayMagician.GameLibraries if (!(originGame is OriginGame)) return false; - // Remove the originGame from the list. + logger.Debug($"OriginLibrary/RemoveOriginGame: Removing Origin game {originGame.Name} from our Origin library"); + + // Remove the OriginGame from the list. int numRemoved = _allOriginGames.RemoveAll(item => item.Id.Equals(originGame.Id)); if (numRemoved == 1) { + logger.Debug($"OriginLibrary/RemoveOriginGame: Removed Origin game with name {originGame.Name}"); return true; } else if (numRemoved == 0) + { + logger.Debug($"OriginLibrary/RemoveOriginGame: Didn't remove Origin game with ID {originGame.Name} from the Origin Library"); return false; + } else throw new OriginLibraryException(); } public static bool RemoveOriginGameById(string originGameId) { - if (originGameId.Equals("0")) + if (originGameId.Equals(0)) return false; - // Remove the originGame from the list. + logger.Debug($"OriginLibrary/RemoveOriginGame2: Removing Origin game with ID {originGameId} from the Origin library"); + + // Remove the OriginGame from the list. int numRemoved = _allOriginGames.RemoveAll(item => item.Id.Equals(originGameId)); if (numRemoved == 1) { + logger.Debug($"OriginLibrary/RemoveOriginGame2: Removed Origin game with ID {originGameId}"); return true; } else if (numRemoved == 0) + { + logger.Debug($"OriginLibrary/RemoveOriginGame2: Didn't remove Origin game with ID {originGameId} from the Origin Library"); return false; + } else throw new OriginLibraryException(); } - public static bool RemoveOriginGame(string originGameNameOrId) { if (String.IsNullOrWhiteSpace(originGameNameOrId)) return false; + logger.Debug($"OriginLibrary/RemoveOriginGame3: Removing Origin game with Name or ID {originGameNameOrId} from the Origin library"); + int numRemoved; - Match match = Regex.Match(originGameNameOrId, originAppIdRegex, RegexOptions.IgnoreCase); + Match match = Regex.Match(originGameNameOrId, OriginAppIdRegex, RegexOptions.IgnoreCase); if (match.Success) numRemoved = _allOriginGames.RemoveAll(item => originGameNameOrId.Equals(item.Id)); else numRemoved = _allOriginGames.RemoveAll(item => originGameNameOrId.Equals(item.Name)); if (numRemoved == 1) + { + logger.Debug($"OriginLibrary/RemoveOriginGame3: Removed Origin game with Name or UUID {originGameNameOrId} "); return true; + } else if (numRemoved == 0) + { + logger.Debug($"OriginLibrary/RemoveOriginGame3: Didn't remove Origin game with Name or UUID {originGameNameOrId} from the Origin Library"); return false; + } else throw new OriginLibraryException(); @@ -238,36 +257,6 @@ namespace DisplayMagician.GameLibraries return false; } - public static bool ContainsOriginGame(string originGameNameOrUuid) - { - if (String.IsNullOrWhiteSpace(originGameNameOrUuid)) - return false; - - - Match match = Regex.Match(originGameNameOrUuid, originAppIdRegex, RegexOptions.IgnoreCase); - if (match.Success) - { - foreach (OriginGame testOriginGame in _allOriginGames) - { - if (originGameNameOrUuid.Equals(Convert.ToInt32(testOriginGame.Id))) - return true; - } - - } - else - { - foreach (OriginGame testOriginGame in _allOriginGames) - { - if (originGameNameOrUuid.Equals(testOriginGame.Name)) - return true; - } - - } - - return false; - - } - public static bool ContainsOriginGameById(string originGameId) { foreach (OriginGame testOriginGame in _allOriginGames) @@ -276,7 +265,37 @@ namespace DisplayMagician.GameLibraries return true; } - + + return false; + + } + + public static bool ContainsOriginGame(string originGameNameOrId) + { + if (String.IsNullOrWhiteSpace(originGameNameOrId)) + return false; + + + Match match = Regex.Match(originGameNameOrId, OriginAppIdRegex, RegexOptions.IgnoreCase); + if (match.Success) + { + foreach (OriginGame testOriginGame in _allOriginGames) + { + if (originGameNameOrId.Equals(Convert.ToInt32(testOriginGame.Id))) + return true; + } + + } + else + { + foreach (OriginGame testOriginGame in _allOriginGames) + { + if (originGameNameOrId.Equals(testOriginGame.Name)) + return true; + } + + } + return false; } @@ -287,7 +306,7 @@ namespace DisplayMagician.GameLibraries if (String.IsNullOrWhiteSpace(originGameNameOrId)) return null; - Match match = Regex.Match(originGameNameOrId, originAppIdRegex, RegexOptions.IgnoreCase); + Match match = Regex.Match(originGameNameOrId, OriginAppIdRegex, RegexOptions.IgnoreCase); if (match.Success) { foreach (OriginGame testOriginGame in _allOriginGames) @@ -330,32 +349,19 @@ namespace DisplayMagician.GameLibraries return parameters; } + public static bool LoadInstalledGames() { try { - // Find the OriginExe location, and the OriginPath for later - /*using (var key = Registry.CurrentUser.OpenSubKey(_registryOriginKey, RegistryKeyPermissionCheck.ReadSubTree)) - { - _originExe = (string)key?.GetValue(@"OriginExe", string.Empty) ?? string.Empty; - _originExe = _originExe.Replace('/', '\\'); - _originPath = (string)key?.GetValue(@"OriginPath", string.Empty) ?? string.Empty; - _originPath = _originPath.Replace('/', '\\'); - }*/ - if (!_isOriginInstalled) { // Origin isn't installed, so we return an empty list. + logger.Info($"OriginLibrary/LoadInstalledGames: Origin library is not installed"); return false; } - //Icon _originIcon = Icon.ExtractAssociatedIcon(_originExe); - //IconExtractor originIconExtractor = new IconExtractor(_originExe); - //Icon _originIcon = originIconExtractor.GetIcon(0); - //MultiIcon _originIcon = new MultiIcon(); - //_originIcon.Load(_originExe); - var localContentPath = Path.Combine(_originLocalContent, "LocalContent"); //var games = new Dictionary(); @@ -366,24 +372,25 @@ namespace DisplayMagician.GameLibraries { try { - string gameId = Path.GetFileNameWithoutExtension(package); - if (!gameId.StartsWith("Origin")) + OriginAppInfo originGame = new OriginAppInfo(); + originGame.GameID = Path.GetFileNameWithoutExtension(package); + if (!originGame.GameID.StartsWith("Origin")) { // If the gameId doesn't start with origin, then we need to find it! // Get game id by fixing file via adding : before integer part of the name // for example OFB-EAST52017 converts to OFB-EAST:52017 - Match match = Regex.Match(gameId, @"^(.*?)(\d+)$"); + Match match = Regex.Match(originGame.GameID, @"^(.*?)(\d+)$"); if (!match.Success) { logger.Warn("Failed to get game id from file " + package); continue; } - gameId = match.Groups[1].Value + ":" + match.Groups[2].Value; + originGame.GameID = match.Groups[1].Value + ":" + match.Groups[2].Value; } // Now we get the rest of the game information out of the manifest file - Dictionary manifestInfo = ParseOriginManifest(package); + Dictionary manifestInfo = ParseOriginManifest(package); if (manifestInfo.ContainsKey("ddinitialdownload") && manifestInfo["ddinitialdownload"] == "1") { @@ -397,341 +404,254 @@ namespace DisplayMagician.GameLibraries continue; } - string gamePath = null; + originGame.GameInstallDir = null; if (manifestInfo.ContainsKey("dipinstallpath")) { - // This is where Origin has installed this game - if (Directory.Exists(manifestInfo["downloading"])) + // This is where Origin has installed this game + originGame.GameInstallDir = HttpUtility.UrlDecode(manifestInfo["dipinstallpath"]); + if (!Directory.Exists(originGame.GameInstallDir)) { - gamePath = manifestInfo["downloading"]; + logger.Debug($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} found but no valid directory found at {originGame.GameInstallDir}"); + continue; } } + + // Now we want to look in the dinstallpath location for the game info + // for the __Installer\installerdata.xml + + string gameInstallerData = Path.Combine(originGame.GameInstallDir, @"__Installer", @"installerdata.xml"); + + if (File.Exists(gameInstallerData)) + { + // Now we parse the XML + XDocument xdoc = XDocument.Load(gameInstallerData); + originGame.GameName = xdoc.XPathSelectElement("/DiPManifest/gameTitles/gameTitle[@locale='en_US']").Value; + string gameFilePath = xdoc.XPathSelectElement("/DiPManifest/runtime/launcher/filePath").Value; + // Check whether gameFilePath contains a registry key! Cause if it does we need to lookup the path there instead + if (gameFilePath.StartsWith("[HKEY_LOCAL_MACHINE")) + { + // The filePath contains a registry key lookup that we need to execute and replace + MatchCollection mc = Regex.Matches(gameFilePath, @"\[HKEY_LOCAL_MACHINE\\(.*)\](.*)"); + string originGameInstallKeyNameAndValue = mc[0].Groups[1].ToString(); + string originGameRestOfFile = mc[0].Groups[2].ToString(); + if (originGameInstallKeyNameAndValue == null) + { + // then we have a problem and we need to continue and ignore this game + logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has registry but we can't match it! gameFilePath is {gameFilePath}."); + continue; + } + + mc = Regex.Matches(originGameInstallKeyNameAndValue, @"(.*)\\([^\\]*)"); + string originGameInstallKeyName = mc[0].Groups[1].ToString(); + string originGameInstallKeyValue = mc[0].Groups[2].ToString(); + + try + { + RegistryKey originGameInstallKey = Registry.LocalMachine.OpenSubKey(originGameInstallKeyName, RegistryKeyPermissionCheck.ReadSubTree); + if (originGameInstallKey == null) + { + // then we have a problem as we cannot find the game exe location! + logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has a install reg key we cannot find! originGameInstallKey is {gameFilePath} and originGameInstallKeyValue is {originGameInstallKeyValue}."); + continue; + } + string originGameInstallLocation = originGameInstallKey.GetValue(originGameInstallKeyValue).ToString(); + originGameInstallLocation = Path.Combine(originGameInstallLocation, originGameRestOfFile); + if (!File.Exists(originGameInstallLocation)) + { + // then we have a problem as we cannot locate the game exe file to start! + logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has gameexe we cannot find! originGameInstallLocation is {originGameInstallLocation}."); + continue; + } + originGame.GameExePath = originGameInstallLocation; + + } + catch (SecurityException ex) + { + logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The user does not have the permissions required to read the Origin Game location registry key {}."); + } + catch (ObjectDisposedException ex) + { + logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Origin ClientPath registry key (closed keys cannot be accessed)."); + } + catch (IOException ex) + { + logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The Origin ClientPath registry key has been marked for deletion so we cannot access the value dueing the OriginLibrary check."); + } + catch (UnauthorizedAccessException ex) + { + logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The user does not have the necessary registry rights to check whether Origin is installed."); + } + } + else if (gameFilePath.StartsWith("[HKEY_CURRENT_USER")) + { + // The filePath contains a registry key lookup that we need to execute and replace + MatchCollection mc = Regex.Matches(gameFilePath, @"\[HKEY_CURRENT_USER\\(.*)\](.*)"); + string originGameInstallKeyNameAndValue = mc[0].Groups[1].ToString(); + string originGameRestOfFile = mc[0].Groups[2].ToString(); + if (originGameInstallKeyNameAndValue == null) + { + // then we have a problem and we need to continue and ignore this game + logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has registry but we can't match it! gameFilePath is {gameFilePath}."); + continue; + } + + mc = Regex.Matches(originGameInstallKeyNameAndValue, @"(.*)\\([^\\]*)"); + string originGameInstallKeyName = mc[0].Groups[1].ToString(); + string originGameInstallKeyValue = mc[0].Groups[2].ToString(); + + try + { + RegistryKey originGameInstallKey = Registry.LocalMachine.OpenSubKey(originGameInstallKeyName, RegistryKeyPermissionCheck.ReadSubTree); + if (originGameInstallKey == null) + { + // then we have a problem as we cannot find the game exe location! + logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has a install reg key we cannot find! originGameInstallKey is {gameFilePath} and originGameInstallKeyValue is {originGameInstallKeyValue}."); + continue; + } + string originGameInstallLocation = originGameInstallKey.GetValue(originGameInstallKeyValue).ToString(); + originGameInstallLocation = Path.Combine(originGameInstallLocation, originGameRestOfFile); + if (!File.Exists(originGameInstallLocation)) + { + // then we have a problem as we cannot locate the game exe file to start! + logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has gameexe we cannot find! originGameInstallLocation is {originGameInstallLocation}."); + continue; + } + originGame.GameExePath = originGameInstallLocation; + + } + catch (SecurityException ex) + { + logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The user does not have the permissions required to read the Origin Game location registry key {}."); + } + catch (ObjectDisposedException ex) + { + logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Origin ClientPath registry key (closed keys cannot be accessed)."); + } + catch (IOException ex) + { + logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The Origin ClientPath registry key has been marked for deletion so we cannot access the value dueing the OriginLibrary check."); + } + catch (UnauthorizedAccessException ex) + { + logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The user does not have the necessary registry rights to check whether Origin is installed."); + } + } + + + if (!File.Exists(originGame.GameExePath)) + { + logger.Debug($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} found but no game exe found at {originGame.GameExePath}"); + continue; + } + + // TODO check for icon! For now we will just use the exe one + originGame.GameOriginIconPath = originGame.GameExePath; + + // If we reach here we add the Game to the list of games we have! + _allOriginGames.Add(new OriginGame(originGame.GameID, originGame.GameName, originGame.GameExePath, originGame.GameOriginIconPath)); + + } + else + { + // If we can't find the __Installer\installerdata.xml file then we ignore this game + continue; + } + + + } + catch (Exception ex) + { + logger.Error(ex, $"OriginLibrary/LoadInstalledGames: Failed to import installed Origin game {package}."); + } + } + } + else + { + logger.Warn($"OriginLibrary/LoadInstalledGames: No Origin games installed in the Origin library"); + return false; + } + + // up to here + + + + /* if (gotGameRegistryKey) + { + // Now we need to lookup the game install path in registry using the game reg we got above + // We assume its 64-bit OS too (not 32bit) + using (RegistryKey OriginGameInstallKey = Registry.LocalMachine.OpenSubKey(gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree)) + { + // If the key doesn't exist we skip it as the game isn't installed any longer! + if (OriginGameInstallKey == null) + { + logger.Trace($"OriginLibrary/LoadInstalledGames: Skipping Origin Game {OriginGameAppInfo.GameName} as it isn't installed at the moment (it was uninstalled at some point)"); + continue; + } + + // If we get here, then we have a real game. + foreach (string regKeyName in OriginGameInstallKey.GetValueNames()) + { + logger.Trace($"OriginLibrary/LoadInstalledGames: OriginGameInstallKey[{regKeyName}] = {OriginGameInstallKey.GetValue(regKeyName)}"); + } + + // From that we lookup the actual game path + string gameInstallDir = OriginGameInstallKey.GetValue("InstallDir", "").ToString(); + logger.Trace($"OriginLibrary/LoadInstalledGames: gameInstallDir found = {gameInstallDir}"); + if (!String.IsNullOrWhiteSpace(gameInstallDir)) + { + OriginGameAppInfo.GameInstallDir = Path.GetFullPath(gameInstallDir).TrimEnd('\\'); + logger.Trace($"OriginLibrary/LoadInstalledGames: OriginGameAppInfo.GameInstallDir = {OriginGameAppInfo.GameInstallDir }"); + OriginGameAppInfo.GameExe = Path.Combine(OriginGameAppInfo.GameInstallDir, gameFileName); + logger.Trace($"OriginLibrary/LoadInstalledGames: OriginGameAppInfo.GameExe = {OriginGameAppInfo.GameExePath }"); + OriginGameAppInfo.GameID = gameId; + logger.Trace($"OriginLibrary/LoadInstalledGames: OriginGameAppInfo.GameID = {OriginGameAppInfo.GameID }"); + } + else + { + logger.Warn($"OriginLibrary/LoadInstalledGames: gameInstallDir is null or all whitespace!"); + } + + // Then we have the gameID, the thumbimage, the icon, the name, the exe path // And we add the Game to the list of games we have! - _allOriginGames.Add(new OriginGame(gameId, gameName, gameExe, gameIconPath)); - - - - var newGame = new GameInfo() - { - Source = "Origin", - GameId = gameId, - IsInstalled = true, - Platform = "PC" - }; - - GameLocalDataResponse localData = null; - - try - { - localData = GetLocalManifest(gameId); - } - catch (Exception e) when (!Environment.IsDebugBuild) - { - logger.Error(e, $"Failed to get Origin manifest for a {gameId}, {package}"); - continue; - } - - if (localData == null) - { - continue; - } - - if (localData.offerType != "Base Game" && localData.offerType != "DEMO") - { - continue; - } - - newGame.Name = StringExtensions.NormalizeGameName(localData.localizableAttributes.displayName); - var installDir = GetInstallDirectory(localData); - if (installDir.IsNullOrEmpty()) - { - continue; - } - - newGame.InstallDirectory = installDir; - newGame.PlayAction = new GameAction - { - IsHandledByPlugin = true, - Type = GameActionType.URL, - Path = Origin.GetLaunchString(gameId) - }; - - games.Add(newGame.GameId, newGame); - } - catch (Exception e) when (!Environment.IsDebugBuild) - { - logger.Error(e, $"Failed to import installed Origin game {package}."); + _allOriginGames.Add(new OriginGame(OriginGameAppInfo.GameID, OriginGameAppInfo.GameName, OriginGameAppInfo.GameExePath, OriginGameAppInfo.GameOriginIconPath)); + logger.Debug($"OriginLibrary/LoadInstalledGames: Adding Origin Game with game id {OriginGameAppInfo.GameID}, name {OriginGameAppInfo.GameName}, game exe {OriginGameAppInfo.GameExePath} and icon path {OriginGameAppInfo.GameOriginIconPath}"); } } + + }*/ + logger.Info($"OriginLibrary/LoadInstalledGames: Found {_allOriginGames.Count} installed Origin games"); + } - - - - - - - - - List originAppIdsInstalled = new List(); - // Now look for what games app id's are actually installed on this computer - using (RegistryKey originAppsKey = Registry.CurrentUser.OpenSubKey(_registryAppsKey, RegistryKeyPermissionCheck.ReadSubTree)) - { - if (originAppsKey != null) - { - // Loop through the subKeys as they are the Origin Game IDs - foreach (string originGameKeyName in originAppsKey.GetSubKeyNames()) - { - if (int.TryParse(originGameKeyName, out int originAppId)) - { - string originGameKeyFullName = $"{_registryAppsKey}\\{originGameKeyName}"; - using (RegistryKey originGameKey = Registry.CurrentUser.OpenSubKey(originGameKeyFullName, 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)originGameKey.GetValue(@"Installed", 0) == 1) - { - // Add this Origin App ID to the list we're keeping for later - originAppIdsInstalled.Add(originAppId); - } - - } - - } - } - } - } - - // Now we parse the origin 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 originAppInfo = new Dictionary(); - - string appInfoVdfFile = Path.Combine(_originPath, "appcache", "appinfo.vdf"); - var newAppInfo = new AppInfo(); - newAppInfo.Read(appInfoVdfFile); - - Debug.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 Origin specific stuff too) - int detectedAppID = Convert.ToInt32(app.AppID); - if (originAppIdsInstalled.Contains(detectedAppID)) - { - - try - { - - OriginAppInfo originGameAppInfo = new OriginAppInfo - { - GameID = detectedAppID, - GameExes = new List() - }; - - foreach (KVObject data in app.Data) - { - //Debug.WriteLine($"App: {app.AppID} - Data.Name: {data.Name}"); - - if (data.Name == "common") - { - foreach (KVObject common in data.Children) - { - - //Debug.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}"); - - if (common.Name == "name") - { - Debug.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}"); - originGameAppInfo.GameName = common.Value.ToString(); - } - else if (common.Name == "clienticon") - { - Debug.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}"); - originGameAppInfo.GameOriginIconPath = Path.Combine(_originPath, @"origin", @"games", String.Concat(common.Value, @".ico")); - } - else if (common.Name == "type") - { - Debug.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") - { - Debug.WriteLine($"App: {detectedAppID} - Config {config.Name}: {config.Value}"); - originGameAppInfo.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") - { - Debug.WriteLine($"App: {detectedAppID} - Config - Launch {launch.Name} - {launch_num.Name}: {launch_num.Value}"); - originGameAppInfo.GameExes.Add(launch_num.Value.ToString()); - } - - } - } - } - } - } - - } - originAppInfo.Add(detectedAppID, originGameAppInfo); - } - catch (ArgumentException ex) - { - Console.WriteLine($"OriginGame/GetAllInstalledGames exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); - //we just want to ignore it if we try to add it twice.... - } - - Debug.WriteLine($"App: {detectedAppID} - Token: {app.Token}"); - } - } - - - - // Now we access the config.vdf that lives in the Origin Config file, as that lists all - // the OriginLibraries. We need to find out where they areso we can interrogate them - _originConfigVdfFile = Path.Combine(_originPath, "config", "config.vdf"); - string originConfigVdfText = File.ReadAllText(_originConfigVdfFile, Encoding.UTF8); - - List originLibrariesPaths = new List(); - // Now we have to parse the config.vdf looking for the location of the OriginLibraries - // We look for lines similar to this: "BaseInstallFolder_1" "E:\\OriginLibrary" - // There may be multiple so we need to check the whole file - Regex originLibrariesRegex = new Regex(@"""BaseInstallFolder_\d+""\s+""(.*)""", RegexOptions.IgnoreCase); - // Try to match all lines against the Regex. - MatchCollection originLibrariesMatches = originLibrariesRegex.Matches(originConfigVdfText); - // If at least one of them matched! - foreach (Match originLibraryMatch in originLibrariesMatches) - { - if (originLibraryMatch.Success) - { - string originLibraryPath = Regex.Unescape(originLibraryMatch.Groups[1].Value); - Debug.WriteLine($"Found origin library: {originLibraryPath}"); - originLibrariesPaths.Add(originLibraryPath); - } - } - // Now we go off and find the details for the games in each Origin Library - foreach (string originLibraryPath in originLibrariesPaths) - { - // Work out the path to the appmanifests for this originLibrary - string originLibraryAppManifestPath = Path.Combine(originLibraryPath, @"originapps"); - // Get the names of the App Manifests for the games installed in this OriginLibrary - string[] originLibraryAppManifestFilenames = Directory.GetFiles(originLibraryAppManifestPath, "appmanifest_*.acf"); - // Go through each app and extract it's details - foreach (string originLibraryAppManifestFilename in originLibraryAppManifestFilenames) - { - // Read in the contents of the file - string originLibraryAppManifestText = File.ReadAllText(originLibraryAppManifestFilename); - // Grab the appid from the file - Regex appidRegex = new Regex(@"""appid""\s+""(\d+)""", RegexOptions.IgnoreCase); - Match appidMatches = appidRegex.Match(originLibraryAppManifestText); - if (appidMatches.Success) - { - - if (int.TryParse(appidMatches.Groups[1].Value, out int originGameId)) - { - // Check if this game is one that was installed - if (originAppInfo.ContainsKey(originGameId)) - { - // This game is an installed game! so we start to populate it with data! - string originGameExe = ""; - - string originGameName = originAppInfo[originGameId].GameName; - - // Construct the full path to the game dir from the appInfo and libraryAppManifest data - string originGameInstallDir = Path.Combine(originLibraryPath, @"originapps", @"common", originAppInfo[originGameId].GameInstallDir); - - // And finally we try to populate the 'where', to see what gets run - // And so we can extract the process name - if (originAppInfo[originGameId].GameExes.Count > 0) - { - foreach (string gameExe in originAppInfo[originGameId].GameExes) - { - originGameExe = Path.Combine(originGameInstallDir, gameExe); - // If the game executable exists, then we can proceed - if (File.Exists(originGameExe)) - { - break; - } - } - - } - - // Next, we need to get the Icons we want to use, and make sure it's the latest one. - string originGameIconPath = ""; - // First of all, we attempt to use the Icon that Origin has cached, if it's available, as that will be updated to the latest - if (File.Exists(originAppInfo[originGameId].GameOriginIconPath) && originAppInfo[originGameId].GameOriginIconPath.EndsWith(".ico")) - { - originGameIconPath = originAppInfo[originGameId].GameOriginIconPath; - } - // If there isn't an icon for us to use, then we need to extract one from the Game Executables - else if (!String.IsNullOrEmpty(originGameExe)) - { - originGameIconPath = originGameExe; - } - // The absolute worst case means we don't have an icon to use. SO we use the Origin one. - else - { - // And we have to make do with a Origin Icon - originGameIconPath = _originPath; - } - - // And we add the Game to the list of games we have! - _allOriginGames.Add(new OriginGame(originGameId, originGameName, originGameExe, originGameIconPath)); - - } - } - } - } - } + catch (ArgumentNullException ex) + { + logger.Warn(ex, "OriginLibrary/GetAllInstalledGames: An argument supplied to the function is null."); + } + catch (NotSupportedException ex) + { + logger.Warn(ex, "OriginLibrary/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, "OriginLibrary/GetAllInstalledGames: The path is longer than the maximum allowed by the operating system."); } catch (SecurityException ex) { - Console.WriteLine($"OriginGame/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($"OriginGame/GetAllInstalledGames unauthorizedaccessexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); - if (ex.Source != null) - Console.WriteLine("UnauthorizedAccessException source: {0} - Message: {1}", ex.Source, ex.Message); - throw; + logger.Warn(ex, "OriginLibrary/GetAllInstalledGames: The user does not have the permissions required to read the Origin InstallDir registry key."); } catch (ObjectDisposedException ex) { - Console.WriteLine($"OriginGame/GetAllInstalledGames objectdisposedexceptions: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); - if (ex.Source != null) - Console.WriteLine("ObjectDisposedException source: {0} - Message: {1}", ex.Source, ex.Message); - throw; + logger.Warn(ex, "OriginLibrary/GetAllInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Origin InstallDir registry key (closed keys cannot be accessed)."); } catch (IOException ex) { - Console.WriteLine($"OriginGame/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; + logger.Warn(ex, "OriginLibrary/GetAllInstalledGames: The Origin InstallDir registry key has been marked for deletion so we cannot access the value dueing the OriginLibrary check."); + } + catch (UnauthorizedAccessException ex) + { + logger.Warn(ex, "OriginLibrary/GetAllInstalledGames: The user does not have the necessary registry rights to check whether Origin is installed."); } return true; diff --git a/DisplayMagician/Program.cs b/DisplayMagician/Program.cs index 08036b2..f1b3b2a 100644 --- a/DisplayMagician/Program.cs +++ b/DisplayMagician/Program.cs @@ -758,10 +758,36 @@ namespace DisplayMagician { }); + // Now lets prepare loading all the Origin games we have installed + Task loadOriginGamesTask = new Task(() => + { + // Check if Origin is installed + if (GameLibraries.OriginLibrary.IsOriginInstalled) + { + // Load Origin library games + logger.Info($"Program/LoadGamesInBackground: Loading Installed Origin Games"); + Console.Write("Loading Installed Origin Games..."); + if (!DisplayMagician.GameLibraries.OriginLibrary.LoadInstalledGames()) + { + logger.Info($"Program/LoadGamesInBackground: Cannot load installed Origin Games!"); + } + Console.WriteLine("Done."); + logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Origin Games (found {GameLibraries.OriginLibrary.InstalledOriginGameCount})"); + } + else + { + logger.Info($"Program/LoadGamesInBackground: Origin not installed."); + Console.WriteLine("Origin not installed."); + } + + }); + + // Store all the tasks in an array so we can wait on them later - Task[] loadGamesTasks = new Task[2]; + Task[] loadGamesTasks = new Task[3]; loadGamesTasks[0] = loadSteamGamesTask; loadGamesTasks[1] = loadUplayGamesTask; + loadGamesTasks[2] = loadOriginGamesTask; logger.Debug($"Program/LoadGamesInBackground: Running game loading tasks."); // Go through and start all the tasks