[WIP] Origin Games are detected

The Task finds the origin games and
returns them. Just need to add the logic
to actually use the returned data to
populate the game library, as well as
use the game library in the shortcuts
as well as actually run the new library too!
This commit is contained in:
Terry MacDonald
2021-04-16 22:40:58 +12:00
parent 21288b4224
commit 44b620851a
4 changed files with 396 additions and 445 deletions

View File

@ -84,6 +84,7 @@
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Web" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq" />
@ -100,9 +101,9 @@
<Compile Include="GameLibraries\SteamAppInfoParser\Package.cs" />
<Compile Include="GameLibraries\SteamAppInfoParser\PackageInfo.cs" />
<Compile Include="GameLibraries\SteamAppInfoParser\App.cs" />
<Compile Include="GameLibraries\UplayConfigurationParser\UplayConfigurationParser.cs" />
<Compile Include="GameLibraries\OriginGame.cs" />
<Compile Include="GameLibraries\OriginLibrary.cs" />
<Compile Include="GameLibraries\UplayConfigurationParser\UplayConfigurationParser.cs" />
<Compile Include="GameLibraries\UplayLibrary.cs" />
<Compile Include="GameLibraries\SteamLibrary.cs" />
<Compile Include="GlobalSuppressions.cs" />

View File

@ -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;
}

View File

@ -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<Game> _allOriginGames = new List<Game>();
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<string> 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<string, GameInfo>();
@ -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<string,string> manifestInfo = ParseOriginManifest(package);
Dictionary<string, string> 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<int> originAppIdsInstalled = new List<int>();
// 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<int, OriginAppInfo> originAppInfo = new Dictionary<int, OriginAppInfo>();
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<string>()
};
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<string> originLibrariesPaths = new List<string>();
// 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;

View File

@ -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