mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
Added the functionality to read the Uplay library correctly, but at present nothing is done with that information. Next step is to add it to the Shortcut Game selector logic.
480 lines
19 KiB
C#
480 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using ValveKeyValue;
|
|
//using HeliosPlus.GameLibraries.UplayAppInfoParser;
|
|
using Microsoft.Win32;
|
|
using System.IO;
|
|
using System.Drawing.IconLib;
|
|
using System.Security;
|
|
using System.Diagnostics;
|
|
using EDIDParser;
|
|
using System.ComponentModel;
|
|
|
|
namespace HeliosPlus.GameLibraries
|
|
{
|
|
public static class UplayLibrary
|
|
{
|
|
#region Class Variables
|
|
// Common items to the class
|
|
private static List<UplayGame> _allUplayGames = new List<UplayGame>();
|
|
private static string uplayAppIdRegex = @"/^[0-9A-F]{1,10}$";
|
|
private static string _uplayExe;
|
|
private static string _uplayPath;
|
|
private static string _uplayConfigVdfFile;
|
|
internal static string registryUplayLauncherKey = @"SOFTWARE\WOW6432Node\Ubisoft\Launcher";
|
|
internal static string registryUplayInstallsKey = @"SOFTWARE\WOW6432Node\Ubisoft\Launcher\Installs";
|
|
internal static string registryUplayOpenCmdKey = @"SOFTWARE\Classes\uplay\Shell\Open\Command";
|
|
|
|
// Other constants that are useful
|
|
#endregion
|
|
|
|
private struct UplayAppInfo
|
|
{
|
|
public uint GameID;
|
|
public string GameName;
|
|
public string GameExe;
|
|
public string GameInstallDir;
|
|
public string GameUplayIconPath;
|
|
}
|
|
|
|
#region Class Constructors
|
|
static UplayLibrary()
|
|
{
|
|
// Find the UplayExe location, and the UplayPath for later
|
|
RegistryKey uplayInstallKey = Registry.LocalMachine.OpenSubKey(registryUplayLauncherKey, RegistryKeyPermissionCheck.ReadSubTree);
|
|
_uplayPath = uplayInstallKey.GetValue("InstallDir", "C:\\Program Files (x86)\\Ubisoft\\Ubisoft Game Launcher\\").ToString();
|
|
_uplayExe = $"{_uplayPath}upc.exe";
|
|
}
|
|
#endregion
|
|
|
|
#region Class Properties
|
|
public static List<UplayGame> AllInstalledGames
|
|
{
|
|
get
|
|
{
|
|
// Load the Uplay Games from Uplay Client if needed
|
|
if (_allUplayGames == null)
|
|
LoadInstalledGames();
|
|
return _allUplayGames;
|
|
}
|
|
}
|
|
|
|
|
|
public static int InstalledUplayGameCount
|
|
{
|
|
get
|
|
{
|
|
return _allUplayGames.Count;
|
|
}
|
|
}
|
|
|
|
public static string UplayExe
|
|
{
|
|
get
|
|
{
|
|
return _uplayExe;
|
|
}
|
|
}
|
|
|
|
public static string UplayPath
|
|
{
|
|
get
|
|
{
|
|
return _uplayPath;
|
|
}
|
|
}
|
|
|
|
public static bool IsUplayInstalled
|
|
{
|
|
get
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(UplayExe) && File.Exists(UplayExe))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
#region Class Methods
|
|
public static bool AddUplayGame(UplayGame uplayGame)
|
|
{
|
|
if (!(uplayGame is UplayGame))
|
|
return false;
|
|
|
|
// Doublecheck if it already exists
|
|
// Because then we just update the one that already exists
|
|
if (ContainsUplayGame(uplayGame))
|
|
{
|
|
// We update the existing Shortcut with the data over
|
|
UplayGame uplayGameToUpdate = GetUplayGame(uplayGame.Id.ToString());
|
|
uplayGame.CopyTo(uplayGameToUpdate);
|
|
}
|
|
else
|
|
{
|
|
// Add the uplayGame to the list of uplayGames
|
|
_allUplayGames.Add(uplayGame);
|
|
}
|
|
|
|
//Doublecheck it's been added
|
|
if (ContainsUplayGame(uplayGame))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
}
|
|
|
|
public static bool RemoveUplayGame(UplayGame uplayGame)
|
|
{
|
|
if (!(uplayGame is UplayGame))
|
|
return false;
|
|
|
|
// Remove the uplayGame from the list.
|
|
int numRemoved = _allUplayGames.RemoveAll(item => item.Id.Equals(uplayGame.Id));
|
|
|
|
if (numRemoved == 1)
|
|
{
|
|
return true;
|
|
}
|
|
else if (numRemoved == 0)
|
|
return false;
|
|
else
|
|
throw new UplayLibraryException();
|
|
}
|
|
|
|
|
|
public static bool RemoveUplayGame(string uplayGameNameOrUuid)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(uplayGameNameOrUuid))
|
|
return false;
|
|
|
|
int numRemoved;
|
|
Match match = Regex.Match(uplayGameNameOrUuid, uplayAppIdRegex, RegexOptions.IgnoreCase);
|
|
if (match.Success)
|
|
numRemoved = _allUplayGames.RemoveAll(item => uplayGameNameOrUuid.Equals(Convert.ToUInt32(item.Id)));
|
|
else
|
|
numRemoved = _allUplayGames.RemoveAll(item => uplayGameNameOrUuid.Equals(item.Name));
|
|
|
|
if (numRemoved == 1)
|
|
return true;
|
|
else if (numRemoved == 0)
|
|
return false;
|
|
else
|
|
throw new UplayLibraryException();
|
|
|
|
}
|
|
|
|
|
|
public static bool ContainsUplayGame(UplayGame uplayGame)
|
|
{
|
|
if (!(uplayGame is UplayGame))
|
|
return false;
|
|
|
|
foreach (UplayGame testUplayGame in _allUplayGames)
|
|
{
|
|
if (testUplayGame.Id.Equals(uplayGame.Id))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static bool ContainsUplayGame(string uplayGameNameOrUuid)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(uplayGameNameOrUuid))
|
|
return false;
|
|
|
|
|
|
Match match = Regex.Match(uplayGameNameOrUuid, uplayAppIdRegex, RegexOptions.IgnoreCase);
|
|
if (match.Success)
|
|
{
|
|
foreach (UplayGame testUplayGame in _allUplayGames)
|
|
{
|
|
if (uplayGameNameOrUuid.Equals(Convert.ToUInt32(testUplayGame.Id)))
|
|
return true;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
foreach (UplayGame testUplayGame in _allUplayGames)
|
|
{
|
|
if (uplayGameNameOrUuid.Equals(testUplayGame.Name))
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
public static bool ContainsUplayGame(uint uplayGameId)
|
|
{
|
|
foreach (UplayGame testUplayGame in _allUplayGames)
|
|
{
|
|
if (uplayGameId == testUplayGame.Id)
|
|
return true;
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
public static UplayGame GetUplayGame(string uplayGameNameOrUuid)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(uplayGameNameOrUuid))
|
|
return null;
|
|
|
|
Match match = Regex.Match(uplayGameNameOrUuid, uplayAppIdRegex, RegexOptions.IgnoreCase);
|
|
if (match.Success)
|
|
{
|
|
foreach (UplayGame testUplayGame in _allUplayGames)
|
|
{
|
|
if (uplayGameNameOrUuid.Equals(Convert.ToUInt32(testUplayGame.Id)))
|
|
return testUplayGame;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
foreach (UplayGame testUplayGame in _allUplayGames)
|
|
{
|
|
if (uplayGameNameOrUuid.Equals(testUplayGame.Name))
|
|
return testUplayGame;
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
public static UplayGame GetUplayGame(uint uplayGameId)
|
|
{
|
|
foreach (UplayGame testUplayGame in _allUplayGames)
|
|
{
|
|
if (uplayGameId == testUplayGame.Id)
|
|
return testUplayGame;
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
public static bool LoadInstalledGames()
|
|
{
|
|
try
|
|
{
|
|
|
|
if (_uplayExe == string.Empty || !File.Exists(_uplayExe))
|
|
{
|
|
// Uplay isn't installed, so we return an empty list.
|
|
return false;
|
|
}
|
|
|
|
|
|
// Look in HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Ubisoft\\Launcher and check the InstallDir key
|
|
// That returns the location of the install dir : E:\Program Files (x86)\Ubisoft\Ubisoft Game Launcher\
|
|
|
|
//RegistryKey uplayInstallKey = Registry.CurrentUser.OpenSubKey(registryUplayInstallsKey, RegistryKeyPermissionCheck.ReadSubTree);
|
|
//string uplayInstallDir = uplayInstallKey.GetValue("InstallDir", "C:\\Program Files (x86)\\Ubisoft\\Ubisoft Game Launcher\\").ToString();
|
|
|
|
// Access {installdir}\\cache\\configuration\\configurations file
|
|
string uplayConfigFilePath = _uplayPath + @"cache\configuration\configurations";
|
|
string uplayConfigFileString = File.ReadAllText(uplayConfigFilePath);
|
|
uplayConfigFileString = uplayConfigFileString.Remove(0, 12);
|
|
// Split the file into records at the SOH unicode character
|
|
List<string> uplayConfigFile = uplayConfigFileString.Split((Char)1).ToList();
|
|
|
|
// Go through every record and attempt to parse it
|
|
foreach (string uplayEntry in uplayConfigFile) {
|
|
// Skip any Uplay entry records that don't start with 'version:'
|
|
if (!uplayEntry.StartsWith("version:",StringComparison.InvariantCultureIgnoreCase))
|
|
continue;
|
|
|
|
//Split the record into entrylines
|
|
List<string> uplayEntryLines = uplayEntry.Split('\n').ToList();
|
|
|
|
// Skip any records NOT containing an entryline with ' start_game:' (note 2 leading spaces)
|
|
// All games contain a start_game entry
|
|
if (!uplayEntryLines.Exists(a => a.StartsWith(" start_game:")))
|
|
continue;
|
|
|
|
// Skip any records containing an entryline with ' third_party_platform:' (note 2 leading spaces)
|
|
// We only want the native uplay games....
|
|
if (uplayEntryLines.Exists(a => a.StartsWith(" third_party_platform:")))
|
|
continue;
|
|
|
|
// if we get here then we have a real game to parse!
|
|
// Yay us :).
|
|
|
|
// First we want to know the index of the start_game entry to use later
|
|
int startGameIndex = uplayEntryLines.FindIndex(a => a == " start_game:");
|
|
MatchCollection mc;
|
|
|
|
// First we check if there are any localization CONSTANTS that we will need to map later.
|
|
Dictionary<string, string> localizations = new Dictionary<string, string>();
|
|
int localizationsIndex = uplayEntryLines.FindIndex(a => a == "localizations:");
|
|
// If there are localizations, then we need to store them for later
|
|
if (localizationsIndex != -1)
|
|
{
|
|
// grab the localizations: -> default: entries to use as a lookup table for the info we need
|
|
int defaultIndex = localizationsIndex + 1;
|
|
int currentIndex = defaultIndex + 1;
|
|
|
|
// Grab all EntryLines with 4 leading spaces (these are all the localizations)
|
|
while (uplayEntryLines[currentIndex].StartsWith(" ")){
|
|
string[] split = uplayEntryLines[currentIndex].Split(':');
|
|
localizations.Add(split[0].Trim(), split[1].Trim());
|
|
currentIndex++;
|
|
}
|
|
|
|
}
|
|
|
|
// for each game record grab:
|
|
UplayAppInfo uplayGameAppInfo = new UplayAppInfo();
|
|
|
|
// name: (lookup the id in lookup table to find the name if needed)
|
|
if (uplayEntryLines.Exists(a => a.StartsWith(" name:", StringComparison.InvariantCultureIgnoreCase)))
|
|
{
|
|
mc = Regex.Matches(uplayEntry, @" name\: (.*)");
|
|
uplayGameAppInfo.GameName = mc[0].Groups[1].ToString();
|
|
// if the name contains a localization reference, then dereference it
|
|
if (localizations.ContainsKey(uplayGameAppInfo.GameName))
|
|
{
|
|
uplayGameAppInfo.GameName = localizations[uplayGameAppInfo.GameName];
|
|
}
|
|
}
|
|
else
|
|
continue;
|
|
|
|
// icon_image: (lookup the id in lookup table to find the ICON)
|
|
if (uplayEntryLines.Exists(a => a.StartsWith(" icon_image:", StringComparison.InvariantCultureIgnoreCase)))
|
|
{
|
|
mc = Regex.Matches(uplayEntry, @" icon_image: (.*)");
|
|
string iconImageFileName = mc[0].Groups[1].ToString();
|
|
// if the icon_image contains a localization reference, then dereference it
|
|
if (localizations.ContainsKey(iconImageFileName))
|
|
{
|
|
iconImageFileName = localizations[iconImageFileName];
|
|
}
|
|
//61fdd16f06ae08158d0a6d476f1c6bd5.ico
|
|
string uplayGameIconPath = _uplayPath + @"data\games\" + iconImageFileName;
|
|
if (File.Exists(uplayGameIconPath) && uplayGameIconPath.EndsWith(".ico"))
|
|
{
|
|
uplayGameAppInfo.GameUplayIconPath = uplayGameIconPath;
|
|
}
|
|
}
|
|
|
|
// find the exe name looking at root: -> start_game: -> online: -> executables: -> path: -> relative: (get ACU.exe)
|
|
// Lookup the Game registry key from looking at root: -> start_game: -> online: -> executables: -> working_directory: -> register: (get HKEY_LOCAL_MACHINE\SOFTWARE\Ubisoft\Launcher\Installs\720\InstallDir)
|
|
// Extract the GameAppID from the number in the working directory (e.g. 720)
|
|
// Lookup the Game install path by reading the game registry key: D:/Ubisoft Game Launcher/Assassin's Creed Unity/
|
|
// join the Game install path and the exe name to get the full game exe path: D:/Ubisoft Game Launcher/Assassin's Creed Unity/ACU.exe
|
|
|
|
//if (uplayEntryLines.Find (a => a.StartsWith(" icon_image:", StringComparison.InvariantCultureIgnoreCase)))
|
|
|
|
string gameFileName = "";
|
|
bool gotGameFileName = false;
|
|
string gameId = "";
|
|
bool gotGameId = false;
|
|
for (int i = startGameIndex; i<= (startGameIndex+30); i++)
|
|
{
|
|
// Stop this loop once we have both filname and gameid
|
|
if (gotGameFileName && gotGameId)
|
|
break;
|
|
|
|
// This line contains the filename
|
|
if (uplayEntryLines[i].StartsWith(" relative:") && !gotGameFileName)
|
|
{
|
|
mc = Regex.Matches(uplayEntryLines[i], @" relative: (.*)");
|
|
gameFileName = mc[0].Groups[1].ToString();
|
|
gotGameFileName = true;
|
|
}
|
|
// This line contains the registryKey
|
|
if (uplayEntryLines[i].StartsWith(" register: HKEY_LOCAL_MACHINE") && !gotGameId)
|
|
{
|
|
// Lookup the GameId within the registry key
|
|
mc = Regex.Matches(uplayEntryLines[i], @"Installs\\(\d+)\\InstallDir");
|
|
gameId = mc[0].Groups[1].ToString();
|
|
gotGameId = true;
|
|
}
|
|
}
|
|
|
|
// Now we need to lookup the game install path in registry using the gameId
|
|
string registryUplayGameInstallsKey = registryUplayInstallsKey + "\\" + gameId;
|
|
RegistryKey uplayGameInstallKey = Registry.LocalMachine.OpenSubKey(registryUplayGameInstallsKey, RegistryKeyPermissionCheck.ReadSubTree);
|
|
|
|
// From that we lookup the actual game path
|
|
uplayGameAppInfo.GameInstallDir = Path.GetFullPath(uplayGameInstallKey.GetValue("InstallDir", "").ToString()).TrimEnd('\\');
|
|
uplayGameAppInfo.GameExe = uplayGameAppInfo.GameInstallDir + gameFileName;
|
|
uplayGameAppInfo.GameID = uint.Parse(gameId);
|
|
|
|
// 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!
|
|
_allUplayGames.Add(new UplayGame(uplayGameAppInfo.GameID, uplayGameAppInfo.GameName, uplayGameAppInfo.GameExe, uplayGameAppInfo.GameUplayIconPath));
|
|
}
|
|
|
|
}
|
|
catch (SecurityException ex)
|
|
{
|
|
Console.WriteLine($"UplayGame/GetAllInstalledGames securityexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
|
|
if (ex.Source != null)
|
|
Console.WriteLine("SecurityException source: {0} - Message: {1}", ex.Source, ex.Message);
|
|
throw;
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
Console.WriteLine($"UplayGame/GetAllInstalledGames unauthorizedaccessexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
|
|
if (ex.Source != null)
|
|
Console.WriteLine("UnauthorizedAccessException source: {0} - Message: {1}", ex.Source, ex.Message);
|
|
throw;
|
|
}
|
|
catch (ObjectDisposedException ex)
|
|
{
|
|
Console.WriteLine($"UplayGame/GetAllInstalledGames objectdisposedexceptions: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
|
|
if (ex.Source != null)
|
|
Console.WriteLine("ObjectDisposedException source: {0} - Message: {1}", ex.Source, ex.Message);
|
|
throw;
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
Console.WriteLine($"UplayGame/GetAllInstalledGames ioexception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
|
|
// Extract some information from this exception, and then
|
|
// throw it to the parent method.
|
|
if (ex.Source != null)
|
|
Console.WriteLine("IOException source: {0} - Message: {1}", ex.Source, ex.Message);
|
|
throw;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
[global::System.Serializable]
|
|
public class UplayLibraryException : Exception
|
|
{
|
|
public UplayLibraryException() { }
|
|
public UplayLibraryException(string message) : base(message) { }
|
|
public UplayLibraryException(string message, Exception inner) : base(message, inner) { }
|
|
protected UplayLibraryException(
|
|
System.Runtime.Serialization.SerializationInfo info,
|
|
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
|
|
}
|
|
|
|
}
|