diff --git a/DisplayMagician/DisplayMagician.csproj b/DisplayMagician/DisplayMagician.csproj
index d8f73a6..e108dd8 100644
--- a/DisplayMagician/DisplayMagician.csproj
+++ b/DisplayMagician/DisplayMagician.csproj
@@ -99,6 +99,8 @@
+
+
diff --git a/DisplayMagician/GameLibraries/EpicGame.cs b/DisplayMagician/GameLibraries/EpicGame.cs
new file mode 100644
index 0000000..b64a103
--- /dev/null
+++ b/DisplayMagician/GameLibraries/EpicGame.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using DisplayMagician.Resources;
+using System.Diagnostics;
+
+namespace DisplayMagician.GameLibraries
+{
+ public class EpicGame : Game
+ {
+ private string _epicGameId;
+ private string _epicGameName;
+ private string _epicGameExePath;
+ private string _epicGameDir;
+ private string _epicGameExe;
+ private string _epicGameProcessName;
+ private string _epicGameIconPath;
+ //private string _epicURI;
+ private static readonly EpicLibrary _epicGameLibrary = EpicLibrary.GetLibrary();
+ private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+ static EpicGame()
+ {
+ ServicePointManager.ServerCertificateValidationCallback +=
+ (send, certificate, chain, sslPolicyErrors) => true;
+ }
+
+
+ public EpicGame(string epicGameId, string epicGameName, string epicGameExePath, string epicGameIconPath)
+ {
+
+ //_gameRegistryKey = $@"{EpicLibrary.registryEpicInstallsKey}\\{EpicGameId}";
+ _epicGameId = epicGameId;
+ _epicGameName = epicGameName;
+ _epicGameExePath = epicGameExePath;
+ _epicGameDir = Path.GetDirectoryName(epicGameExePath);
+ _epicGameExe = Path.GetFileName(_epicGameExePath);
+ _epicGameProcessName = Path.GetFileNameWithoutExtension(_epicGameExePath);
+ _epicGameIconPath = epicGameIconPath;
+
+ }
+
+ public override string Id
+ {
+ get => _epicGameId;
+ set => _epicGameId = value;
+ }
+
+ public override string Name
+ {
+ get => _epicGameName;
+ set => _epicGameName = value;
+ }
+
+ public override SupportedGameLibraryType GameLibrary
+ {
+ get => SupportedGameLibraryType.Epic;
+ }
+
+ public override string IconPath
+ {
+ get => _epicGameIconPath;
+ set => _epicGameIconPath = value;
+ }
+
+ public override string ExePath
+ {
+ get => _epicGameExePath;
+ set => _epicGameExePath = value;
+ }
+
+ public override string Directory
+ {
+ get => _epicGameDir;
+ set => _epicGameDir = value;
+ }
+
+ public override bool IsRunning
+ {
+ get
+ {
+ int numGameProcesses = 0;
+ List gameProcesses = Process.GetProcessesByName(_epicGameProcessName).ToList();
+ foreach (Process gameProcess in gameProcesses)
+ {
+ try
+ {
+ if (gameProcess.ProcessName.Equals(_epicGameProcessName))
+ numGameProcesses++;
+ }
+ catch (Exception ex)
+ {
+ logger.Debug(ex, $"EpicGame/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 assume that the process is a game process
+ // as it matched the epical process search
+ numGameProcesses++;
+ continue;
+ }
+ else
+ {
+ if (filePath.StartsWith(_epicGameExePath))
+ numGameProcesses++;
+ }
+
+ }
+ }
+ if (numGameProcesses > 0)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ // Have to do much more research to figure out how to detect when Epic is updating a game
+ public override bool IsUpdating
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public override GameStartMode StartMode
+ {
+ get => GameStartMode.URI;
+ }
+
+ public override string GetStartURI(string gameArguments = "")
+ {
+ string address = $"epic2://game/launch?offerIds={_epicGameId}";
+ if (String.IsNullOrWhiteSpace(gameArguments))
+ {
+ address += "/" + gameArguments;
+ }
+ return address;
+ }
+
+ public bool CopyTo(EpicGame EpicGame)
+ {
+ if (!(EpicGame is EpicGame))
+ return false;
+
+ // Copy all the game data over to the other game
+ EpicGame.IconPath = IconPath;
+ EpicGame.Id = Id;
+ EpicGame.Name = Name;
+ EpicGame.ExePath = ExePath;
+ EpicGame.Directory = Directory;
+ return true;
+ }
+
+ public override string ToString()
+ {
+ var name = _epicGameName;
+
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ name = Language.Unknown;
+ }
+
+ if (IsRunning)
+ {
+ return name + " " + Language.Running;
+ }
+
+ /*if (IsUpdating)
+ {
+ return name + " " + Language.Updating;
+ }*/
+
+ return name;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/DisplayMagician/GameLibraries/EpicLibrary.cs b/DisplayMagician/GameLibraries/EpicLibrary.cs
new file mode 100644
index 0000000..9de256d
--- /dev/null
+++ b/DisplayMagician/GameLibraries/EpicLibrary.cs
@@ -0,0 +1,753 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using Microsoft.Win32;
+using System.IO;
+using System.Security;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using System.Web;
+using System.Diagnostics;
+
+namespace DisplayMagician.GameLibraries
+{
+ public sealed class EpicLibrary : GameLibrary
+ {
+ #region Class Variables
+ // Static members are 'eagerly initialized', that is,
+ // immediately when class is loaded for the first time.
+ // .NET guarantees thread safety for static initialization
+ private static readonly EpicLibrary _instance = new EpicLibrary();
+
+
+ // Common items to the class
+ private List _allEpicGames = new List();
+ private string EpicAppIdRegex = @"/^[0-9A-F]{1,10}$";
+ private string _epicExe;
+ private string _epicPath;
+ private string _epicLocalContent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Epic");
+ private bool _isEpicInstalled = false;
+ private List _epicProcessList = new List(){ "epic" };
+
+ //private string _epicConfigVdfFile;
+ internal string registryEpicLauncherKey = @"SOFTWARE\WOW6432Node\Epic";
+ //internal string registryEpicInstallsKey = @"SOFTWARE\WOW6432Node\Ubisoft\Launcher\Installs";
+ //internal string registryEpicOpenCmdKey = @"SOFTWARE\Classes\Epic\Shell\Open\Command";
+ private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+
+ // Other constants that are useful
+ #endregion
+
+ #region Class Constructors
+ static EpicLibrary() { }
+
+ private EpicLibrary()
+ {
+ try
+ {
+ logger.Trace($"EpicLibrary/EpicLibrary: Epic launcher registry key = HKLM\\{registryEpicLauncherKey}");
+ // Find the EpicExe location, and the EpicPath for later
+ RegistryKey EpicInstallKey = Registry.LocalMachine.OpenSubKey(registryEpicLauncherKey, RegistryKeyPermissionCheck.ReadSubTree);
+ if (EpicInstallKey == null)
+ return;
+ _epicExe = EpicInstallKey.GetValue("ClientPath", @"C:\Program Files (x86)\Epic\Epic.exe").ToString();
+ _epicPath = _epicExe;
+ _epicPath = _epicPath.Replace(@"\Epic.exe", "");
+ if (File.Exists(_epicExe))
+ {
+ logger.Info($"EpicLibrary/EpicLibrary: Epic library is installed in {_epicPath}. Found {_epicExe}");
+ _isEpicInstalled = true;
+ }
+ else
+ {
+ logger.Info($"EpicLibrary/EpicLibrary: Epic library is not installed!");
+ }
+
+ }
+ catch (SecurityException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/EpicLibrary: The user does not have the permissions required to read the Epic ClientPath registry key.");
+ }
+ catch(ObjectDisposedException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/EpicLibrary: The Microsoft.Win32.RegistryKey is closed when trying to access the Epic ClientPath registry key (closed keys cannot be accessed).");
+ }
+ catch (IOException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/EpicLibrary: The Epic ClientPath registry key has been marked for deletion so we cannot access the value dueing the EpicLibrary check.");
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/EpicLibrary: The user does not have the necessary registry rights to check whether Epic is installed.");
+ }
+ }
+ #endregion
+
+ #region Class Properties
+ public override List AllInstalledGames
+ {
+ get
+ {
+ // Load the Epic Games from Epic Client if needed
+ if (_allEpicGames.Count == 0)
+ LoadInstalledGames();
+ return _allEpicGames;
+ }
+ }
+
+
+ public override int InstalledGameCount
+ {
+ get
+ {
+ return _allEpicGames.Count;
+ }
+ }
+
+ public override string GameLibraryName
+ {
+ get
+ {
+ return "Epic";
+ }
+ }
+
+ public override SupportedGameLibraryType GameLibraryType
+ {
+ get
+ {
+ return SupportedGameLibraryType.Epic;
+ }
+ }
+
+ public override string GameLibraryExe
+ {
+ get
+ {
+ return _epicExe;
+ }
+ }
+
+ public override string GameLibraryPath
+ {
+ get
+ {
+ return _epicPath;
+ }
+ }
+
+ public override bool IsGameLibraryInstalled
+ {
+ get
+ {
+ return _isEpicInstalled;
+ }
+
+ }
+
+ public override bool IsRunning
+ {
+ get
+ {
+ List epicLibraryProcesses = new List();
+
+ foreach (string epicLibraryProcessName in _epicProcessList)
+ {
+ // Look for the processes with the ProcessName we sorted out earlier
+ epicLibraryProcesses.AddRange(Process.GetProcessesByName(epicLibraryProcessName));
+ }
+
+ // If we have found one or more processes then we should be good to go
+ // so let's break, and get to the next step....
+ if (epicLibraryProcesses.Count > 0)
+ return true;
+ else
+ return false;
+ }
+
+ }
+
+ public override bool IsUpdating
+ {
+ get
+ {
+ // Not implemeted at present
+ // so we just return a false
+ // TODO Implement Epic specific detection for updating the game client
+ return false;
+ }
+
+ }
+
+ public override List GameLibraryProcesses
+ {
+ get
+ {
+ return _epicProcessList;
+ }
+ }
+
+
+ #endregion
+
+ #region Class Methods
+ public static EpicLibrary GetLibrary()
+ {
+ return _instance;
+ }
+
+
+ public override bool AddGame(Game epicGame)
+ {
+ if (!(epicGame is EpicGame))
+ return false;
+
+ // Doublecheck if it already exists
+ // Because then we just update the one that already exists
+ if (ContainsGame(epicGame))
+ {
+ logger.Debug($"EpicLibrary/AddEpicGame: Updating Epic game {epicGame.Name} in our Epic library");
+ // We update the existing Shortcut with the data over
+ EpicGame epicGameToUpdate = (EpicGame)GetGame(epicGame.Id.ToString());
+ epicGame.CopyTo(epicGameToUpdate);
+ }
+ else
+ {
+ logger.Debug($"EpicLibrary/AddEpicGame: Adding Epic game {epicGame.Name} to our Epic library");
+ // Add the EpicGame to the list of EpicGames
+ _allEpicGames.Add(epicGame);
+ }
+
+ //Doublecheck it's been added
+ if (ContainsGame(epicGame))
+ {
+ return true;
+ }
+ else
+ return false;
+
+ }
+
+ public override bool RemoveGame(Game epicGame)
+ {
+ if (!(epicGame is EpicGame))
+ return false;
+
+ logger.Debug($"EpicLibrary/RemoveEpicGame: Removing Epic game {epicGame.Name} from our Epic library");
+
+ // Remove the EpicGame from the list.
+ int numRemoved = _allEpicGames.RemoveAll(item => item.Id.Equals(epicGame.Id));
+
+ if (numRemoved == 1)
+ {
+ logger.Debug($"EpicLibrary/RemoveEpicGame: Removed Epic game with name {epicGame.Name}");
+ return true;
+ }
+ else if (numRemoved == 0)
+ {
+ logger.Debug($"EpicLibrary/RemoveEpicGame: Didn't remove Epic game with ID {epicGame.Name} from the Epic Library");
+ return false;
+ }
+ else
+ throw new EpicLibraryException();
+ }
+
+ public override bool RemoveGameById(string epicGameId)
+ {
+ if (epicGameId.Equals(0))
+ return false;
+
+ logger.Debug($"EpicLibrary/RemoveEpicGame2: Removing Epic game with ID {epicGameId} from the Epic library");
+
+ // Remove the EpicGame from the list.
+ int numRemoved = _allEpicGames.RemoveAll(item => item.Id.Equals(epicGameId));
+
+ if (numRemoved == 1)
+ {
+ logger.Debug($"EpicLibrary/RemoveEpicGame2: Removed Epic game with ID {epicGameId}");
+ return true;
+ }
+ else if (numRemoved == 0)
+ {
+ logger.Debug($"EpicLibrary/RemoveEpicGame2: Didn't remove Epic game with ID {epicGameId} from the Epic Library");
+ return false;
+ }
+ else
+ throw new EpicLibraryException();
+ }
+
+ public override bool RemoveGame(string epicGameNameOrId)
+ {
+ if (String.IsNullOrWhiteSpace(epicGameNameOrId))
+ return false;
+
+ logger.Debug($"EpicLibrary/RemoveEpicGame3: Removing Epic game with Name or ID {epicGameNameOrId} from the Epic library");
+
+ int numRemoved;
+ Match match = Regex.Match(epicGameNameOrId, EpicAppIdRegex, RegexOptions.IgnoreCase);
+ if (match.Success)
+ numRemoved = _allEpicGames.RemoveAll(item => epicGameNameOrId.Equals(item.Id));
+ else
+ numRemoved = _allEpicGames.RemoveAll(item => epicGameNameOrId.Equals(item.Name));
+
+ if (numRemoved == 1)
+ {
+ logger.Debug($"EpicLibrary/RemoveEpicGame3: Removed Epic game with Name or UUID {epicGameNameOrId} ");
+ return true;
+ }
+ else if (numRemoved == 0)
+ {
+ logger.Debug($"EpicLibrary/RemoveEpicGame3: Didn't remove Epic game with Name or UUID {epicGameNameOrId} from the Epic Library");
+ return false;
+ }
+ else
+ throw new EpicLibraryException();
+
+ }
+
+ public override bool ContainsGame(Game epicGame)
+ {
+ if (!(epicGame is EpicGame))
+ return false;
+
+ foreach (EpicGame testEpicGame in _allEpicGames)
+ {
+ if (testEpicGame.Id.Equals(epicGame.Id))
+ return true;
+ }
+
+ return false;
+ }
+
+ public override bool ContainsGameById(string epicGameId)
+ {
+ foreach (EpicGame testEpicGame in _allEpicGames)
+ {
+ if (epicGameId == testEpicGame.Id)
+ return true;
+ }
+
+
+ return false;
+
+ }
+
+ public override bool ContainsGame(string epicGameNameOrId)
+ {
+ if (String.IsNullOrWhiteSpace(epicGameNameOrId))
+ return false;
+
+
+ Match match = Regex.Match(epicGameNameOrId, EpicAppIdRegex, RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ foreach (EpicGame testEpicGame in _allEpicGames)
+ {
+ if (epicGameNameOrId.Equals(Convert.ToInt32(testEpicGame.Id)))
+ return true;
+ }
+
+ }
+ else
+ {
+ foreach (EpicGame testEpicGame in _allEpicGames)
+ {
+ if (epicGameNameOrId.Equals(testEpicGame.Name))
+ return true;
+ }
+
+ }
+
+ return false;
+
+ }
+
+
+ public override Game GetGame(string epicGameNameOrId)
+ {
+ if (String.IsNullOrWhiteSpace(epicGameNameOrId))
+ return null;
+
+ Match match = Regex.Match(epicGameNameOrId, EpicAppIdRegex, RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ foreach (EpicGame testEpicGame in _allEpicGames)
+ {
+ if (epicGameNameOrId.Equals(Convert.ToInt32(testEpicGame.Id)))
+ return testEpicGame;
+ }
+
+ }
+ else
+ {
+ foreach (EpicGame testEpicGame in _allEpicGames)
+ {
+ if (epicGameNameOrId.Equals(testEpicGame.Name))
+ return testEpicGame;
+ }
+
+ }
+
+ return null;
+
+ }
+
+ public override Game GetGameById(string epicGameId)
+ {
+ foreach (EpicGame testEpicGame in _allEpicGames)
+ {
+ if (epicGameId == testEpicGame.Id)
+ return testEpicGame;
+ }
+
+ return null;
+
+ }
+
+ private Dictionary ParseEpicManifest(string path)
+ {
+ string encodedContents = File.ReadAllText(path);
+ Dictionary parameters = Regex.Matches(encodedContents, "([^?=&]+)(=([^&]*))?").Cast().ToDictionary(x => x.Groups[1].Value, x => x.Groups[3].Value);
+ return parameters;
+ }
+
+
+ public override bool LoadInstalledGames()
+ {
+ try
+ {
+
+ if (!_isEpicInstalled)
+ {
+ // Epic isn't installed, so we return an empty list.
+ logger.Info($"EpicLibrary/LoadInstalledGames: Epic library is not installed");
+ return false;
+ }
+
+ var localContentPath = Path.Combine(_epicLocalContent, "LocalContent");
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Looking for Local Content in {localContentPath}");
+
+ if (Directory.Exists(localContentPath))
+ {
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Local Content Directory {localContentPath} exists!");
+ string[] packages = Directory.GetFiles(localContentPath, "*.mfst", SearchOption.AllDirectories);
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Found .mfst files in Local Content Directory {localContentPath}: {packages.ToString()}");
+ foreach (string package in packages)
+ {
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Parsing {package} name to find GameID");
+ try
+ {
+ GameAppInfo epicGame = new GameAppInfo();
+ epicGame.GameID = Path.GetFileNameWithoutExtension(package);
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Got GameID of {epicGame.GameID } from file {package}");
+ if (!epicGame.GameID.StartsWith("Epic"))
+ {
+ // If the gameId doesn't start with epic, 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(epicGame.GameID, @"^(.*?)(\d+)$");
+ if (!match.Success)
+ {
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Failed to match game id from file {package} name so ignoring game");
+ continue;
+ }
+
+ epicGame.GameID = match.Groups[1].Value + ":" + match.Groups[2].Value;
+ logger.Trace($"EpicLibrary/LoadInstalledGames: GameID doesn't start with 'Epic' so using different pattern to find {epicGame.GameID} GameID");
+ }
+
+ // Now we get the rest of the game information out of the manifest file
+ Dictionary manifestInfo = ParseEpicManifest(package);
+
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Looking whether Epic is still downloading the game to install it");
+ if (manifestInfo.ContainsKey("ddinitialdownload") && manifestInfo["ddinitialdownload"] == "1")
+ {
+ // Epic is downloading and installing the game so we skip it
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic is still downloading the game with Game ID {epicGame.GameID} to install it");
+ continue;
+ }
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Looking whether Epic is downloading game updates");
+ if (manifestInfo.ContainsKey("downloading") && manifestInfo["downloading"] == "1")
+ {
+ // Epic is downloading some new content so we can't play it at the moment
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic is downloading game updates for the game with Game ID {epicGame.GameID}");
+ continue;
+ }
+
+ epicGame.GameInstallDir = null;
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Looking where the game with Game ID {epicGame.GameID} is installed");
+ if (manifestInfo.ContainsKey("dipinstallpath"))
+ {
+ // This is where Epic has installed this game
+ epicGame.GameInstallDir = HttpUtility.UrlDecode(manifestInfo["dipinstallpath"]);
+ if (String.IsNullOrEmpty(epicGame.GameInstallDir) || !Directory.Exists(epicGame.GameInstallDir))
+ {
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic game with ID {epicGame.GameID} found but no valid directory found at {epicGame.GameInstallDir}");
+ continue;
+ }
+ }
+ else
+ {
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Couldn't figure out where Game ID {epicGame.GameID} is installed. Skipping game.");
+ }
+
+ string gameInstallerData = Path.Combine(epicGame.GameInstallDir, @"__Installer", @"installerdata.xml");
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Parsing the Game Installer Data at {gameInstallerData}");
+
+ if (File.Exists(gameInstallerData))
+ {
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Game Installer Data file was found at {gameInstallerData}");
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Attempting to parse XML Game Installer Data file at {gameInstallerData}");
+ // Now we parse the XML
+ XDocument xdoc = XDocument.Load(gameInstallerData);
+ epicGame.GameName = xdoc.XPathSelectElement("/DiPManifest/gameTitles/gameTitle[@locale='en_US']").Value;
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Game Name {epicGame.GameName} found in Game Installer Data file {gameInstallerData}");
+ string gameFilePath = xdoc.XPathSelectElement("/DiPManifest/runtime/launcher/filePath").Value;
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Game File Path is {gameFilePath } found in Game Installer Data file {gameInstallerData}");
+
+ string epicGameInstallLocation = "";
+ // 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"))
+ {
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Game File Path starts with a registery key so needs to be translated");
+ // The filePath contains a registry key lookup that we need to execute and replace
+ string epicGameInstallKeyNameAndValue = "";
+ string epicGameRestOfFile = "";
+ MatchCollection mc = Regex.Matches(gameFilePath, @"\[HKEY_LOCAL_MACHINE\\(.*)\](.*)");
+ if (mc.Count > 0)
+ {
+ // Split the Reg key bit from the File Path bit
+
+ epicGameInstallKeyNameAndValue = mc[0].Groups[1].ToString();
+ logger.Trace($"EpicLibrary/LoadInstalledGames: epicGameInstallKeyNameAndValue = {epicGameInstallKeyNameAndValue}");
+ epicGameRestOfFile = mc[0].Groups[2].ToString();
+ logger.Trace($"EpicLibrary/LoadInstalledGames: epicGameRestOfFile = {epicGameRestOfFile}");
+ if (epicGameInstallKeyNameAndValue == null || epicGameInstallKeyNameAndValue == "")
+ {
+ // then we have a problem and we need to continue and ignore this game
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic game with ID {epicGame.GameID} has registry key but we can't extract it! gameFilePath is {gameFilePath}.");
+ continue;
+ }
+
+ // Split the reg key from the value name
+
+ string epicGameInstallKeyName = "";
+ string epicGameInstallKeyValue = "";
+ mc = Regex.Matches(epicGameInstallKeyNameAndValue, @"(.*)\\([^\\]*)");
+ if (mc.Count > 0)
+ {
+ epicGameInstallKeyName = mc[0].Groups[1].ToString();
+ logger.Trace($"EpicLibrary/LoadInstalledGames: epicGameInstallKeyName = {epicGameInstallKeyName }");
+ epicGameInstallKeyValue = mc[0].Groups[2].ToString();
+ logger.Trace($"EpicLibrary/LoadInstalledGames: epicGameInstallKeyValue = {epicGameInstallKeyValue }");
+ }
+
+ // Lookup the reg key to figure out where the game is installed
+ try
+ {
+ RegistryKey epicGameInstallKey = Registry.LocalMachine.OpenSubKey(epicGameInstallKeyName, RegistryKeyPermissionCheck.ReadSubTree);
+ if (epicGameInstallKey == null)
+ {
+ // then we have a problem as we cannot find the game exe location!
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic game with ID {epicGame.GameID} has a install reg key we cannot find! epicGameInstallKey is {gameFilePath} and epicGameInstallKeyValue is {epicGameInstallKeyValue}.");
+ continue;
+ }
+ epicGameInstallLocation = Path.Combine(epicGameInstallKey.GetValue(epicGameInstallKeyValue).ToString(), epicGameRestOfFile);
+ if (!File.Exists(epicGameInstallLocation))
+ {
+ // then we have a problem as we cannot locate the game exe file to start!
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic game with ID {epicGame.GameID} has gameexe we cannot find! epicGameInstallLocation is {epicGameInstallLocation}.");
+ continue;
+ }
+ epicGame.GameExePath = epicGameInstallLocation;
+ }
+ catch (SecurityException ex)
+ {
+ logger.Warn(ex, $"EpicLibrary/LoadInstalledGames: The user does not have the permissions required to read the Epic Game location registry key {epicGameInstallKeyName}, so skipping game");
+ continue;
+ }
+ catch (ObjectDisposedException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/LoadInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Epic ClientPath registry key (closed keys cannot be accessed), so skipping game");
+ continue;
+ }
+ catch (IOException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/LoadInstalledGames: The Epic ClientPath registry key has been marked for deletion so we cannot access the value dueing the EpicLibrary check, so skipping game");
+ continue;
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/LoadInstalledGames: The user does not have the necessary registry rights to check whether Epic is installed, so skipping game");
+ continue;
+ }
+ }
+ else
+ {
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Game File Path {gameFilePath} starts with '[HEKY_LOCAL_MACHINE' but didn't match the regex when it should have");
+ continue;
+ }
+
+ }
+ 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\\(.*)\](.*)");
+ if (mc.Count > 0)
+ {
+ string epicGameInstallKeyNameAndValue = mc[0].Groups[1].ToString();
+ string epicGameRestOfFile = mc[0].Groups[2].ToString();
+ if (epicGameInstallKeyNameAndValue == null)
+ {
+ // then we have a problem and we need to continue and ignore this game
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic game with ID {epicGame.GameID} has registry but we can't match it! gameFilePath is {gameFilePath}.");
+ continue;
+ }
+
+ mc = Regex.Matches(epicGameInstallKeyNameAndValue, @"(.*)\\([^\\]*)");
+ string epicGameInstallKeyName = mc[0].Groups[1].ToString();
+ string epicGameInstallKeyValue = mc[0].Groups[2].ToString();
+
+ try
+ {
+ RegistryKey epicGameInstallKey = Registry.LocalMachine.OpenSubKey(epicGameInstallKeyName, RegistryKeyPermissionCheck.ReadSubTree);
+ if (epicGameInstallKey == null)
+ {
+ // then we have a problem as we cannot find the game exe location!
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic game with ID {epicGame.GameID} has a install reg key we cannot find! epicGameInstallKey is {gameFilePath} and epicGameInstallKeyValue is {epicGameInstallKeyValue}.");
+ continue;
+ }
+ epicGameInstallLocation = Path.Combine(epicGameInstallKey.GetValue(epicGameInstallKeyValue).ToString(), epicGameRestOfFile);
+ if (!File.Exists(epicGameInstallLocation))
+ {
+ // then we have a problem as we cannot locate the game exe file to start!
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic game with ID {epicGame.GameID} has gameexe we cannot find! epicGameInstallLocation is {epicGameInstallLocation}.");
+ continue;
+ }
+ epicGame.GameExePath = epicGameInstallLocation;
+
+ }
+ catch (SecurityException ex)
+ {
+ logger.Warn(ex, $"EpicLibrary/LoadInstalledGames: The user does not have the permissions required to read the Epic Game location registry key {epicGameInstallKeyName}, so skipping game");
+ continue;
+ }
+ catch (ObjectDisposedException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/LoadInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Epic ClientPath registry key (closed keys cannot be accessed), so skipping game");
+ continue;
+ }
+ catch (IOException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/LoadInstalledGames: The Epic ClientPath registry key has been marked for deletion so we cannot access the value dueing the EpicLibrary check, so skipping game");
+ continue;
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/LoadInstalledGames: The user does not have the necessary registry rights to check whether Epic is installed, so skipping game");
+ continue;
+ }
+ }
+ else
+ {
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Game File Path {gameFilePath} starts with '[HKEY_CURRENT_USER' but didn't match the regex when it should have, so skipping game");
+ continue;
+ }
+ }
+ else
+ {
+ // If we get here, then the gameFilepath is the actual filepath! So we just copy it.
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Game File Path {gameFilePath} doesn't start with '[HKEY_LOCAL_MACHINE' or '[HKEY_CURRENT_USER' so it must be aplain file path");
+ epicGame.GameExePath = gameFilePath;
+ }
+
+
+ if (!File.Exists(epicGame.GameExePath))
+ {
+ logger.Warn($"EpicLibrary/LoadInstalledGames: Epic game with ID {epicGame.GameID} found but no game exe found at epicGame.GameExePath {epicGame.GameExePath} so skipping game");
+ continue;
+ }
+
+ // TODO check for icon! For now we will just use the exe one
+ epicGame.GameIconPath = epicGame.GameExePath;
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Epic gameIconPath = {epicGame.GameIconPath} (currently just taking it from the file exe!");
+
+ // If we reach here we add the Game to the list of games we have!
+ _allEpicGames.Add(new EpicGame(epicGame.GameID, epicGame.GameName, epicGame.GameExePath, epicGame.GameIconPath));
+ }
+ else
+ {
+ // If we can't find the __Installer\installerdata.xml file then we ignore this game
+ logger.Trace($"EpicLibrary/LoadInstalledGames: Couldn't find Game Installer Data file at {gameInstallerData} for game with GameID {epicGame.GameID} so skipping this game");
+ continue;
+ }
+
+
+ }
+ catch (Exception ex)
+ {
+ logger.Error(ex, $"EpicLibrary/LoadInstalledGames: Failed to import installed Epic game {package}.");
+ }
+ }
+ }
+ else
+ {
+ logger.Warn($"EpicLibrary/LoadInstalledGames: No Epic games installed in the Epic library");
+ return false;
+ }
+
+ logger.Info($"EpicLibrary/LoadInstalledGames: Found {_allEpicGames.Count} installed Epic games");
+
+ }
+
+ catch (ArgumentNullException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/GetAllInstalledGames: An argument supplied to the function is null.");
+ }
+ catch (NotSupportedException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/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, "EpicLibrary/GetAllInstalledGames: The path is longer than the maximum allowed by the operating system.");
+ }
+ catch (SecurityException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/GetAllInstalledGames: The user does not have the permissions required to read the Epic InstallDir registry key.");
+ }
+ catch (ObjectDisposedException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/GetAllInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Epic InstallDir registry key (closed keys cannot be accessed).");
+ }
+ catch (IOException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/GetAllInstalledGames: The Epic InstallDir registry key has been marked for deletion so we cannot access the value dueing the EpicLibrary check.");
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ logger.Warn(ex, "EpicLibrary/GetAllInstalledGames: The user does not have the necessary registry rights to check whether Epic is installed.");
+ }
+
+ return true;
+ }
+
+ #endregion
+
+ }
+
+ [global::System.Serializable]
+ public class EpicLibraryException : GameLibraryException
+ {
+ public EpicLibraryException() { }
+ public EpicLibraryException(string message) : base(message) { }
+ public EpicLibraryException(string message, Exception inner) : base(message, inner) { }
+ protected EpicLibraryException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+ }
+
+}
diff --git a/DisplayMagician/GameLibraries/GameLibrary.cs b/DisplayMagician/GameLibraries/GameLibrary.cs
index 5884915..798a21e 100644
--- a/DisplayMagician/GameLibraries/GameLibrary.cs
+++ b/DisplayMagician/GameLibraries/GameLibrary.cs
@@ -8,7 +8,8 @@ namespace DisplayMagician.GameLibraries
Unknown,
Steam,
Uplay,
- Origin
+ Origin,
+ Epic
}
public class GameLibrary