mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
[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:
parent
a9bb295d1f
commit
8f41b94427
@ -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; }
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
495
HeliosDisplayManagement/GameLibraries/SteamGame.cs
Normal file
495
HeliosDisplayManagement/GameLibraries/SteamGame.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
539
HeliosDisplayManagement/GameLibraries/UplayGame.cs
Normal file
539
HeliosDisplayManagement/GameLibraries/UplayGame.cs
Normal 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;
|
||||
});
|
||||
}*/
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
BIN
HeliosDisplayManagement/Resources/Epic.ico
Normal file
BIN
HeliosDisplayManagement/Resources/Epic.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
BIN
HeliosDisplayManagement/Resources/Origin.ico
Normal file
BIN
HeliosDisplayManagement/Resources/Origin.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 293 KiB |
BIN
HeliosDisplayManagement/Resources/Steam.ico
Normal file
BIN
HeliosDisplayManagement/Resources/Steam.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 292 KiB |
BIN
HeliosDisplayManagement/Resources/Uplay.ico
Normal file
BIN
HeliosDisplayManagement/Resources/Uplay.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 155 KiB |
@ -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; }
|
||||
}
|
||||
}
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
42
README.md
42
README.md
@ -1,29 +1,22 @@
|
||||
# Helios Display Management
|
||||
[](https://github.com/falahati/HeliosDisplayManagement/blob/master/LICENSE)
|
||||
[](https://github.com/falahati/HeliosDisplayManagement/commits/master)
|
||||
[](https://github.com/falahati/HeliosDisplayManagement/issues)
|
||||
# HeliosPlus
|
||||
[](https://github.com/temacdonald/HeliosDisplayManagement/blob/master/LICENSE)
|
||||
[](https://github.com/temacdonald/HeliosDisplayManagement/commits/master)
|
||||
[](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://github.com/falahati/HeliosDisplayManagement/releases)
|
||||
[](https://github.com/falahati/HeliosDisplayManagement/releases)
|
||||
[](https://github.com/temacdonald/HeliosDisplayManagement/releases)
|
||||
[](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://www.coinpayments.net/index.php?cmd=_donate&reset=1&merchant=820707aded07845511b841f9c4c335cd&item_name=Donate¤cy=USD&amountf=20.00000000&allow_amount=1&want_shipping=0&allow_extra=1)
|
||||
[](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
|
||||
|
Loading…
Reference in New Issue
Block a user