[WIP] Massive redevelopment to alter shortcut form

Added in a binary VDF reader to find and figure out all
of the Game Icons, Exe locations, Names, Ids and
Install Dirs all from the local file syste,m. It makes the
shortcuts populate within 1 second, rather than the
60 seconds it was taking beforehand. Users should
love the newfound responsiveness.
This commit is contained in:
temacdonald 2020-04-27 22:55:44 +12:00
parent a9bb295d1f
commit 8f41b94427
26 changed files with 1527 additions and 1106 deletions

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ValveKeyValue;
namespace HeliosPlus.GameLibraries.SteamAppInfoParser
{
public class App
{
public uint AppID { get; set; }
public uint Size { get; set; }
public uint InfoState { get; set; }
public DateTime LastUpdated { get; set; }
public ulong Token { get; set; }
public ReadOnlyCollection<byte> Hash { get; set; }
public uint ChangeNumber { get; set; }
public KVObject Data { get; set; }
}
}

View File

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ValveKeyValue;
namespace HeliosPlus.GameLibraries.SteamAppInfoParser
{
class AppInfo
{
private const uint Magic = 0x07_56_44_27;
public EUniverse Universe { get; set; }
public List<App> Apps { get; set; } = new List<App>();
/// <summary>
/// Opens and reads the given filename.
/// </summary>
/// <param name="filename">The file to open and read.</param>
public void Read(string filename)
{
var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Read(fs);
}
/// <summary>
/// Reads the given <see cref="Stream"/>.
/// </summary>
/// <param name="input">The input <see cref="Stream"/> to read from.</param>
public void Read(Stream input)
{
var reader = new BinaryReader(input);
var magic = reader.ReadUInt32();
if (magic != Magic)
{
throw new InvalidDataException($"Unknown magic header: {magic}");
}
Universe = (EUniverse)reader.ReadUInt32();
var deserializer = KVSerializer.Create(KVSerializationFormat.KeyValues1Binary);
do
{
var appid = reader.ReadUInt32();
if (appid == 0)
{
break;
}
var app = new App
{
AppID = appid,
Size = reader.ReadUInt32(),
InfoState = reader.ReadUInt32(),
LastUpdated = DateTimeFromUnixTime(reader.ReadUInt32()),
Token = reader.ReadUInt64(),
Hash = new ReadOnlyCollection<byte>(reader.ReadBytes(20)),
ChangeNumber = reader.ReadUInt32(),
Data = deserializer.Deserialize(input),
};
Apps.Add(app);
} while (true);
}
public static DateTime DateTimeFromUnixTime(uint unixTime)
{
return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTime);
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HeliosPlus.GameLibraries.SteamAppInfoParser
{
public enum EUniverse
{
Invalid = 0,
Public = 1,
Beta = 2,
Internal = 3,
Dev = 4,
Max = 5,
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ValveKeyValue;
namespace HeliosPlus.GameLibraries.SteamAppInfoParser
{
public class Package
{
public uint SubID { get; set; }
public ReadOnlyCollection<byte> Hash { get; set; }
public uint ChangeNumber { get; set; }
public ulong Token { get; set; }
public KVObject Data { get; set; }
}
}

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ValveKeyValue;
namespace HeliosPlus.GameLibraries.SteamAppInfoParser
{
class PackageInfo
{
private const uint Magic = 0x06_56_55_28;
private const uint Magic27 = 0x06_56_55_27;
public EUniverse Universe { get; set; }
public List<Package> Packages { get; set; } = new List<Package>();
/// <summary>
/// Opens and reads the given filename.
/// </summary>
/// <param name="filename">The file to open and read.</param>
public void Read(string filename)
{
var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Read(fs);
}
/// <summary>
/// Reads the given <see cref="Stream"/>.
/// </summary>
/// <param name="input">The input <see cref="Stream"/> to read from.</param>
public void Read(Stream input)
{
var reader = new BinaryReader(input);
var magic = reader.ReadUInt32();
if (magic != Magic && magic != Magic27)
{
throw new InvalidDataException($"Unknown magic header: {magic}");
}
Universe = (EUniverse)reader.ReadUInt32();
var deserializer = KVSerializer.Create(KVSerializationFormat.KeyValues1Binary);
do
{
var subid = reader.ReadUInt32();
if (subid == 0xFFFFFFFF)
{
break;
}
var package = new Package
{
SubID = subid,
Hash = new ReadOnlyCollection<byte>(reader.ReadBytes(20)),
ChangeNumber = reader.ReadUInt32(),
};
if (magic != Magic27)
{
package.Token = reader.ReadUInt64();
}
package.Data = deserializer.Deserialize(input);
Packages.Add(package);
} while (true);
}
}
}

View File

@ -0,0 +1,495 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using HeliosPlus.Resources;
using HeliosPlus.Shared;
using HtmlAgilityPack;
using Microsoft.Win32;
using Newtonsoft.Json;
//using VdfParser;
using Gameloop.Vdf;
using System.Collections.ObjectModel;
using ValveKeyValue;
using System.Security.Cryptography;
using System.ServiceModel.Configuration;
using HeliosPlus.GameLibraries.SteamAppInfoParser;
namespace HeliosPlus.GameLibraries
{
public class SteamGame
{
private static string _steamExe;
private static string _steamPath;
private static string _steamConfigVdfFile;
private static string _registrySteamKey = @"SOFTWARE\\Valve\\Steam";
private static string _registryAppsKey = $@"{_registrySteamKey}\\Apps";
private static SupportedGameLibrary _library = SupportedGameLibrary.Steam;
private string _gameRegistryKey;
private uint _steamGameId;
private string _steamGameName;
private string _steamGamePath;
private string _steamGameExe;
private Icon _steamGameIcon;
private static List<SteamGame> _allSteamGames;
private struct SteamAppInfo
{
public uint GameID;
public string GameName;
public List<string> GameExes;
public string GameInstallDir;
public string GameSteamIconPath;
}
static SteamGame()
{
ServicePointManager.ServerCertificateValidationCallback +=
(send, certificate, chain, sslPolicyErrors) => true;
}
public SteamGame(uint steamGameId, string steamGameName, string steamGamePath, string steamGameExe, Icon steamGameIcon)
{
_gameRegistryKey = $@"{_registryAppsKey}\\{steamGameId}";
_steamGameId = steamGameId;
_steamGameName = steamGameName;
_steamGamePath = steamGamePath;
_steamGameExe = steamGameExe;
_steamGameIcon = steamGameIcon;
}
public uint GameId { get => _steamGameId; }
public SupportedGameLibrary GameLibrary { get => SupportedGameLibrary.Steam; }
public Icon GameIcon { get => _steamGameIcon; }
public bool IsRunning
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree))
{
if ((int)key?.GetValue(@"Running", 0) == 1)
{
return true;
}
return false;
}
}
catch (SecurityException e)
{
if (e.Source != null)
Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
catch (IOException e)
{
// Extract some information from this exception, and then
// throw it to the parent method.
if (e.Source != null)
Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
}
}
public bool IsUpdating
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree))
{
if ((int)key?.GetValue(@"Updating", 0) == 1)
{
return true;
}
return false;
}
}
catch (SecurityException e)
{
if (e.Source != null)
Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
catch (IOException e)
{
// Extract some information from this exception, and then
// throw it to the parent method.
if (e.Source != null)
Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
}
}
public string GameName { get => _steamGameName; }
public static string SteamExe { get => _steamExe; }
public string GamePath { get => _steamGamePath; }
public static List<SteamGame> AllGames { get => _allSteamGames; }
public static bool SteamInstalled
{
get
{
if (!string.IsNullOrWhiteSpace(SteamGame._steamExe) && File.Exists(SteamGame._steamExe))
{
return true;
}
return false;
}
}
public static List<SteamGame> GetAllInstalledGames()
{
List<SteamGame> steamGameList = new List<SteamGame>();
_allSteamGames = steamGameList;
try
{
// Find the SteamExe location, and the SteamPath for later
using (var key = Registry.CurrentUser.OpenSubKey(_registrySteamKey, RegistryKeyPermissionCheck.ReadSubTree))
{
_steamExe = (string)key?.GetValue(@"SteamExe", string.Empty) ?? string.Empty;
_steamExe = _steamExe.Replace('/','\\');
_steamPath = (string)key?.GetValue(@"SteamPath", string.Empty) ?? string.Empty;
_steamPath = _steamPath.Replace('/', '\\');
}
if (_steamExe == string.Empty || !File.Exists(_steamExe))
{
// Steam isn't installed, so we return an empty list.
return steamGameList;
}
Icon _steamIcon = Icon.ExtractAssociatedIcon(_steamExe);
List<uint> steamAppIdsInstalled = new List<uint>();
// Now look for what games app id's are actually installed on this computer
using (RegistryKey steamAppsKey = Registry.CurrentUser.OpenSubKey(_registryAppsKey, RegistryKeyPermissionCheck.ReadSubTree))
{
if (steamAppsKey != null)
{
// Loop through the subKeys as they are the Steam Game IDs
foreach (string steamGameKeyName in steamAppsKey.GetSubKeyNames())
{
uint steamAppId = 0;
if (uint.TryParse(steamGameKeyName, out steamAppId))
{
string steamGameKeyFullName = $"{_registryAppsKey}\\{steamGameKeyName}";
using (RegistryKey steamGameKey = Registry.CurrentUser.OpenSubKey(steamGameKeyFullName, RegistryKeyPermissionCheck.ReadSubTree))
{
// If the Installed Value is set to 1, then the game is installed
// We want to keep track of that for later
if ((int)steamGameKey.GetValue(@"Installed", 0) == 1)
{
// Add this Steam App ID to the list we're keeping for later
steamAppIdsInstalled.Add(steamAppId);
}
}
}
}
}
}
// Now we parse the steam appinfo.vdf to get access to things like:
// - The game name
// - THe game installation dir
// - Sometimes the game icon
// - Sometimes the game executable name (from which we can get the icon)
Dictionary<uint, SteamAppInfo> steamAppInfo = new Dictionary<uint, SteamAppInfo>();
string appInfoVdfFile = Path.Combine(_steamPath, "appcache", "appinfo.vdf");
var newAppInfo = new AppInfo();
newAppInfo.Read(appInfoVdfFile);
Console.WriteLine($"{newAppInfo.Apps.Count} apps");
// Chec through all the apps we've extracted
foreach (var app in newAppInfo.Apps)
{
// We only care about the appIDs we have listed as actual games
// (The AppIds include all other DLC and Steam specific stuff too)
if ( steamAppIdsInstalled.Contains(app.AppID))
{
try
{
SteamAppInfo steamGameAppInfo = new SteamAppInfo();
steamGameAppInfo.GameID = app.AppID;
steamGameAppInfo.GameExes = new List<string>();
foreach (KVObject data in app.Data)
{
//Console.WriteLine($"App: {app.AppID} - Data.Name: {data.Name}");
if (data.Name == "common")
{
foreach (KVObject common in data.Children) {
//Console.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}");
if (common.Name == "name")
{
Console.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}");
steamGameAppInfo.GameName = common.Value.ToString();
}
else if (common.Name == "clienticon")
{
Console.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}");
steamGameAppInfo.GameSteamIconPath = Path.Combine(_steamPath, @"steam", @"games", String.Concat(common.Value, @".ico"));
}
else if (common.Name == "type")
{
Console.WriteLine($"App: {app.AppID} - Common {common.Name}: {common.Value}");
}
}
}
else if (data.Name == "config")
{
foreach (KVObject config in data.Children)
{
//Console.WriteLine($"App: {app.AppID} - Config {config.Name}: {config.Value}");
if (config.Name == "installdir")
{
Console.WriteLine($"App: {app.AppID} - Config {config.Name}: {config.Value}");
steamGameAppInfo.GameInstallDir = config.Value.ToString();
}
else if (config.Name == "launch")
{
foreach (KVObject launch in config.Children)
{
foreach (KVObject launch_num in launch.Children)
{
if (launch_num.Name == "executable")
{
Console.WriteLine($"App: {app.AppID} - Config - Launch {launch.Name} - {launch_num.Name}: {launch_num.Value}");
steamGameAppInfo.GameExes.Add(launch_num.Value.ToString());
}
}
}
}
}
}
}
steamAppInfo.Add(app.AppID, steamGameAppInfo);
}
catch (ArgumentException e)
{
//we just want to ignore it if we try to add it twice....
}
Console.WriteLine($"App: {app.AppID} - Token: {app.Token}");
}
}
// Now we access the config.vdf that lives in the Steam Config file, as that lists all
// the SteamLibraries. We need to find out where they areso we can interrogate them
_steamConfigVdfFile = Path.Combine(_steamPath, "config", "config.vdf");
string steamConfigVdfText = File.ReadAllText(_steamConfigVdfFile, Encoding.UTF8);
List<string> steamLibrariesPaths = new List<string>();
// Now we have to parse the config.vdf looking for the location of the SteamLibraries
// We look for lines similar to this: "BaseInstallFolder_1" "E:\\SteamLibrary"
// There may be multiple so we need to check the whole file
Regex steamLibrariesRegex = new Regex(@"""BaseInstallFolder_\d+""\s+""(.*)""", RegexOptions.IgnoreCase);
// Try to match all lines against the Regex.
Match steamLibrariesMatches = steamLibrariesRegex.Match(steamConfigVdfText);
// If at least one of them matched!
if (steamLibrariesMatches.Success)
{
// Loop throug the results and add to an array
for (int i = 1; i < steamLibrariesMatches.Groups.Count; i++)
{
string steamLibraryPath = Regex.Unescape(steamLibrariesMatches.Groups[i].Value);
Console.WriteLine($"Found steam library: {steamLibraryPath}");
steamLibrariesPaths.Add(steamLibraryPath);
}
}
// Now we go off and find the details for the games in each Steam Library
foreach (string steamLibraryPath in steamLibrariesPaths)
{
// Work out the path to the appmanifests for this steamLibrary
string steamLibraryAppManifestPath = Path.Combine(steamLibraryPath, @"steamapps");
// Get the names of the App Manifests for the games installed in this SteamLibrary
string[] steamLibraryAppManifestFilenames = Directory.GetFiles(steamLibraryAppManifestPath, "appmanifest_*.acf");
// Go through each app and extract it's details
foreach (string steamLibraryAppManifestFilename in steamLibraryAppManifestFilenames)
{
// Read in the contents of the file
string steamLibraryAppManifestText = File.ReadAllText(steamLibraryAppManifestFilename);
// Grab the appid from the file
Regex appidRegex = new Regex(@"""appid""\s+""(\d+)""", RegexOptions.IgnoreCase);
Match appidMatches = appidRegex.Match(steamLibraryAppManifestText);
if (appidMatches.Success)
{
uint steamGameId = 0;
if (uint.TryParse(appidMatches.Groups[1].Value, out steamGameId))
{
// Check if this game is one that was installed
if (steamAppInfo.ContainsKey(steamGameId)) {
// This game is an installed game! so we start to populate it with data!
string steamGameExe = "";
string steamGameName = steamAppInfo[steamGameId].GameName;
// Construct the full path to the game dir from the appInfo and libraryAppManifest data
string steamGameInstallDir = Path.Combine(steamLibraryPath, @"steamapps", @"common", steamAppInfo[steamGameId].GameInstallDir);
// Next, we need to get the Icons we want to use, and make sure it's the latest one.
Icon steamGameIcon = null;
// First of all, we attempt to use the Icon that Steam has cached, if it's available, as that will be updated to the latest
if (File.Exists(steamAppInfo[steamGameId].GameSteamIconPath))
{
steamGameIcon = Icon.ExtractAssociatedIcon(steamAppInfo[steamGameId].GameSteamIconPath);
}
// If there isn't an icon for us to use, then we need to extract one from the Game Executables
else if (steamAppInfo[steamGameId].GameExes.Count > 0)
{
foreach (string gameExe in steamAppInfo[steamGameId].GameExes)
{
steamGameExe = Path.Combine(steamGameInstallDir, gameExe);
// If the game executable exists, then we can proceed
if (File.Exists(steamGameExe))
{
// Now we need to get the Icon from the app if possible if it's not in the games folder
try
{
steamGameIcon = Icon.ExtractAssociatedIcon(steamGameExe);
break;
}
catch (ArgumentException e)
{
// We drop out here if the executable didn't have an Icon
// That's fine, let's just try the next one!
}
}
}
}
// The absolute worst case means we don't have an icon to use. SO we use the Steam one.
else
{
// And we have to make do with a Steam Icon
steamGameIcon = _steamIcon;
}
// And finally we try to populate the 'where', to see what gets run
// And so we can extract the process name
if (steamAppInfo[steamGameId].GameExes.Count > 0)
{
foreach (string gameExe in steamAppInfo[steamGameId].GameExes)
{
steamGameExe = Path.Combine(steamGameInstallDir, gameExe);
// If the game executable exists, then we can proceed
if (File.Exists(steamGameExe))
{
break;
}
}
}
// And we add the Game to the list of games we have!
steamGameList.Add(new SteamGame(steamGameId, steamGameName, steamGameInstallDir, steamGameExe, steamGameIcon));
}
}
}
}
}
}
catch (SecurityException e)
{
if (e.Source != null)
Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
catch (UnauthorizedAccessException e)
{
if (e.Source != null)
Console.WriteLine("UnauthorizedAccessException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
catch (ObjectDisposedException e)
{
if (e.Source != null)
Console.WriteLine("ObjectDisposedException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
catch (IOException e)
{
// Extract some information from this exception, and then
// throw it to the parent method.
if (e.Source != null)
Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
return steamGameList;
}
public override string ToString()
{
var name = _steamGameName;
if (string.IsNullOrWhiteSpace(name))
{
name = Language.Unknown;
}
if (IsRunning)
{
return name + " " + Language.Running;
}
if (IsUpdating)
{
return name + " " + Language.Updating;
}
/*if (IsInstalled)
{
return name + " " + Language.Installed;
}*/
return name + " " + Language.Not_Installed;
}
}
}

View File

@ -0,0 +1,539 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using HeliosPlus.Resources;
using HeliosPlus.Shared;
using HtmlAgilityPack;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace HeliosPlus.GameLibraries
{
public class UplayGame
{
private static string _uplayExe;
private static string _uplayPath;
private static string _uplayConfigVdfFile;
private static string _registryUplayKey = @"SOFTWARE\\Valve\\Uplay";
private static string _registryAppsKey = $@"{_registryUplayKey}\\Apps";
private static SupportedGameLibrary _library = SupportedGameLibrary.Uplay;
//private static string _iconCachePath;
private string _gameRegistryKey;
private uint _uplayGameId;
private string _uplayGameName;
private string _uplayGamePath;
private string _uplayGameExe;
private Icon _uplayGameIcon;
private static List<UplayGame> _allUplayGames;
static UplayGame()
{
ServicePointManager.ServerCertificateValidationCallback +=
(send, certificate, chain, sslPolicyErrors) => true;
}
public UplayGame(uint uplayGameId, string uplayGameName, string uplayGamePath, string uplayGameExe, Icon uplayGameIcon)
{
_gameRegistryKey = $@"{_registryAppsKey}\\{uplayGameId}";
_uplayGameId = uplayGameId;
_uplayGameName = uplayGameName;
//_iconCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
// Assembly.GetExecutingAssembly().GetName().Name, @"UplayIconCache");
_uplayGamePath = uplayGamePath;
_uplayGameExe = uplayGameExe;
_uplayGameIcon = uplayGameIcon;
}
public uint GameId { get => _uplayGameId; }
public SupportedGameLibrary GameLibrary { get => SupportedGameLibrary.Uplay; }
public Icon GameIcon { get => _uplayGameIcon; }
/* public static string GameIdCacheFilePath
{
get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Assembly.GetExecutingAssembly().GetName().Name, @"UplayGamesCache.json");
}
*/
public bool IsRunning
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree))
{
if ((int)key?.GetValue(@"Running", 0) == 1)
{
return true;
}
return false;
}
}
catch (SecurityException e)
{
if (e.Source != null)
Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
catch (IOException e)
{
// Extract some information from this exception, and then
// throw it to the parent method.
if (e.Source != null)
Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
}
}
public bool IsUpdating
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(_gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree))
{
if ((int)key?.GetValue(@"Updating", 0) == 1)
{
return true;
}
return false;
}
}
catch (SecurityException e)
{
if (e.Source != null)
Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
catch (IOException e)
{
// Extract some information from this exception, and then
// throw it to the parent method.
if (e.Source != null)
Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
}
}
public string GameName { get => _uplayGameName; }
public static string UplayExe { get => _uplayExe; }
public string GamePath { get => _uplayGamePath; }
public static List<UplayGame> AllGames { get => _allUplayGames; }
public static bool UplayInstalled
{
get
{
if (!string.IsNullOrWhiteSpace(UplayGame._uplayExe) && File.Exists(UplayGame._uplayExe))
{
return true;
}
return false;
}
}
/*public static List<GameLibraryAppDetails> GetAllGames()
{
lock (AllGamesLock)
{
if (_allGames == null)
{
_allGames = GetCachedGameIds()?.ToList();
}
}
// Update only once
if (!_allGamesUpdated)
{
if (_allGames?.Count > 0)
{
UpdateGamesFromWeb();
}
else
{
UpdateGamesFromWeb()?.Join();
}
}
return _allGames;
}*/
public static List<UplayGame> GetAllInstalledGames()
{
List<UplayGame> uplayGameList = new List<UplayGame>();
_allUplayGames = uplayGameList;
try
{
// Find the UplayExe location, and the UplayPath for later
using (var key = Registry.CurrentUser.OpenSubKey(_registryUplayKey, RegistryKeyPermissionCheck.ReadSubTree))
{
_uplayExe = (string)key?.GetValue(@"UplayExe", string.Empty) ?? string.Empty;
_uplayPath = (string)key?.GetValue(@"UplayPath", string.Empty) ?? string.Empty;
}
if (_uplayExe == string.Empty)
{
// Uplay isn't installed, so we return an empty list.
return uplayGameList;
}
List<uint> uplayAppIdsInstalled = new List<uint>();
// Now look for what games app id's are actually installed on this computer
using (RegistryKey uplayAppsKey = Registry.CurrentUser.OpenSubKey(_registryAppsKey, RegistryKeyPermissionCheck.ReadSubTree))
{
if (uplayAppsKey != null)
{
// Loop through the subKeys as they are the Uplay Game IDs
foreach (string uplayGameKeyName in uplayAppsKey.GetSubKeyNames())
{
uint uplayAppId = 0;
if (uint.TryParse(uplayGameKeyName, out uplayAppId))
{
using (RegistryKey uplayGameKey = Registry.CurrentUser.OpenSubKey(uplayGameKeyName, RegistryKeyPermissionCheck.ReadSubTree))
{
// If the Installed Value is set to 1, then the game is installed
// We want to keep track of that for later
if ((bool)uplayGameKey.GetValue(@"Installed", 0))
{
// Add this Uplay App ID to the list we're keeping for later
uplayAppIdsInstalled.Add(uplayAppId);
}
}
}
}
}
}
// Now we access the config.vdf that lives in the Uplay Config file, as that lists all
// the UplayLibraries. We need to find out where they areso we can interrogate them
_uplayConfigVdfFile = Path.Combine(_uplayPath, @"config", @"config.vdf");
string uplayConfigVdfText = File.ReadAllText(_uplayConfigVdfFile);
List<string> uplayLibrariesPaths = new List<string>();
// Now we have to parse the config.vdf looking for the location of the UplayLibraries
// We look for lines similar to this: "BaseInstallFolder_1" "E:\\UplayLibrary"
// There may be multiple so we need to check the whole file
Regex uplayLibrariesRegex = new Regex(@"""BaseInstallFolder_\d+""\s+""([^""])"")", RegexOptions.IgnoreCase);
// Try to match all lines against the Regex.
Match uplayLibrariesMatches = uplayLibrariesRegex.Match(uplayConfigVdfText);
// If at least one of them matched!
if (uplayLibrariesMatches.Success)
{
// Loop throug the results and add to an array
for (int i = 1; i <= uplayLibrariesMatches.Groups.Count; i++)
{
Console.WriteLine($"Found uplay library: {uplayLibrariesMatches.Groups[i].Value}");
uplayLibrariesPaths.Add(uplayLibrariesMatches.Groups[i].Value);
}
}
// Now we go off and find the details for the games in each Uplay Library
foreach (string uplayLibraryPath in uplayLibrariesPaths)
{
// Work out the path to the appmanifests for this uplayLibrary
string uplayLibraryAppManifestPath = Path.Combine(uplayLibraryPath, @"uplayapps");
// Get the names of the App Manifests for the games installed in this UplayLibrary
string[] uplayLibraryAppManifestFilenames = Directory.GetFiles(uplayLibraryAppManifestPath, "appmanifest_*.acf");
// Go through each app and extract it's details
foreach (string uplayLibraryAppManifestFilename in uplayLibraryAppManifestFilenames)
{
// Read in the contents of the file
string uplayLibraryAppManifestText = File.ReadAllText(uplayLibraryAppManifestFilename);
// Grab the appid from the file
Regex appidRegex = new Regex(@"""appid""\s+""(\d+)"")", RegexOptions.IgnoreCase);
Match appidMatches = appidRegex.Match(uplayLibraryAppManifestText);
if (appidMatches.Success)
{
uint uplayGameId = 0;
string uplayGameName = String.Empty;
if (uint.TryParse(appidMatches.Groups[1].Value, out uplayGameId))
{
// Check if this game is one that was installed
if (uplayAppIdsInstalled.Contains(uplayGameId))
{
// This game is an installed game! so we start to populate it with data!
// Grab the Uplay game name from the app manifeest file
Regex nameRegex = new Regex(@"""name""\s+""([^""])"")", RegexOptions.IgnoreCase);
Match nameMatches = nameRegex.Match(uplayLibraryAppManifestText);
if (nameMatches.Success)
{
uplayGameName = nameMatches.Groups[1].Value;
// We need to also get the installdir from the app manifeest file
Regex installDirRegex = new Regex(@"""installdir""\s+""([^""])"")", RegexOptions.IgnoreCase);
Match installDirMatches = installDirRegex.Match(uplayLibraryAppManifestText);
if (installDirMatches.Success)
{
// Construct the full path to the game dir
string uplayGameInstallDir = Path.Combine(uplayLibraryPath, @"uplayapps", @"common", installDirMatches.Groups[1].Value);
// Get the names of the *.config within the gamesdir. There is one per game, and it is the main game exe
// This is the one we want to get the icon from.
string[] uplayGameConfigs = Directory.GetFiles(uplayGameInstallDir, "*.config");
// Pick the first one, and use that (as there should only be one). Derive the exe name from it
//string uplayGameExe = Path.Combine(uplayGameInstallDir, uplayGameConfigs[0].Remove(uplayGameConfigs[0].LastIndexOf(".config")));
string uplayGameExe = Path.Combine(uplayGameInstallDir, Path.GetFileNameWithoutExtension(uplayGameConfigs[0]));
// Now we need to get the Icon
Icon uplayGameIcon = Icon.ExtractAssociatedIcon(uplayGameExe);
uplayGameList.Add(new UplayGame(uplayGameId, uplayGameName, uplayGameInstallDir, uplayGameExe, uplayGameIcon));
}
}
}
}
}
}
}
}
catch (SecurityException e)
{
if (e.Source != null)
Console.WriteLine("SecurityException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
catch (IOException e)
{
// Extract some information from this exception, and then
// throw it to the parent method.
if (e.Source != null)
Console.WriteLine("IOException source: {0} - Message: {1}", e.Source, e.Message);
throw;
}
return uplayGameList;
}
/* public static string GetAppName(uint appId)
{
return GetAllGames()?.FirstOrDefault(g => g.AppId == appId)?.Name;
}*/
/* private static void CacheGameIds(IEnumerable<GameLibraryAppDetails> gameIds)
{
try
{
var json = JsonConvert.SerializeObject(gameIds, Formatting.Indented);
if (!string.IsNullOrWhiteSpace(json))
{
var dir = Path.GetDirectoryName(GameIdCacheFilePath);
if (dir != null)
{
Directory.CreateDirectory(dir);
File.WriteAllText(GameIdCacheFilePath, json, Encoding.Unicode);
}
}
}
catch
{
// ignored
}
}*/
/*
private static GameLibraryAppDetails[] GetCachedGameIds()
{
try
{
if (File.Exists(GameIdCacheFilePath))
{
var json = File.ReadAllText(GameIdCacheFilePath, Encoding.Unicode);
if (!string.IsNullOrWhiteSpace(json))
{
return JsonConvert.DeserializeObject<GameLibraryAppDetails[]>(json);
}
}
}
catch
{
// ignored
}
return null;
}*/
/* private static Thread UpdateGamesFromWeb()
{
if (_allGamesUpdated)
{
return null;
}
_allGamesUpdated = true;
var thread = new Thread(() =>
{
try
{
var newGames = new List<GameLibraryAppDetails>();
using (var webClient = new WebClient())
{
webClient.Headers.Add(@"User-Agent",
@"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0");
webClient.Headers.Add(@"Accept", @"text/html,application/xhtml+xml,application/xml;");
var response = webClient.OpenRead(@"https://uplaydb.info/api/GetAppList/");
if (response != null)
{
using (response)
{
using (var reader = new StreamReader(response))
{
var content = reader.ReadToEnd();
if (!string.IsNullOrWhiteSpace(content))
{
dynamic appids = JsonConvert.DeserializeObject(content);
if (appids != null && appids.success == true)
{
foreach (var app in appids.data)
{
try
{
newGames.Add(new GameLibraryAppDetails(SupportedGameLibrary.Uplay, uint.Parse(app.Name), app.Value.Value));
}
catch
{
// ignored
}
}
}
}
reader.Close();
}
response.Close();
}
}
}
*//* if (newGames.Count > 0)
{
lock (AllGamesLock)
{
_allGames = newGames;
CacheGameIds(_allGames);
}
}*//*
}
catch
{
// ignored
}
});
thread.Start();
return thread;
}*/
public override string ToString()
{
var name = _uplayGameName;
if (string.IsNullOrWhiteSpace(name))
{
name = Language.Unknown;
}
if (IsRunning)
{
return name + " " + Language.Running;
}
if (IsUpdating)
{
return name + " " + Language.Updating;
}
/*if (IsInstalled)
{
return name + " " + Language.Installed;
}*/
return name + " " + Language.Not_Installed;
}
/* public Task<string> DownloadIcon()
{
return Task.Run(() =>
{
if (!Directory.Exists(IconCachePath))
{
try
{
Directory.CreateDirectory(IconCachePath);
}
catch
{
return null;
}
}
var localPath = Path.Combine(IconCachePath, GameId + ".ico");
if (File.Exists(localPath))
{
return localPath;
}
var iconUrl = new HtmlWeb().Load("https://uplaydb.info/app/" + GameId)
.DocumentNode.SelectNodes("//a[@href]")
.Select(node => node.Attributes["href"].Value)
.FirstOrDefault(attribute => attribute.EndsWith(".ico") && attribute.Contains("/" + GameId + "/"));
if (!string.IsNullOrWhiteSpace(iconUrl))
{
try
{
using (var client = new WebClient())
{
client.DownloadFile(iconUrl, localPath);
}
}
catch
{
return null;
}
}
return File.Exists(localPath) ? localPath : null;
});
}*/
}
}

