DisplayMagician/HeliosPlus/GameLibraries/UplayLibrary.cs
Terry MacDonald 401e76c6e4 Can create Uplay shortcuts now
Added support to read Uplay games and
to load them into the interface. Also added
the ability to save the Uplay shortcuts.
Now need to test and then add the ability
to run Uplay Games!
2020-11-14 18:00:57 +13:00

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<Game> _allUplayGames = new List<Game>();
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<Game> 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) { }
}
}