mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
Creation of EpicLibrary files
This commit is contained in:
parent
0f294996c1
commit
28759f8552
@ -99,6 +99,8 @@
|
||||
<Compile Include="GameLibraries\Game.cs" />
|
||||
<Compile Include="GameLibraries\GameLibrary.cs" />
|
||||
<Compile Include="GameLibraries\GameUtils.cs" />
|
||||
<Compile Include="GameLibraries\EpicGame.cs" />
|
||||
<Compile Include="GameLibraries\EpicLibrary.cs" />
|
||||
<Compile Include="GameLibraries\SteamAppInfoParser\AppInfo.cs" />
|
||||
<Compile Include="GameLibraries\SteamAppInfoParser\EUniverse.cs" />
|
||||
<Compile Include="GameLibraries\SteamAppInfoParser\Package.cs" />
|
||||
|
183
DisplayMagician/GameLibraries/EpicGame.cs
Normal file
183
DisplayMagician/GameLibraries/EpicGame.cs
Normal file
@ -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<Process> 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
753
DisplayMagician/GameLibraries/EpicLibrary.cs
Normal file
753
DisplayMagician/GameLibraries/EpicLibrary.cs
Normal file
@ -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<Game> _allEpicGames = new List<Game>();
|
||||
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<string> _epicProcessList = new List<string>(){ "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<Game> 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<Process> epicLibraryProcesses = new List<Process>();
|
||||
|
||||
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<string> 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<string, string> ParseEpicManifest(string path)
|
||||
{
|
||||
string encodedContents = File.ReadAllText(path);
|
||||
Dictionary<string, string> parameters = Regex.Matches(encodedContents, "([^?=&]+)(=([^&]*))?").Cast<Match>().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<string, string> 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) { }
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user