mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
Added robust Uplay config file game parsing
Used Josef Nemec's AMAZING code as a starter at https://github.com/JosefNemec/PlayniteExtensions/blob/master/source/Libraries/UplayLibrary/Models/ProductSchema.cs to build a proper Uplay Game parser! Uplay use protobuf encoded files that contain YAML embedded within them, just to annoy developers like me. Uplay processing should be much more robust now.
This commit is contained in:
parent
4a8acd4b86
commit
b02155d9b1
@ -108,7 +108,7 @@
|
||||
<Compile Include="GameLibraries\SteamAppInfoParser\Package.cs" />
|
||||
<Compile Include="GameLibraries\SteamAppInfoParser\PackageInfo.cs" />
|
||||
<Compile Include="GameLibraries\SteamAppInfoParser\App.cs" />
|
||||
<Compile Include="GameLibraries\UplayConfigurationParser\UplayConfigurationParser.cs" />
|
||||
<Compile Include="GameLibraries\UplayFileStructure.cs" />
|
||||
<Compile Include="GameLibraries\OriginGame.cs" />
|
||||
<Compile Include="GameLibraries\OriginLibrary.cs" />
|
||||
<Compile Include="GameLibraries\UplayLibrary.cs" />
|
||||
@ -312,6 +312,9 @@
|
||||
<PackageReference Include="NLog">
|
||||
<Version>4.7.11</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="protobuf-net">
|
||||
<Version>3.0.101</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="QueryString.NET">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
@ -324,6 +327,9 @@
|
||||
<PackageReference Include="WinFormAnimation">
|
||||
<Version>1.6.0.4</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="YamlDotNet">
|
||||
<Version>11.2.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Properties\Settings.settings">
|
||||
|
@ -1,104 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// This configuration parses logic is kept here for possible future use
|
||||
// It was really difficult to find this logic in some obscure webpage
|
||||
// so I'm keeping it in case I need it later.
|
||||
namespace DisplayMagician.GameLibraries.UplayConfigurationParser
|
||||
{
|
||||
class UplayConfigurationParser
|
||||
{
|
||||
|
||||
/*def _convert_data(self, data):
|
||||
# calculate object size (konrad's formula)
|
||||
if data > 256 * 256:
|
||||
data = data - (128 * 256 * math.ceil(data / (256 * 256)))
|
||||
data = data - (128 * math.ceil(data / 256))
|
||||
else:
|
||||
if data > 256:
|
||||
data = data - (128 * math.ceil(data / 256))
|
||||
return data*/
|
||||
|
||||
internal static decimal ConvertData (decimal data)
|
||||
{
|
||||
if (data > 65536)
|
||||
{
|
||||
data = data - (128 * 256 * Math.Ceiling(data / 65536));
|
||||
}
|
||||
else if (data > 256)
|
||||
{
|
||||
data = data - (128 * Math.Ceiling(data / 256));
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*def _parse_configuration_header(self, header, second_eight= False):
|
||||
try:
|
||||
offset = 1
|
||||
multiplier = 1
|
||||
record_size = 0
|
||||
tmp_size = 0
|
||||
|
||||
if second_eight:
|
||||
while header[offset] != 0x08 or(header[offset] == 0x08 and header[offset + 1] == 0x08) :
|
||||
record_size += header[offset] * multiplier
|
||||
multiplier *= 256
|
||||
offset += 1
|
||||
tmp_size += 1
|
||||
else:
|
||||
while header[offset] != 0x08 or record_size == 0:
|
||||
record_size += header[offset] * multiplier
|
||||
multiplier *= 256
|
||||
offset += 1
|
||||
tmp_size += 1
|
||||
|
||||
record_size = self._convert_data(record_size)
|
||||
|
||||
offset += 1 # skip 0x08
|
||||
|
||||
# look for launch_id
|
||||
multiplier = 1
|
||||
launch_id = 0
|
||||
|
||||
while header[offset] != 0x10 or header[offset + 1] == 0x10:
|
||||
launch_id += header[offset] * multiplier
|
||||
multiplier *= 256
|
||||
offset += 1
|
||||
|
||||
launch_id = self._convert_data(launch_id)
|
||||
|
||||
offset += 1 # skip 0x10
|
||||
|
||||
multiplier = 1
|
||||
launch_id_2 = 0
|
||||
while header[offset] != 0x1A or(header[offset] == 0x1A and header[offset + 1] == 0x1A) :
|
||||
launch_id_2 += header[offset] * multiplier
|
||||
multiplier *= 256
|
||||
offset += 1
|
||||
|
||||
launch_id_2 = self._convert_data(launch_id_2)
|
||||
|
||||
#if object size is smaller than 128b, there might be a chance that secondary size will not occupy 2b
|
||||
if record_size - offset < 128 <= record_size:
|
||||
tmp_size -= 1
|
||||
record_size += 1
|
||||
|
||||
# we end up in the middle of header, return values normalized
|
||||
# to end of record as well real yaml size and game launch_id
|
||||
return record_size - offset, launch_id, launch_id_2, offset + tmp_size + 1
|
||||
except:
|
||||
# something went horribly wrong, do not crash it,
|
||||
# just return 0s, this way it will be handled later in the code
|
||||
# 10 is to step a little in configuration file in order to find next game
|
||||
return 0, 0, 0, 10*/
|
||||
|
||||
//internal static decimal ParseConfigurationHeader(decimal data);
|
||||
}
|
||||
}
|
144
DisplayMagician/GameLibraries/UplayFileStructure.cs
Normal file
144
DisplayMagician/GameLibraries/UplayFileStructure.cs
Normal file
@ -0,0 +1,144 @@
|
||||
using ProtoBuf;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DisplayMagician.GameLibraries
|
||||
{
|
||||
// #####################################################################################################
|
||||
// # This set of classes are used for deserialising Uplay protobuf files
|
||||
// #####################################################################################################
|
||||
|
||||
[ProtoContract]
|
||||
public class UplayCachedGame
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public uint UplayId { get; set; }
|
||||
[ProtoMember(2)]
|
||||
public uint InstallId { get; set; }
|
||||
[ProtoMember(3)]
|
||||
public string GameInfo { get; set; }
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class UplayCachedGameCollection
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public List<UplayCachedGame> Games { get; set; }
|
||||
}
|
||||
|
||||
// #####################################################################################################
|
||||
// # This set of classes are used for deserialising Uplay YAML enbedded within the protobuf file format
|
||||
// #####################################################################################################
|
||||
public class ProductInformation
|
||||
{
|
||||
public class Executable
|
||||
{
|
||||
public class Path
|
||||
{
|
||||
public string relative;
|
||||
}
|
||||
|
||||
public class WorkingDirectory
|
||||
{
|
||||
public string register;
|
||||
public string append;
|
||||
}
|
||||
|
||||
public Path path;
|
||||
public WorkingDirectory working_directory;
|
||||
public string internal_name;
|
||||
public string description;
|
||||
public string shortcut_name;
|
||||
public string icon_image;
|
||||
}
|
||||
|
||||
public class StartGameItem
|
||||
{
|
||||
public bool after_game_report_enabled;
|
||||
public bool overlay_supported;
|
||||
public bool overlay_product_activation_enabled;
|
||||
public bool overlay_required;
|
||||
public bool overlay_shop_enabled;
|
||||
public bool legacy_ticket_enabled;
|
||||
public List<Executable> executables;
|
||||
|
||||
}
|
||||
|
||||
public class StartGame
|
||||
{
|
||||
public StartGameItem online;
|
||||
public StartGameItem offline;
|
||||
}
|
||||
|
||||
public class DigitalDistribution
|
||||
{
|
||||
public int version;
|
||||
}
|
||||
|
||||
public class Localization
|
||||
{
|
||||
public string l1;
|
||||
}
|
||||
|
||||
public class Club
|
||||
{
|
||||
public bool enabled;
|
||||
}
|
||||
|
||||
public class Addon
|
||||
{
|
||||
public uint id;
|
||||
public bool is_visible;
|
||||
public string name;
|
||||
public string description;
|
||||
public string thumb_image;
|
||||
}
|
||||
|
||||
public class Uplay
|
||||
{
|
||||
public string game_code;
|
||||
public string achievements;
|
||||
public string achievements_sync_id;
|
||||
}
|
||||
|
||||
public class ThirdPartyPlatform
|
||||
{
|
||||
public string name;
|
||||
}
|
||||
|
||||
public class Product
|
||||
{
|
||||
public string name;
|
||||
public string background_image;
|
||||
public string thumb_image;
|
||||
public string logo_image;
|
||||
public string dialog_image;
|
||||
public string icon_image;
|
||||
public ThirdPartyPlatform third_party_platform;
|
||||
public string sort_string;
|
||||
public bool cloud_saves;
|
||||
public string forum_url;
|
||||
public string homepage_url;
|
||||
public string facebook_url;
|
||||
public string help_url;
|
||||
public bool after_game_report_ad;
|
||||
public bool force_safe_mode;
|
||||
public bool uplay_pipe_required;
|
||||
public bool show_properties;
|
||||
public bool game_streaming_enabled;
|
||||
public Uplay uplay;
|
||||
public List<Addon> addons;
|
||||
public Club club;
|
||||
public DigitalDistribution digital_distribution;
|
||||
public bool is_ulc;
|
||||
public bool is_visible;
|
||||
public StartGame start_game;
|
||||
}
|
||||
|
||||
public string version;
|
||||
public Product root;
|
||||
public Dictionary<string,Localization> localizations;
|
||||
public uint uplay_id;
|
||||
public uint install_id;
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,10 @@ using Microsoft.Win32;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Diagnostics;
|
||||
using ProtoBuf;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
using System.Globalization;
|
||||
|
||||
namespace DisplayMagician.GameLibraries
|
||||
{
|
||||
@ -404,6 +408,52 @@ namespace DisplayMagician.GameLibraries
|
||||
|
||||
}
|
||||
|
||||
public bool GetInstallDirFromRegKey(string regKeyPath, out string filePath)
|
||||
{
|
||||
filePath = "";
|
||||
|
||||
RegistryKey uplayGameInstallKey;
|
||||
if (regKeyPath.StartsWith("HKEY_LOCAL_MACHINE"))
|
||||
{
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyPath}");
|
||||
string regKeyText = regKeyPath.Replace(@"HKEY_LOCAL_MACHINE\", "");
|
||||
uplayGameInstallKey = Registry.LocalMachine.OpenSubKey(regKeyText, RegistryKeyPermissionCheck.ReadSubTree);
|
||||
}
|
||||
else if (regKeyPath.StartsWith("HKEY_CURRENT_USER"))
|
||||
{
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKCU reg key {regKeyPath}");
|
||||
string regKeyText = regKeyPath.Replace(@"HKEY_CURRENT_USER\", "");
|
||||
uplayGameInstallKey = Registry.LocalMachine.OpenSubKey(regKeyText, RegistryKeyPermissionCheck.ReadSubTree);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Skipping processing as regkey supplied was odd: {regKeyPath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the key doesn't exist we skip it as the game isn't installed any longer!
|
||||
if (uplayGameInstallKey == null)
|
||||
{
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Skipping Uplay Game as it isn't installed at the moment (it was uninstalled at some point)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// From that we lookup the actual game path
|
||||
string gameInstallDir = uplayGameInstallKey.GetValue("InstallDir", "").ToString();
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: gameInstallDir found = {gameInstallDir}");
|
||||
if (!String.IsNullOrWhiteSpace(gameInstallDir))
|
||||
{
|
||||
filePath = Path.GetFullPath(gameInstallDir).TrimEnd('\\');
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"UplayLibrary/GetInstallDirFromRegKey: gameInstallDir is null or all whitespace!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool LoadInstalledGames()
|
||||
{
|
||||
try
|
||||
@ -475,210 +525,286 @@ namespace DisplayMagician.GameLibraries
|
||||
// Access {installdir}\\cache\\configuration\\configurations file
|
||||
string uplayConfigFilePath = _uplayPath + @"cache\configuration\configurations";
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Config File Path = {uplayConfigFilePath }");
|
||||
string uplayConfigFileString = File.ReadAllText(uplayConfigFilePath);
|
||||
uplayConfigFileString = uplayConfigFileString.Remove(0, 12);
|
||||
string[] dividingText = { "version: 2.0" };
|
||||
List<string> uplayConfigFile = uplayConfigFileString.Split(dividingText,StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
// 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.OrdinalIgnoreCase))
|
||||
// continue;
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.IgnoreUnmatchedProperties()
|
||||
.Build();
|
||||
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Entry that starts with 'version: 2.0') = {uplayEntry}");
|
||||
|
||||
//Split the record into entrylines
|
||||
string[] delimeters = { "\r\n" };
|
||||
List<string> uplayEntryLines = uplayEntry.Split(delimeters, System.StringSplitOptions.RemoveEmptyEntries).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.StartsWith(" 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)
|
||||
using (var file = File.OpenRead(uplayConfigFilePath))
|
||||
{
|
||||
// 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:
|
||||
GameAppInfo uplayGameAppInfo = new GameAppInfo();
|
||||
|
||||
// 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)))
|
||||
|
||||
bool gotGameIconPath = false;
|
||||
bool gotGameName = false;
|
||||
string gameFileName = "";
|
||||
bool gotGameFileName = false;
|
||||
string gameId = "";
|
||||
bool gotGameId = false;
|
||||
string gameRegistryKey = "";
|
||||
bool gotGameRegistryKey = false;
|
||||
for (int i = 0; i <= 50; i++)
|
||||
try
|
||||
{
|
||||
// Stop this loop once we have both filname and gameid
|
||||
if (gotGameFileName && gotGameId && gotGameIconPath && gotGameName && gotGameRegistryKey)
|
||||
var gameCollection = ProtoBuf.Serializer.Deserialize<UplayCachedGameCollection>(file).Games;
|
||||
foreach (var item in gameCollection)
|
||||
{
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: We got all the entries: gameFileName = {gameFileName } && gameId = {gameId } && gameIconPath = {uplayGameAppInfo.GameIconPath} && gameName = {uplayGameAppInfo.GameName}");
|
||||
if (!String.IsNullOrEmpty(item.GameInfo))
|
||||
{
|
||||
ProductInformation productInfo;
|
||||
try
|
||||
{
|
||||
productInfo = deserializer.Deserialize<ProductInformation>(item.GameInfo);
|
||||
var root = productInfo.root;
|
||||
|
||||
string gameName = "";
|
||||
string gameExePath = "";
|
||||
string gameIconPath = "";
|
||||
|
||||
// Try finding the Game Name using the localisation currently in use as a first step
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Looking for the Uplay game name.");
|
||||
string currentLang = CultureInfo.CurrentCulture.Name;
|
||||
foreach (var lang in productInfo.localizations)
|
||||
{
|
||||
// If we find the same language as the user is using, then let's use that!
|
||||
if (lang.Key.Equals(currentLang))
|
||||
{
|
||||
gameName = lang.Value.l1;
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: We found the Uplay game name '{gameName}' in the user's language of {currentLang}.");
|
||||
break;
|
||||
}
|
||||
|
||||
// This line contains the Game Name
|
||||
if (uplayEntryLines[i].StartsWith(" name:", StringComparison.OrdinalIgnoreCase) && !gotGameName)
|
||||
{
|
||||
mc = Regex.Matches(uplayEntryLines[i], @" name\: (.*)");
|
||||
if (mc.Count > 0)
|
||||
{
|
||||
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];
|
||||
}
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Found uplayGameAppInfo.GameName = {uplayGameAppInfo.GameName}");
|
||||
gotGameName = true;
|
||||
}
|
||||
}
|
||||
else if (uplayEntryLines[i].StartsWith(" icon_image:", StringComparison.OrdinalIgnoreCase) && !gotGameIconPath)
|
||||
// If the gameName isn't available in the users language, then we go for default
|
||||
if (String.IsNullOrEmpty(gameName) && productInfo.localizations.ContainsKey("default"))
|
||||
{
|
||||
mc = Regex.Matches(uplayEntryLines[i], @"icon_image: (.*)");
|
||||
if (mc.Count > 0)
|
||||
gameName = productInfo.localizations["default"].l1;
|
||||
if (!String.IsNullOrEmpty(gameName))
|
||||
{
|
||||
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];
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Found iconImageFile = {iconImageFileName }");
|
||||
}
|
||||
//61fdd16f06ae08158d0a6d476f1c6bd5.ico
|
||||
string uplayGameIconPath = _uplayPath + @"data\games\" + iconImageFileName;
|
||||
if (File.Exists(uplayGameIconPath) && uplayGameIconPath.EndsWith(".ico"))
|
||||
{
|
||||
uplayGameAppInfo.GameIconPath = uplayGameIconPath;
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Found uplayGameAppInfo.GameUplayIconPath = {uplayGameAppInfo.GameIconPath }");
|
||||
}
|
||||
gotGameIconPath = true;
|
||||
}
|
||||
}
|
||||
// This line contains the filename
|
||||
else if (uplayEntryLines[i].StartsWith(" relative:") && !gotGameFileName)
|
||||
{
|
||||
mc = Regex.Matches(uplayEntryLines[i], @"relative: (.*)");
|
||||
if (mc.Count > 0)
|
||||
{
|
||||
gameFileName = mc[0].Groups[1].ToString();
|
||||
gotGameFileName = true;
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Found gameFileName = {gameFileName}");
|
||||
}
|
||||
}
|
||||
// This line contains the registryKey
|
||||
else if (uplayEntryLines[i].StartsWith(" register: HKEY_LOCAL_MACHINE") && !gotGameId)
|
||||
{
|
||||
|
||||
// Lookup the GameId within the registry key
|
||||
mc = Regex.Matches(uplayEntryLines[i], @"Installs\\(\d+)\\InstallDir");
|
||||
if (mc.Count > 0)
|
||||
{
|
||||
gameId = mc[0].Groups[1].ToString();
|
||||
gotGameId = true;
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Found gameId = {gameId}");
|
||||
}
|
||||
mc = Regex.Matches(uplayEntryLines[i], @"HKEY_LOCAL_MACHINE\\(.*?)\\InstallDir");
|
||||
if (mc.Count > 0)
|
||||
{
|
||||
gameRegistryKey = mc[0].Groups[1].ToString();
|
||||
gameRegistryKey = gameRegistryKey.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
|
||||
gotGameRegistryKey = true;
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Found gameRegistryKey = {gameRegistryKey}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: gameId = {gameId}");
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: gameFileName = {gameFileName}");
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: gameGameIconPath = {uplayGameAppInfo.GameIconPath}");
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: gameRegistryKey = {gameRegistryKey}");
|
||||
|
||||
if (gotGameRegistryKey)
|
||||
{
|
||||
// Now we need to lookup the game install path in registry using the game reg we got above
|
||||
// We assume its 64-bit OS too (not 32bit)
|
||||
using (RegistryKey uplayGameInstallKey = Registry.LocalMachine.OpenSubKey(gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree))
|
||||
{
|
||||
// If the key doesn't exist we skip it as the game isn't installed any longer!
|
||||
if (uplayGameInstallKey == null)
|
||||
{
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Skipping Uplay Game {uplayGameAppInfo.GameName} as it isn't installed at the moment (it was uninstalled at some point)");
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we get here, then we have a real game.
|
||||
foreach (string regKeyName in uplayGameInstallKey.GetValueNames())
|
||||
{
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: uplayGameInstallKey[{regKeyName}] = {uplayGameInstallKey.GetValue(regKeyName)}");
|
||||
}
|
||||
|
||||
// From that we lookup the actual game path
|
||||
string gameInstallDir = uplayGameInstallKey.GetValue("InstallDir", "").ToString();
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: gameInstallDir found = {gameInstallDir}");
|
||||
if (!String.IsNullOrWhiteSpace(gameInstallDir))
|
||||
{
|
||||
uplayGameAppInfo.GameInstallDir = Path.GetFullPath(gameInstallDir).TrimEnd('\\');
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: uplayGameAppInfo.GameInstallDir = {uplayGameAppInfo.GameInstallDir }");
|
||||
uplayGameAppInfo.GameExePath = Path.Combine(uplayGameAppInfo.GameInstallDir, gameFileName);
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: uplayGameAppInfo.GameExe = {uplayGameAppInfo.GameExePath}");
|
||||
uplayGameAppInfo.GameID = gameId;
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: uplayGameAppInfo.GameID = {uplayGameAppInfo.GameID }");
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Looking for the Uplay game name with the en language as the local language didn't work. We found game name '{gameName}'. ");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"UplayLibrary/LoadInstalledGames: gameInstallDir is null or all whitespace!");
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Looking for the Uplay game name with the en language as the local language didn't work. We found no en language. ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now we'll try to sort out the rest of the game data!
|
||||
// We first look for the online executable information
|
||||
if (root.start_game.online.executables.Count > 0)
|
||||
{
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay game {gameName} has some online executables to process! ");
|
||||
|
||||
// First up we look at the online games, cause they're just better!
|
||||
foreach (var executable in root.start_game.online.executables)
|
||||
{
|
||||
string exePath = "";
|
||||
|
||||
// Check if its a full path or a relative path
|
||||
if (!String.IsNullOrEmpty(executable.path.relative))
|
||||
{
|
||||
if (executable.working_directory.register.StartsWith("HKEY_LOCAL_MACHINE"))
|
||||
{
|
||||
// This copes with relative files using a HKEY_LOCAL_MACHINE registry
|
||||
|
||||
string regKeyText = executable.working_directory.register;
|
||||
regKeyText = regKeyText.Replace(@"\InstallDir", "");
|
||||
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
|
||||
|
||||
if (this.GetInstallDirFromRegKey(regKeyText, out exePath))
|
||||
{
|
||||
gameExePath = Path.Combine(exePath, executable.path.relative);
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses local machine registry key: {executable.working_directory.register} ");
|
||||
}
|
||||
}
|
||||
/*else if (executable.working_directory.register.StartsWith("HKEY_CURRENT_USER"))
|
||||
{
|
||||
// This copes with relative files using a HKEY_CURRENT_USER registry
|
||||
|
||||
string regKeyText = executable.working_directory.register;
|
||||
regKeyText = regKeyText.Replace(@"\InstallDir", "");
|
||||
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
|
||||
|
||||
if (this.GetInstallDirFromRegKey(executable.working_directory.register, out exePath))
|
||||
{
|
||||
gameExePath = Path.Combine(exePath, executable.path.relative);
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses current user registry key: {executable.working_directory.register} ");
|
||||
}
|
||||
}*/
|
||||
else if (!String.IsNullOrEmpty(executable.working_directory.append))
|
||||
{
|
||||
// This copes with relative files using an appended path
|
||||
gameExePath = Path.Combine(executable.working_directory.append, executable.path.relative);
|
||||
gameIconPath = Path.Combine(executable.working_directory.append, executable.icon_image);
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses appended file path: {executable.working_directory.append} ");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Problem!
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: Found relative GameExePath {executable.path.relative} for Uplay game {gameName} but no registry key or appended file path! Skipping this game.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should cope with full pathed files, but we have no examples to test! So log it
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: Found non-relative GameExePath {executable.path} for Uplay game {gameName} but we've not seen it before so no idea how to handle it! Skipping this game.");
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: executable.path for troubleshooting: {executable.path}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// We should check the exe file exists, and if it doesn't then we need to do the next exe
|
||||
if (!File.Exists(gameExePath))
|
||||
{
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: Couldn't find the GameExePath {gameExePath} for Uplay game {gameName} so skipping this exe, and trying the next one.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now try to get the Uplay game icon
|
||||
if (!String.IsNullOrEmpty(executable.icon_image))
|
||||
{
|
||||
gameIconPath = Path.Combine(_uplayPath, "data", "games", executable.icon_image);
|
||||
|
||||
// If the icon file isn't actually there, then use the game exe instead.
|
||||
if (!File.Exists(gameIconPath))
|
||||
{
|
||||
gameIconPath = gameExePath;
|
||||
}
|
||||
}
|
||||
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Found GameExePath {exePath} and Icon Path {gameIconPath} for Uplay game {gameName}.");
|
||||
|
||||
// We do a final check to make sure that we do have a GameName, and if not we use the shortcut
|
||||
if (String.IsNullOrEmpty(gameName) && !String.IsNullOrEmpty(executable.shortcut_name))
|
||||
{
|
||||
gameName = executable.shortcut_name;
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Game Name was still empty, so we're using the shortcut name as a last resort: {executable.shortcut_name} ");
|
||||
}
|
||||
|
||||
// Now we need to save the game name, cause if we're here then we're good enough to save
|
||||
// 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!
|
||||
_allGames.Add(new UplayGame(uplayGameAppInfo.GameID, uplayGameAppInfo.GameName, uplayGameAppInfo.GameExePath, uplayGameAppInfo.GameIconPath));
|
||||
logger.Debug($"UplayLibrary/LoadInstalledGames: Adding Uplay Game with game id {uplayGameAppInfo.GameID}, name {uplayGameAppInfo.GameName}, game exe {uplayGameAppInfo.GameExePath} and icon path {uplayGameAppInfo.GameIconPath}");
|
||||
_allGames.Add(new UplayGame(productInfo.uplay_id.ToString(), gameName, gameExePath, gameIconPath));
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Adding Uplay Game with game id {productInfo.uplay_id}, name {gameName}, game exe {gameExePath} and icon path {gameIconPath}");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// This is the offline exes
|
||||
else if (root.start_game.offline.executables.Count > 0)
|
||||
{
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay game {gameName} has some offline executables to process! ");
|
||||
|
||||
// we look at the offline games, cause there weren't any online ones
|
||||
foreach (var executable in root.start_game.offline.executables)
|
||||
{
|
||||
string exePath = "";
|
||||
|
||||
// Check if its a full path or a relative path
|
||||
if (!String.IsNullOrEmpty(executable.path.relative))
|
||||
{
|
||||
if (executable.working_directory.register.StartsWith("HKEY_LOCAL_MACHINE"))
|
||||
{
|
||||
// This copes with relative files using a HKEY_LOCAL_MACHINE registry
|
||||
|
||||
string regKeyText = executable.working_directory.register;
|
||||
regKeyText = regKeyText.Replace(@"\InstallDir", "");
|
||||
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
|
||||
|
||||
if (this.GetInstallDirFromRegKey(regKeyText, out exePath))
|
||||
{
|
||||
gameExePath = Path.Combine(exePath, executable.path.relative);
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses local machine registry key: {executable.working_directory.register} ");
|
||||
}
|
||||
}
|
||||
/*else if (executable.working_directory.register.StartsWith("HKEY_CURRENT_USER"))
|
||||
{
|
||||
// This copes with relative files using a HKEY_CURRENT_USER registry
|
||||
|
||||
string regKeyText = executable.working_directory.register;
|
||||
regKeyText = regKeyText.Replace(@"\InstallDir", "");
|
||||
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
|
||||
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
|
||||
|
||||
if (this.GetInstallDirFromRegKey(executable.working_directory.register, out exePath))
|
||||
{
|
||||
gameExePath = Path.Combine(exePath, executable.path.relative);
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses current user registry key: {executable.working_directory.register} ");
|
||||
}
|
||||
}*/
|
||||
else if (!String.IsNullOrEmpty(executable.working_directory.append))
|
||||
{
|
||||
// This copes with relative files using an appended path
|
||||
gameExePath = Path.Combine(executable.working_directory.append, executable.path.relative);
|
||||
gameIconPath = Path.Combine(executable.working_directory.append, executable.icon_image);
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses appended file path: {executable.working_directory.append} ");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Problem!
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: Found relative GameExePath {executable.path.relative} for Uplay game {gameName} but no registry key or appended file path! Skipping this game.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should cope with full pathed files, but we have no examples to test! So log it
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: Found non-relative GameExePath {executable.path} for Uplay game {gameName} but we've not seen it before so no idea how to handle it! Skipping this game.");
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: executable.path for troubleshooting: {executable.path}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// We should check the exe file exists, and if it doesn't then we need to do the next exe
|
||||
if (!File.Exists(gameExePath))
|
||||
{
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: Couldn't find the GameExePath {gameExePath} for Uplay game {gameName} so skipping this exe, and trying the next one.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now try to get the Uplay game icon
|
||||
if (!String.IsNullOrEmpty(executable.icon_image))
|
||||
{
|
||||
gameIconPath = Path.Combine(_uplayPath, "data", "games", executable.icon_image);
|
||||
|
||||
// If the icon file isn't actually there, then use the game exe instead.
|
||||
if (!File.Exists(gameIconPath))
|
||||
{
|
||||
gameIconPath = gameExePath;
|
||||
}
|
||||
}
|
||||
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Found GameExePath {exePath} and Icon Path {gameIconPath} for Uplay game {gameName}.");
|
||||
|
||||
// We do a final check to make sure that we do have a GameName, and if not we use the shortcut
|
||||
if (String.IsNullOrEmpty(gameName) && !String.IsNullOrEmpty(executable.shortcut_name))
|
||||
{
|
||||
gameName = executable.shortcut_name;
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Game Name was still empty, so we're using the shortcut name as a last resort: {executable.shortcut_name} ");
|
||||
}
|
||||
|
||||
// Now we need to save the game name, cause if we're here then we're good enough to save
|
||||
// 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!
|
||||
_allGames.Add(new UplayGame(productInfo.uplay_id.ToString(), gameName, gameExePath, gameIconPath));
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Adding Uplay Game with game id {productInfo.uplay_id}, name {gameName}, game exe {gameExePath} and icon path {gameIconPath}");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Entry {gameName} doesn't have any executables associated with it! We have to skip adding this game.");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// If we get an error processing the game YAML, lets try and skip this game and try the next one. It might work!
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: Problem deserialising the YAML embedded in the Uplay configuration file {uplayConfigFilePath}. Cannot process this games!");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// We can't do anything if we hit here.
|
||||
logger.Error($"UplayLibrary/LoadInstalledGames: Problem deserialising the protobuf Uplay configuration file {uplayConfigFilePath}. Cannot process any games!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info($"UplayLibrary/LoadInstalledGames: Found {_allGames.Count} installed Uplay games");
|
||||
|
@ -26,8 +26,8 @@ using System.Resources;
|
||||
[assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")]
|
||||
|
||||
// Version information
|
||||
[assembly: AssemblyVersion("2.0.1.95")]
|
||||
[assembly: AssemblyFileVersion("2.0.1.95")]
|
||||
[assembly: AssemblyVersion("2.0.1.123")]
|
||||
[assembly: AssemblyFileVersion("2.0.1.123")]
|
||||
[assembly: NeutralResourcesLanguageAttribute( "en" )]
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user