View File

@ -68,6 +68,11 @@
<Reference Include="System.Xml.Serialization" />
</ItemGroup>
<ItemGroup>
<Compile Include="GameLibraries\SteamAppInfoParser\AppInfo.cs" />
<Compile Include="GameLibraries\SteamAppInfoParser\EUniverse.cs" />
<Compile Include="GameLibraries\SteamAppInfoParser\Package.cs" />
<Compile Include="GameLibraries\SteamAppInfoParser\PackageInfo.cs" />
<Compile Include="GameLibraries\SteamAppInfoParser\App.cs" />
<Compile Include="Validators.cs" />
<Compile Include="DisplayRepresentation.cs" />
<Compile Include="InterProcess\IPCClient.cs" />
@ -79,10 +84,8 @@
<DesignTime>True</DesignTime>
<DependentUpon>Language.resx</DependentUpon>
</Compile>
<Compile Include="Uplay\UplayAppIdNamePair.cs" />
<Compile Include="Uplay\UplayGame.cs" />
<Compile Include="Steam\SteamGame.cs" />
<Compile Include="Steam\SteamAppIdNamePair.cs" />
<Compile Include="GameLibraries\UplayGame.cs" />
<Compile Include="GameLibraries\SteamGame.cs" />
<Compile Include="UIForms\EditForm.cs">
<SubType>Form</SubType>
</Compile>
@ -180,6 +183,9 @@
<PackageReference Include="CircularProgressBar">
<Version>2.7.0.7</Version>
</PackageReference>
<PackageReference Include="Gameloop.Vdf">
<Version>0.5.0</Version>
</PackageReference>
<PackageReference Include="HtmlAgilityPack">
<Version>1.11.23</Version>
</PackageReference>
@ -192,6 +198,9 @@
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
<PackageReference Include="ValveKeyValue">
<Version>0.3.0.144</Version>
</PackageReference>
<PackageReference Include="WindowsDisplayAPI">
<Version>1.3.0.13</Version>
</PackageReference>
@ -199,6 +208,18 @@
<Version>1.6.0.4</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="Resources\Steam.ico" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Uplay.ico" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Origin.ico" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Epic.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -13,14 +13,12 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using HeliosPlus.InterProcess;
using HeliosPlus.Resources;
using HeliosPlus.Steam;
using HeliosPlus.GameLibraries;
using HeliosPlus.Shared;
using HeliosPlus.Uplay;
using HeliosPlus.UIForms;
using System.Net.NetworkInformation;
namespace HeliosPlus {
public enum SupportedGameLibrary
{
Unknown,
@ -28,10 +26,10 @@ namespace HeliosPlus {
Uplay
}
internal static class Program
{
internal static string ShortcutIconCachePath;
internal static Profile GetProfile(string profileName)
{
@ -158,6 +156,21 @@ namespace HeliosPlus {
private static int Main(string[] args)
{
// Figure out where the shortcut's will go
ShortcutIconCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Assembly.GetExecutingAssembly().GetName().Name, @"ShortcutIconCache");
// Create the Shortcut Icon Cache if it doesn't exist so that it's avilable for all the program
if (!Directory.Exists(ShortcutIconCachePath))
{
try
{
Directory.CreateDirectory(ShortcutIconCachePath);
}
catch
{
}
}
var app = new CommandLineApplication();
//app.Name = "HeliosDM+";
@ -549,6 +562,13 @@ namespace HeliosPlus {
private static void SwitchToSteamGame(Profile profile, string steamGameIdToRun, uint timeout, string steamGameArguments)
{
// Convert the steamGameIdToRun string to a uint for Steam Games
uint steamGameIdUint = 0;
if (!uint.TryParse(steamGameIdToRun, out steamGameIdUint))
{
throw new Exception("ERROR - Couldn't convert the string steamGameIdToRun parameter to steamGameIdUint in SwitchToSteamGame!");
}
var rollbackProfile = Profile.GetCurrent(string.Empty);
if (!profile.IsPossible)
@ -569,36 +589,30 @@ namespace HeliosPlus {
}
List<SteamGame> allSteamGames = SteamGame.GetAllInstalledGames();
var steamGame = new SteamGame(Convert.ToUInt32(steamGameIdToRun));
SteamGame steamGameToRun = null;
foreach (SteamGame steamGameToCheck in allSteamGames)
{
if (steamGameToCheck.GameId == steamGameIdUint)
{
steamGameToRun = steamGameToCheck;
break;
}
}
if (!SteamGame.SteamInstalled)
{
throw new Exception(Language.Steam_is_not_installed);
}
if (!File.Exists(SteamGame.SteamAddress))
{
throw new Exception(Language.Steam_executable_file_not_found);
}
if (!steamGame.IsInstalled)
{
throw new Exception(Language.Steam_game_is_not_installed);
}
if (!steamGame.IsOwned)
{
throw new Exception(Language.Steam_game_is_not_owned);
}
if (!GoProfile(profile))
{
throw new Exception(Language.Can_not_change_active_profile);
}
var address = $"steam://rungameid/{steamGame.AppId}";
var address = $"steam://rungameid/{steamGameToRun.GameId}";
if (!string.IsNullOrWhiteSpace(steamGameArguments))
{
@ -611,14 +625,14 @@ namespace HeliosPlus {
while (ticks < timeout * 1000)
{
if (steamGame.IsRunning)
if (steamGameToRun.IsRunning)
{
break;
}
Thread.Sleep(300);
if (!steamGame.IsUpdating)
if (!steamGameToRun.IsUpdating)
{
ticks += 300;
}
@ -635,7 +649,7 @@ namespace HeliosPlus {
Icon = Properties.Resources.Icon,
Text = string.Format(
Language.Waiting_for_the_0_to_terminate,
steamGame.Name),
steamGameToRun.GameName),
Visible = true
};
Application.DoEvents();
@ -646,11 +660,11 @@ namespace HeliosPlus {
}
// Wait for the game to exit
if (steamGame.IsRunning)
if (steamGameToRun.IsRunning)
{
while (true)
{
if (!steamGame.IsRunning)
if (!steamGameToRun.IsRunning)
{
break;
}
@ -703,14 +717,14 @@ namespace HeliosPlus {
var steamGame = new SteamGame(Convert.ToUInt32(uplayGameIdToRun));
/*var steamGame = new SteamGame(Convert.ToUInt32(uplayGameIdToRun));
if (!SteamGame.SteamInstalled)
{
throw new Exception(Language.Steam_is_not_installed);
}
if (!File.Exists(SteamGame.SteamAddress))
if (!File.Exists(SteamGame.SteamExe))
{
throw new Exception(Language.Steam_executable_file_not_found);
}
@ -720,11 +734,6 @@ namespace HeliosPlus {
throw new Exception(Language.Steam_game_is_not_installed);
}
if (!steamGame.IsOwned)
{
throw new Exception(Language.Steam_game_is_not_owned);
}
if (!GoProfile(profile))
{
throw new Exception(Language.Can_not_change_active_profile);
@ -806,7 +815,7 @@ namespace HeliosPlus {
{
throw new Exception(Language.Can_not_change_active_profile);
}
}
}*/
}

View File

@ -60,6 +60,16 @@ namespace HeliosPlus.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Epic {
get {
object obj = ResourceManager.GetObject("Epic", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
@ -71,12 +81,32 @@ namespace HeliosPlus.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Bitmap SteamIcon {
internal static System.Drawing.Icon Origin {
get {
object obj = ResourceManager.GetObject("SteamIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
object obj = ResourceManager.GetObject("Origin", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Steam {
get {
object obj = ResourceManager.GetObject("Steam", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Uplay {
get {
object obj = ResourceManager.GetObject("Uplay", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}

View File

@ -118,10 +118,19 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Epic" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Epic.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="SteamIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\SteamIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="Origin" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Origin.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Steam" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Steam.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Uplay" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Uplay.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -611,11 +611,11 @@ namespace HeliosPlus.Resources {
}
/// <summary>
/// Looks up a localized string similar to Shortcut place successfully..
/// Looks up a localized string similar to Shortcut placed successfully..
/// </summary>
internal static string Shortcut_place_successfully {
internal static string Shortcut_placed_successfully {
get {
return ResourceManager.GetString("Shortcut_place_successfully", resourceCulture);
return ResourceManager.GetString("Shortcut_placed_successfully", resourceCulture);
}
}

View File

@ -219,8 +219,8 @@
<data name="Switching_display_profile_to_profile" xml:space="preserve">
<value>Switching display profile to '{0}'.</value>
</data>
<data name="Shortcut_place_successfully" xml:space="preserve">
<value>Shortcut place successfully.</value>
<data name="Shortcut_placed_successfully" xml:space="preserve">
<value>Shortcut placed successfully.</value>
</data>
<data name="Shortcut" xml:space="preserve">
<value>Shortcut</value>

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View File

@ -1,18 +0,0 @@
namespace HeliosPlus.Steam
{
public class SteamAppIdNamePair
{
public SteamAppIdNamePair(uint appId, string name)
{
AppId = appId;
Name = name;
}
public SteamAppIdNamePair()
{
}
public uint AppId { get; set; }
public string Name { get; set; }
}
}

View File

@ -1,425 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HeliosPlus.Resources;
using HtmlAgilityPack;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace HeliosPlus.Steam
{
public class SteamGame
{
private static List<SteamAppIdNamePair> _allGames;
private static bool _allGamesUpdated;
private static readonly object AllGamesLock = new object();
private string _name;
static SteamGame()
{
ServicePointManager.ServerCertificateValidationCallback +=
(send, certificate, chain, sslPolicyErrors) => true;
}
public SteamGame(uint appId)
{
AppId = appId;
}
public uint AppId { get; }
public static string GameIdsPath
{
get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Assembly.GetExecutingAssembly().GetName().Name, @"SteamGames.json");
}
public static string IconCache
{
get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Assembly.GetExecutingAssembly().GetName().Name, @"SteamIconCache");
}
public bool IsInstalled
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryApp, RegistryKeyPermissionCheck.ReadSubTree))
{
return (int) (key?.GetValue(@"Installed", 0) ?? 0) > 0;
}
}
catch
{
return false;
}
}
}
public bool IsOwned
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryApp, RegistryKeyPermissionCheck.ReadSubTree))
{
return key != null;
}
}
catch
{
return false;
}
}
}
public bool IsRunning
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryApp, RegistryKeyPermissionCheck.ReadSubTree))
{
return (int) (key?.GetValue(@"Running", 0) ?? 0) > 0;
}
}
catch
{
return false;
}
}
}
public bool IsUpdating
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryApp, RegistryKeyPermissionCheck.ReadSubTree))
{
return (int) (key?.GetValue(@"Updating", 0) ?? 0) > 0;
}
}
catch
{
return false;
}
}
}
public string Name
{
get => _name ?? (_name = GetAppName(AppId));
}
private string RegistryApp
{
get => $@"{RegistryApps}\\{AppId}";
}
private static string RegistryApps
{
get => $@"{RegistrySteam}\\Apps";
}
private static string RegistrySteam
{
get => @"SOFTWARE\\Valve\\Steam";
}
public static string SteamAddress
{
get
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistrySteam, RegistryKeyPermissionCheck.ReadSubTree))
{
return (string) key?.GetValue(@"SteamExe", string.Empty) ?? string.Empty;
}
}
}
public static bool SteamInstalled
{
get => !string.IsNullOrWhiteSpace(SteamAddress);
}
public static List<SteamAppIdNamePair> GetAllGames()
{
lock (AllGamesLock)
{
if (_allGames == null)
{
_allGames = GetCachedGameIds()?.ToList();
}
}
// Update only once
if (!_allGamesUpdated)
{
if (_allGames?.Count > 0)
{
UpdateGamesFromWeb();
}
else
{
UpdateGamesFromWeb()?.Join();
}
}
return _allGames;
}
public static SteamGame[] GetAllOwnedGames()
{
var list = new List<SteamGame>();
try
{
using (
var subKey = Registry.CurrentUser.OpenSubKey(RegistryApps, RegistryKeyPermissionCheck.ReadSubTree))
{
if (subKey != null)
{
foreach (var keyName in subKey.GetSubKeyNames())
{
uint gameId;
if (uint.TryParse(keyName, out gameId))
{
list.Add(new SteamGame(gameId));
}
}
}
}
}
catch (Exception)
{
// ignored
}
return list.ToArray();
}
public static string GetAppName(uint appId)
{
return GetAllGames()?.FirstOrDefault(g => g.AppId == appId)?.Name;
}
private static void CacheGameIds(IEnumerable<SteamAppIdNamePair> gameIds)
{
try
{
var json = JsonConvert.SerializeObject(gameIds, Formatting.Indented);
if (!string.IsNullOrWhiteSpace(json))
{
var dir = Path.GetDirectoryName(GameIdsPath);
if (dir != null)
{
Directory.CreateDirectory(dir);
File.WriteAllText(GameIdsPath, json, Encoding.Unicode);
}
}
}
catch
{
// ignored
}
}
private static SteamAppIdNamePair[] GetCachedGameIds()
{
try
{
if (File.Exists(GameIdsPath))
{
var json = File.ReadAllText(GameIdsPath, Encoding.Unicode);
if (!string.IsNullOrWhiteSpace(json))
{
return JsonConvert.DeserializeObject<SteamAppIdNamePair[]>(json);
}
}
}
catch
{
// ignored
}
return null;
}
private static Thread UpdateGamesFromWeb()
{
if (_allGamesUpdated)
{
return null;
}
_allGamesUpdated = true;
var thread = new Thread(() =>
{
try
{
var newGames = new List<SteamAppIdNamePair>();
using (var webClient = new WebClient())
{
webClient.Headers.Add(@"User-Agent",
@"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0");
webClient.Headers.Add(@"Accept", @"text/html,application/xhtml+xml,application/xml;");
var response = webClient.OpenRead(@"https://steamdb.info/api/GetAppList/");
if (response != null)
{
using (response)
{
using (var reader = new StreamReader(response))
{
var content = reader.ReadToEnd();
if (!string.IsNullOrWhiteSpace(content))
{
dynamic appids = JsonConvert.DeserializeObject(content);
if (appids != null && appids.success == true)
{
foreach (var app in appids.data)
{
try
{
newGames.Add(new SteamAppIdNamePair(uint.Parse(app.Name),
app.Value.Value));
}
catch
{
// ignored
}
}
}
}
reader.Close();
}
response.Close();
}
}
}
if (newGames.Count > 0)
{
lock (AllGamesLock)
{
_allGames = newGames;
CacheGameIds(_allGames);
}
}
}
catch
{
// ignored
}
});
thread.Start();
return thread;
}
public override string ToString()
{
var name = Name;
if (string.IsNullOrWhiteSpace(name))
{
name = Language.Unknown;
}
if (IsRunning)
{
return name + " " + Language.Running;
}
if (IsUpdating)
{
return name + " " + Language.Updating;
}
if (IsInstalled)
{
return name + " " + Language.Installed;
}
if (IsOwned)
{
return name + " " + Language.Not_Installed;
}
return name + " " + Language.Not_Owned;
}
public Task<string> GetIcon()
{
return Task.Run(() =>
{
if (!Directory.Exists(IconCache))
{
try
{
Directory.CreateDirectory(IconCache);
}
catch
{
return null;
}
}
var localPath = Path.Combine(IconCache, AppId + ".ico");
if (File.Exists(localPath))
{
return localPath;
}
var iconUrl = new HtmlWeb().Load("https://steamdb.info/app/" + AppId)
.DocumentNode.SelectNodes("//a[@href]")
.Select(node => node.Attributes["href"].Value)
.FirstOrDefault(attribute => attribute.EndsWith(".ico") && attribute.Contains("/" + AppId + "/"));
if (!string.IsNullOrWhiteSpace(iconUrl))
{
try
{
using (var client = new WebClient())
{
client.DownloadFile(iconUrl, localPath);
}
}
catch
{
return null;
}
}
return File.Exists(localPath) ? localPath : null;
});
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@ -14,7 +15,8 @@ namespace HeliosPlus.UIForms
private const string GroupActive = "active";
private const string GroupCurrent = "current";
private const string GroupSaved = "saved";
private static Profile SelectedProfile;
private Profile SelectedProfile;
public MainForm()
{

View File

@ -9,13 +9,14 @@ using System.Runtime.InteropServices;
using System.Windows.Forms;
using HeliosPlus.Resources;
using HeliosPlus.Shared;
using HeliosPlus.Steam;
using HeliosPlus.GameLibraries;
using NvAPIWrapper.Native.GPU;
namespace HeliosPlus.UIForms
{
public partial class ShortcutForm : Form
{
public ShortcutForm()
{
InitializeComponent();
@ -26,13 +27,6 @@ namespace HeliosPlus.UIForms
Profile = profile;
}
public static string IconCache
{
get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Assembly.GetExecutingAssembly().GetName().Name, @"IconCache");
}
public string ProcessNameToMonitor
{
get
@ -223,12 +217,30 @@ namespace HeliosPlus.UIForms
try
{
// Try to set up some sensible suggestions for the Shortcut name
if (rb_switch_perm.Checked)
{
dialog_save.FileName = Profile.Name;
}
else
{
if (rb_standalone.Checked)
{
dialog_save.FileName = Path.GetFileNameWithoutExtension(ExecutableNameAndPath);
}
else
{
dialog_save.FileName = GameName;
}
}
// Show the Save Shortcut window
if (dialog_save.ShowDialog(this) == DialogResult.OK)
{
if (CreateShortcut(dialog_save.FileName))
{
MessageBox.Show(
Language.Shortcut_place_successfully,
Language.Shortcut_placed_successfully,
Language.Shortcut,
MessageBoxButtons.OK,
MessageBoxIcon.Information);
@ -256,30 +268,19 @@ namespace HeliosPlus.UIForms
// ReSharper disable once CyclomaticComplexity
private bool CreateShortcut(string fileName)
{
var programName = Path.GetFileNameWithoutExtension(txt_executable.Text);
var description = string.Empty;
var icon = string.Empty;
string programName = Path.GetFileNameWithoutExtension(txt_executable.Text);
string shortcutDescription = string.Empty;
Icon shortcutIcon = null;
string shortcutIconFileName = string.Empty;
var args = new List<string>
{
// Add the SwitchProfile command as the first argument to start to switch to another profile
$"{HeliosStartupAction.SwitchProfile}",
// Add the Profile Name as the second argument (use that rather than ID - though ID still will work!)
$"--profile \"{dv_profile.Profile.Name}\""
$"--profile \"{Profile.Name}\""
};
if (!Directory.Exists(IconCache))
{
try
{
Directory.CreateDirectory(IconCache);
}
catch
{
// ignored
}
}
// Only add the rest of the options if the temporary switch radio button is set
if (rb_switch_temp.Checked)
{
@ -287,52 +288,55 @@ namespace HeliosPlus.UIForms
if (rb_standalone.Checked)
{
// Doublecheck the Executable text field is filled in
if (string.IsNullOrWhiteSpace(txt_executable.Text))
if (string.IsNullOrWhiteSpace(ExecutableNameAndPath))
{
throw new Exception(Language.Executable_address_can_not_be_empty);
}
// Doublecheck the Executable text field is a path to a real file
if (!File.Exists(txt_executable.Text))
if (!File.Exists(ExecutableNameAndPath))
{
throw new Exception(Language.Executable_file_not_found);
}
// Add the executable command and the executable name to the shortcut arguments
args.Add($"execute \"{txt_executable.Text.Trim()}\"");
args.Add($"execute \"{ExecutableNameAndPath}\"");
// Check that the wait for executable radiobutton is on
if (rb_wait_executable.Checked)
{
// Doublecheck the process name has text in it
if (!string.IsNullOrWhiteSpace(txt_process_name.Text))
if (!string.IsNullOrWhiteSpace(ProcessNameToMonitor))
{
// Add the waitfor argument and the process name to the shortcut arguments
args.Add($"--waitfor \"{txt_process_name.Text.Trim()}\"");
args.Add($"--waitfor \"{ProcessNameToMonitor}\"");
}
}
// Add the timeout argument and the timeout duration in seconds to the shortcut arguments
args.Add($"--timeout {(int)nud_timeout_executable.Value}");
args.Add($"--timeout {ExecutableTimeout}");
if (cb_args_executable.Checked && !string.IsNullOrWhiteSpace(txt_args_executable.Text))
if (cb_args_executable.Checked)
{
args.Add($"--arguments \"{txt_args_executable.Text.Trim()}\"");
args.Add($"--arguments \"{ExecutableArguments}\"");
}
// Prepare text for the shortcut description field
description = string.Format(Language.Executing_application_with_profile, programName, Profile.Name);
shortcutDescription = string.Format(Language.Executing_application_with_profile, programName, Profile.Name);
// Work out the name of the shortcut we'll save.
shortcutIconFileName = Path.Combine(Program.ShortcutIconCachePath, String.Concat(Path.GetFileNameWithoutExtension(ExecutableNameAndPath), @".ico"));
// Grab an icon for the selected executable
try
{
icon = Path.Combine(IconCache, Guid.NewGuid() + ".ico");
new ProfileIcon(Profile).ToIconOverly(txt_executable.Text)
.Save(icon, MultiIconFormat.ICO);
// We'll first try to extract the Icon from the executable the user provided
shortcutIcon = Icon.ExtractAssociatedIcon(ExecutableNameAndPath);
}
catch (Exception)
{
icon = $"{txt_executable.Text.Trim()},0";
// but if that doesn't work, then we use our own one.
shortcutIcon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
}
}
// Only add the rest of the options if the temporary switch radio button is set
@ -341,47 +345,75 @@ namespace HeliosPlus.UIForms
{
// TODO need to make this work so at least one game library is installed
// i.e. if (!SteamGame.SteamInstalled && !UplayGame.UplayInstalled )
if (!SteamGame.SteamInstalled)
if (GameLibrary == SupportedGameLibrary.Steam)
{
throw new Exception(Language.Steam_is_not_installed);
if (!SteamGame.SteamInstalled)
{
throw new Exception(Language.Steam_is_not_installed);
}
List<SteamGame> allSteamGames = SteamGame.GetAllInstalledGames();
SteamGame steamGameToRun = null;
foreach (SteamGame steamGameToCheck in allSteamGames)
{
if (steamGameToCheck.GameId == GameAppId)
{
steamGameToRun = steamGameToCheck;
break;
}
}
shortcutIcon = steamGameToRun.GameIcon;
// Work out the name of the shortcut we'll save.
shortcutIconFileName = Path.Combine(Program.ShortcutIconCachePath, @"Uplay", String.Concat(GameAppId.ToString(), @".ico"));
args.Add($"--steam {GameAppId}");
}
else if (GameLibrary == SupportedGameLibrary.Uplay)
{
if (!UplayGame.UplayInstalled)
{
throw new Exception(Language.Steam_is_not_installed);
}
List<UplayGame> allUplayGames = UplayGame.GetAllInstalledGames();
UplayGame uplayGameToRun = null;
foreach (UplayGame uplayGameToCheck in allUplayGames)
{
if (uplayGameToCheck.GameId == GameAppId)
{
uplayGameToRun = uplayGameToCheck;
break;
}
}
shortcutIcon = uplayGameToRun.GameIcon;
// Work out the name of the shortcut we'll save.
shortcutIconFileName = Path.Combine(Program.ShortcutIconCachePath, @"Uplay", String.Concat(GameAppId.ToString(), @".ico"));
args.Add($"--uplay {GameAppId}");
}
// TODO - Add in Uplay game as well depending on which one was requested
// Add the Steam Game ID to the shortcut arguments
var steamGame = new SteamGame((uint) nud_game_appid.Value);
args.Add($"--steam {(int) nud_game_appid.Value}");
// Add the game timeout argument and the timeout duration in seconds to the shortcut arguments
args.Add($"--timeout {(int) nud_timeout_game.Value}");
args.Add($"--timeout {GameTimeout}");
if (cb_args_game.Checked && !string.IsNullOrWhiteSpace(txt_args_game.Text))
if (cb_args_game.Checked)
{
args.Add($"--arguments \"{txt_args_game.Text.Trim()}\"");
args.Add($"--arguments \"{GameArguments}\"");
}
// Prepare text for the shortcut description field
description = string.Format(Language.Executing_application_with_profile, steamGame.Name,
Profile.Name);
var steamIcon = steamGame.GetIcon().Result;
shortcutDescription = string.Format(Language.Executing_application_with_profile, GameName, Profile.Name);
// Grab an icon for the selected game
if (!string.IsNullOrWhiteSpace(steamIcon))
{
try
{
icon = Path.Combine(IconCache, Guid.NewGuid() + ".ico");
new ProfileIcon(Profile).ToIconOverly(steamIcon)
.Save(icon, MultiIconFormat.ICO);
}
catch (Exception)
{
icon = steamIcon;
}
}
else
{
icon = $"{SteamGame.SteamAddress},0";
}
}
}
@ -389,23 +421,30 @@ namespace HeliosPlus.UIForms
else
{
// Prepare text for the shortcut description field
description = string.Format(Language.Switching_display_profile_to_profile, Profile.Name);
shortcutDescription = string.Format(Language.Switching_display_profile_to_profile, Profile.Name);
// Work out the name of the shortcut we'll save.
shortcutIconFileName = Path.Combine(Program.ShortcutIconCachePath, String.Concat(Profile.Name, @".ico"));
// Grab an icon for the selected profile
try
{
icon = Path.Combine(IconCache, Guid.NewGuid() + ".ico");
new ProfileIcon(Profile).ToIcon().Save(icon, MultiIconFormat.ICO);
MultiIcon profileMultiIcon = new ProfileIcon(Profile).ToIcon();
shortcutIcon = profileMultiIcon[0].Icon;
}
catch
{
icon = string.Empty;
// but if that doesn't work, then we use our own one.
shortcutIcon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
}
}
// TODO - Make this semi-automatic if its a game to be launched. Maybe still show
// a save prompt, but suggest the name of the game extracted from the game launcher
// or the executable itself.
// Save the Icon to the Shortcut Icon cache as the Shortcut will need to refer to it.
if (!File.Exists(shortcutIconFileName))
{
using (StreamWriter fs = File.AppendText(shortcutIconFileName))
shortcutIcon.Save(fs.BaseStream);
}
// Now we are ready to create a shortcut based on the filename the user gave us
fileName = Path.ChangeExtension(fileName, @"lnk");
@ -433,14 +472,11 @@ namespace HeliosPlus.UIForms
{
shortcut.TargetPath = Application.ExecutablePath;
shortcut.Arguments = string.Join(" ", args);
shortcut.Description = description;
shortcut.Description = shortcutDescription;
shortcut.WorkingDirectory = Path.GetDirectoryName(Application.ExecutablePath) ??
string.Empty;
if (!string.IsNullOrWhiteSpace(icon))
{
shortcut.IconLocation = icon;
}
shortcut.IconLocation = shortcutIconFileName;
shortcut.Save();
}
@ -524,20 +560,6 @@ namespace HeliosPlus.UIForms
g_temporary.Enabled = true;
}
/*g_temporary.Enabled = rb_switch_temp.Checked;
p_standalone.Enabled = rb_standalone.Checked;
txt_process_name.Enabled = cb_process.Checked;
nud_timeout.Enabled = cb_process.Checked;
p_game.Enabled = rb_launcher.Checked;
txt_args_executable.Enabled = cb_args.Checked;
if (rb_launcher.Checked)
{
nud_steamappid_ValueChanged(rb_launcher, e);
}*/
}
private void label1_Click(object sender, EventArgs e)
@ -606,30 +628,12 @@ namespace HeliosPlus.UIForms
// Set the Profile name
lbl_profile.Text = $"Selected Profile: {dv_profile.Profile?.Name ?? Language.None}";
// Start finding the games and loading the tree_games
foreach (var game in SteamGame.GetAllOwnedGames().OrderByDescending(game => game.IsInstalled).ThenBy(game => game.Name))
// Start finding the games and loading the Games ListView
List<SteamGame> allSteamGames = SteamGame.GetAllInstalledGames();
foreach (var game in allSteamGames.OrderBy(game => game.GameName))
{
var iconAddress = await game.GetIcon();
if (!string.IsNullOrWhiteSpace(iconAddress))
{
try
{
using (var fileReader = File.OpenRead(iconAddress))
{
var icon = new Icon(fileReader, il_games.ImageSize);
il_games.Images.Add(icon);
}
}
catch
{
il_games.Images.Add(Properties.Resources.SteamIcon);
}
}
else
{
il_games.Images.Add(Properties.Resources.SteamIcon);
}
//var iconAddress = await game.GetIcon();
il_games.Images.Add(game.GameIcon);
if (!Visible)
{
@ -638,7 +642,7 @@ namespace HeliosPlus.UIForms
lv_games.Items.Add(new ListViewItem
{
Text = game.Name,
Text = game.GameName,
Tag = game,
ImageIndex = il_games.Images.Count - 1
});

View File

@ -3,7 +3,7 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using HeliosPlus.Steam;
using HeliosPlus.GameLibraries;
namespace HeliosPlus.UIForms
{
@ -40,31 +40,9 @@ namespace HeliosPlus.UIForms
private async void SteamGamesForm_Load(object sender, EventArgs e)
{
foreach (
var game in
SteamGame.GetAllOwnedGames().OrderByDescending(game => game.IsInstalled).ThenBy(game => game.Name))
foreach (var game in SteamGame.GetAllInstalledGames().OrderByDescending(game => game.GameName))
{
var iconAddress = await game.GetIcon();
if (!string.IsNullOrWhiteSpace(iconAddress))
{
try
{
using (var fileReader = File.OpenRead(iconAddress))
{
var icon = new Icon(fileReader, il_games.ImageSize);
il_games.Images.Add(icon);
}
}
catch
{
il_games.Images.Add(Properties.Resources.SteamIcon);
}
}
else
{
il_games.Images.Add(Properties.Resources.SteamIcon);
}
il_games.Images.Add(game.GameIcon);
if (!Visible)
{
@ -73,7 +51,7 @@ namespace HeliosPlus.UIForms
lv_games.Items.Add(new ListViewItem
{
Text = game.Name,
Text = game.GameName,
Tag = game,
ImageIndex = il_games.Images.Count - 1
});

View File

@ -1,18 +0,0 @@
namespace HeliosPlus.Uplay
{
public class UplayAppIdNamePair
{
public UplayAppIdNamePair(uint appId, string name)
{
AppId = appId;
Name = name;
}
public UplayAppIdNamePair()
{
}
public uint AppId { get; set; }
public string Name { get; set; }
}
}

View File

@ -1,425 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HeliosPlus.Resources;
using HtmlAgilityPack;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace HeliosPlus.Uplay
{
public class UplayGame
{
private static List<UplayAppIdNamePair> _allGames;
private static bool _allGamesUpdated;
private static readonly object AllGamesLock = new object();
private string _name;
static UplayGame()
{
ServicePointManager.ServerCertificateValidationCallback +=
(send, certificate, chain, sslPolicyErrors) => true;
}
public UplayGame(uint appId)
{
AppId = appId;
}
public uint AppId { get; }
public static string GameIdsPath
{
get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Assembly.GetExecutingAssembly().GetName().Name, @"SteamGames.json");
}
public static string IconCache
{
get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Assembly.GetExecutingAssembly().GetName().Name, @"SteamIconCache");
}
public bool IsInstalled
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryApp, RegistryKeyPermissionCheck.ReadSubTree))
{
return (int) (key?.GetValue(@"Installed", 0) ?? 0) > 0;
}
}
catch
{
return false;
}
}
}
public bool IsOwned
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryApp, RegistryKeyPermissionCheck.ReadSubTree))
{
return key != null;
}
}
catch
{
return false;
}
}
}
public bool IsRunning
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryApp, RegistryKeyPermissionCheck.ReadSubTree))
{
return (int) (key?.GetValue(@"Running", 0) ?? 0) > 0;
}
}
catch
{
return false;
}
}
}
public bool IsUpdating
{
get
{
try
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryApp, RegistryKeyPermissionCheck.ReadSubTree))
{
return (int) (key?.GetValue(@"Updating", 0) ?? 0) > 0;
}
}
catch
{
return false;
}
}
}
public string Name
{
get => _name ?? (_name = GetAppName(AppId));
}
private string RegistryApp
{
get => $@"{RegistryApps}\\{AppId}";
}
private static string RegistryApps
{
get => $@"{RegistryUplay}\\Apps";
}
private static string RegistryUplay
{
get => @"SOFTWARE\\Valve\\Steam";
}
public static string UplayAddress
{
get
{
using (
var key = Registry.CurrentUser.OpenSubKey(RegistryUplay, RegistryKeyPermissionCheck.ReadSubTree))
{
return (string) key?.GetValue(@"SteamExe", string.Empty) ?? string.Empty;
}
}
}
public static bool UplayInstalled
{
get => !string.IsNullOrWhiteSpace(UplayAddress);
}
public static List<UplayAppIdNamePair> GetAllGames()
{
lock (AllGamesLock)
{
if (_allGames == null)
{
_allGames = GetCachedGameIds()?.ToList();
}
}
// Update only once
if (!_allGamesUpdated)
{
if (_allGames?.Count > 0)
{
UpdateGamesFromWeb();
}
else
{
UpdateGamesFromWeb()?.Join();
}
}
return _allGames;
}
public static UplayGame[] GetAllOwnedGames()
{
var list = new List<UplayGame>();
try
{
using (
var subKey = Registry.CurrentUser.OpenSubKey(RegistryApps, RegistryKeyPermissionCheck.ReadSubTree))
{
if (subKey != null)
{
foreach (var keyName in subKey.GetSubKeyNames())
{
uint gameId;
if (uint.TryParse(keyName, out gameId))
{
list.Add(new UplayGame(gameId));
}
}
}
}
}
catch (Exception)
{
// ignored
}
return list.ToArray();
}
public static string GetAppName(uint appId)
{
return GetAllGames()?.FirstOrDefault(g => g.AppId == appId)?.Name;
}
private static void CacheGameIds(IEnumerable<UplayAppIdNamePair> gameIds)
{
try
{
var json = JsonConvert.SerializeObject(gameIds, Formatting.Indented);
if (!string.IsNullOrWhiteSpace(json))
{
var dir = Path.GetDirectoryName(GameIdsPath);
if (dir != null)
{
Directory.CreateDirectory(dir);
File.WriteAllText(GameIdsPath, json, Encoding.Unicode);
}
}
}
catch
{
// ignored
}
}
private static UplayAppIdNamePair[] GetCachedGameIds()
{
try
{
if (File.Exists(GameIdsPath))
{
var json = File.ReadAllText(GameIdsPath, Encoding.Unicode);
if (!string.IsNullOrWhiteSpace(json))
{
return JsonConvert.DeserializeObject<UplayAppIdNamePair[]>(json);
}
}
}
catch
{
// ignored
}
return null;
}
private static Thread UpdateGamesFromWeb()
{
if (_allGamesUpdated)
{
return null;
}
_allGamesUpdated = true;
var thread = new Thread(() =>
{
try
{
var newGames = new List<UplayAppIdNamePair>();
using (var webClient = new WebClient())
{
webClient.Headers.Add(@"User-Agent",
@"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0");
webClient.Headers.Add(@"Accept", @"text/html,application/xhtml+xml,application/xml;");
var response = webClient.OpenRead(@"https://steamdb.info/api/GetAppList/");
if (response != null)
{
using (response)
{
using (var reader = new StreamReader(response))
{
var content = reader.ReadToEnd();
if (!string.IsNullOrWhiteSpace(content))
{
dynamic appids = JsonConvert.DeserializeObject(content);
if (appids != null && appids.success == true)
{
foreach (var app in appids.data)
{
try
{
newGames.Add(new UplayAppIdNamePair(uint.Parse(app.Name),
app.Value.Value));
}
catch
{
// ignored
}
}
}
}
reader.Close();
}
response.Close();
}
}
}
if (newGames.Count > 0)
{
lock (AllGamesLock)
{
_allGames = newGames;
CacheGameIds(_allGames);
}
}
}
catch
{
// ignored
}
});
thread.Start();
return thread;
}
public override string ToString()
{
var name = Name;
if (string.IsNullOrWhiteSpace(name))
{
name = Language.Unknown;
}
if (IsRunning)
{
return name + " " + Language.Running;
}
if (IsUpdating)
{
return name + " " + Language.Updating;
}
if (IsInstalled)
{
return name + " " + Language.Installed;
}
if (IsOwned)
{
return name + " " + Language.Not_Installed;
}
return name + " " + Language.Not_Owned;
}
public Task<string> GetIcon()
{
return Task.Run(() =>
{
if (!Directory.Exists(IconCache))
{
try
{
Directory.CreateDirectory(IconCache);
}
catch
{
return null;
}
}
var localPath = Path.Combine(IconCache, AppId + ".ico");
if (File.Exists(localPath))
{
return localPath;
}
var iconUrl = new HtmlWeb().Load("https://steamdb.info/app/" + AppId)
.DocumentNode.SelectNodes("//a[@href]")
.Select(node => node.Attributes["href"].Value)
.FirstOrDefault(attribute => attribute.EndsWith(".ico") && attribute.Contains("/" + AppId + "/"));
if (!string.IsNullOrWhiteSpace(iconUrl))
{
try
{
using (var client = new WebClient())
{
client.DownloadFile(iconUrl, localPath);
}
}
catch
{
return null;
}
}
return File.Exists(localPath) ? localPath : null;
});
}
}
}

View File

@ -8,7 +8,7 @@ using McMaster.Extensions.CommandLineUtils;
using McMaster.Extensions.CommandLineUtils.Validation;
using System.ComponentModel.DataAnnotations;
using HeliosPlus.Shared;
using HeliosPlus.Steam;
using HeliosPlus.GameLibraries;
namespace HeliosPlus
{

View File

@ -1,29 +1,22 @@
# Helios Display Management
[![](https://img.shields.io/github/license/falahati/HeliosDisplayManagement.svg?style=flat-square)](https://github.com/falahati/HeliosDisplayManagement/blob/master/LICENSE)
[![](https://img.shields.io/github/commit-activity/y/falahati/HeliosDisplayManagement.svg?style=flat-square)](https://github.com/falahati/HeliosDisplayManagement/commits/master)
[![](https://img.shields.io/github/issues/falahati/HeliosDisplayManagement.svg?style=flat-square)](https://github.com/falahati/HeliosDisplayManagement/issues)
# HeliosPlus
[![](https://img.shields.io/github/license/temacdonald/HeliosDisplayManagement.svg?style=flat-square)](https://github.com/temacdonald/HeliosDisplayManagement/blob/master/LICENSE)
[![](https://img.shields.io/github/commit-activity/y/temacdonald/HeliosDisplayManagement.svg?style=flat-square)](https://github.com/temacdonald/HeliosDisplayManagement/commits/master)
[![](https://img.shields.io/github/issues/temacdonald/HeliosDisplayManagement.svg?style=flat-square)](https://github.com/temacdonald/HeliosDisplayManagement/issues)
An open source tool for display profile management. Readily switch between display profiles & settings.
An open source tool for automatically changing your display settings from a Windows Shortcut. Allows you to temporarily change your display settings while you play a game.
**This program is still in development and currently in the pre-release stage; expect missing features, incomplete features and bugs**
**This program is heavily based on the amazing HeliosDisplayManagement created by Soroush Falahati!**
<div style="text-align:center"><img src="READMEAssets/Preview.png"/></div>
## Download
[![](https://img.shields.io/github/downloads/falahati/HeliosDisplayManagement/total.svg?style=flat-square)](https://github.com/falahati/HeliosDisplayManagement/releases)
[![](https://img.shields.io/github/tag-date/falahati/HeliosDisplayManagement.svg?label=version&style=flat-square)](https://github.com/falahati/HeliosDisplayManagement/releases)
[![](https://img.shields.io/github/downloads/temacdonald/HeliosDisplayManagement/total.svg?style=flat-square)](https://github.com/temacdonald/HeliosDisplayManagement/releases)
[![](https://img.shields.io/github/tag-date/temacdonald/HeliosDisplayManagement.svg?label=version&style=flat-square)](https://github.com/temacdonald/HeliosDisplayManagement/releases)
The latest version of this application is available for download via the [release](https://github.com/falahati/HeliosDisplayManagement/releases) page.
The latest version of this application is available for download via the [release](https://github.com/temacdonald/HeliosDisplayManagement/releases) page.
## Donation
Donations assist development and are greatly appreciated; also always remember that [every coffee counts!](https://media.makeameme.org/created/one-simply-does-i9k8kx.jpg) :)
[![](https://img.shields.io/badge/crypto-CoinPayments-8a00a3.svg?style=flat-square)](https://www.coinpayments.net/index.php?cmd=_donate&reset=1&merchant=820707aded07845511b841f9c4c335cd&item_name=Donate&currency=USD&amountf=20.00000000&allow_amount=1&want_shipping=0&allow_extra=1)
[![](https://img.shields.io/badge/shetab-ZarinPal-8a00a3.svg?style=flat-square)](https://zarinp.al/@falahati)
**--OR--**
You can always donate your time by contributing to the project or by introducing it to others.
No need to donate! I am doing this work to scratch a programming itch I've had for a while. It's pretty fun to take something carefully crafted by another developer and extend it with a lot of other awesome features. Thanks to Soroush for making HeliosDisplayMangement and licensing it under GPL2 so I could make the improvements I was longing for!
## What it does
@ -31,7 +24,7 @@ Provides an overview of saved profiles for easy editing and switching, as well a
Please read through the README for features (current and planned) and issues you may encounter while using the program.
Feel free to report missing features or bugs using the project [issue tracker](https://github.com/falahati/HeliosDisplayManagement/issues).
Feel free to report missing features or bugs using the project [issue tracker](https://github.com/temacdonald/HeliosDisplayManagement/issues).
## Current features
@ -119,7 +112,7 @@ Feel free to report missing features or bugs using the project [issue tracker](h
### Switch via Command Line
**Command:**
`HeliosDisplayManagement.exe {arguments}`
`HeliosPlus.exe {arguments}`
**Arguments:**
@ -144,14 +137,16 @@ Feel free to report missing features or bugs using the project [issue tracker](h
## Related Projects
- [**WindowsDisplayAPI**](https://github.com/falahati/WindowsDisplayAPI/): WindowsDisplayAPI is a .Net wrapper for Windows Display and Windows CCD APIs
- [**WindowsDisplayAPI**](https://github.com/temacdonald/WindowsDisplayAPI/): WindowsDisplayAPI is a .Net wrapper for Windows Display and Windows CCD APIs
- [**EDIDParser**](https://github.com/falahati/EDIDParser/): EDIDParser is a library allowing all .Net developers to parse and to extract information from raw EDID binary data. (Extended Display Identification Data)
- [**EDIDParser**](https://github.com/temacdonald/EDIDParser/): EDIDParser is a library allowing all .Net developers to parse and to extract information from raw EDID binary data. (Extended Display Identification Data)
- [**NvAPIWrapper**](https://github.com/falahati/NvAPIWrapper/): NvAPIWrapper is a .Net wrapper for NVIDIA public API, capable of managing all aspects of a display setup using NVIDIA GPUs
- [**NvAPIWrapper**](https://github.com/temacdonald/NvAPIWrapper/): NvAPIWrapper is a .Net wrapper for NVIDIA public API, capable of managing all aspects of a display setup using NVIDIA GPUs
## License
Copyright (C) 2019 Soroush Falahati
Copyright © Terry MacDonald 2020-2021
Original HelioDisplayManagement copyright © Soroush Falahati 2017-2020
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -171,4 +166,5 @@ with this program; if not, write to the Free Software Foundation, Inc.,
## Credits
Thanks for the work and the time that all of our contributors put into making this a better project. Following is a short list, containing the name of some of these people:
* Original HelioDisplayManagement project created by the amazing Soroush Falahati
* Readme file created by @timegrinder