diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index bd149f0..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "SteamStorefrontAPI"] - path = SteamStorefrontAPI - url = https://git.jeddunk.xyz/jeddunk/SteamStorefrontAPI.git diff --git a/GoldbergGUI.Core/GoldbergGUI.Core.csproj b/GoldbergGUI.Core/GoldbergGUI.Core.csproj index 72f32a9..79fab83 100644 --- a/GoldbergGUI.Core/GoldbergGUI.Core.csproj +++ b/GoldbergGUI.Core/GoldbergGUI.Core.csproj @@ -1,28 +1,27 @@  - - net8.0 - 0.3.0 - Jeddunk - AnyCPU;x86;x64 - + + netcoreapp3.1 + 0.1.0 + 0.1.0 + Jeddunk + - - - - - - - - + + + + + + + - - - ..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_32\PresentationCore\v4.0_4.0.0.0__31bf3856ad364e35\PresentationCore.dll - - - ..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll - - + + + ..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_32\PresentationCore\v4.0_4.0.0.0__31bf3856ad364e35\PresentationCore.dll + + + ..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll + + diff --git a/GoldbergGUI.Core/Models/GoldbergModel.cs b/GoldbergGUI.Core/Models/GoldbergModel.cs index d6a40fe..58e0f89 100644 --- a/GoldbergGUI.Core/Models/GoldbergModel.cs +++ b/GoldbergGUI.Core/Models/GoldbergModel.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -// ReSharper disable ClassNeverInstantiated.Global -// ReSharper disable UnusedMember.Global namespace GoldbergGUI.Core.Models { @@ -34,22 +32,22 @@ namespace GoldbergGUI.Core.Models /// /// List of DLC /// - public List DlcList { get; set; } - - public List Depots { get; set; } - + public List DlcList { get; set; } + + public List Depots { get; set; } + public List SubscribedGroups { get; set; } - - //public List AppPaths { get; set; } - + + public List AppPaths { get; set; } + public List Achievements { get; set; } - + public List Items { get; set; } - + public List Leaderboards { get; set; } - + public List Stats { get; set; } - + // Add controller setting here! /// /// Set offline mode. @@ -63,28 +61,24 @@ namespace GoldbergGUI.Core.Models /// Disable overlay (experimental only). /// public bool DisableOverlay { get; set; } - + public GoldbergGlobalConfiguration OverwrittenGlobalConfiguration { get; set; } } - public class DlcApp : SteamApp + public class Depot { - public DlcApp() { } - - public DlcApp(SteamApp steamApp) - { - AppId = steamApp.AppId; - Name = steamApp.Name; - ComparableName = steamApp.ComparableName; - AppType = steamApp.AppType; - LastModified = steamApp.LastModified; - PriceChangeNumber = steamApp.PriceChangeNumber; - } - /// - /// Path to DLC (relative to Steam API DLL) (optional) + /// ID of Depot. /// - public string AppPath { get; set; } + public int DepotId { get; set; } + /// + /// Name of Depot. + /// + public string Name { get; set; } + /// + /// Associated DLC App ID, can be null (e.g. if Depot is for base game). + /// + public int DlcAppId { get; set; } } public class Group @@ -103,46 +97,51 @@ namespace GoldbergGUI.Core.Models public int AppId { get; set; } } + public class AppPath + { + public int AppId { get; set; } + public string Path { get; set; } + } + public class Achievement { /// /// Achievement description. /// [JsonPropertyName("description")] - public string Description { get; set; } + public string Description { get; set; } /// - /// Human readable name, as shown on webpage, game library, overlay, etc. + /// Human readable name, as shown on webpage, game libary, overlay, etc. /// [JsonPropertyName("displayName")] - public string DisplayName { get; set; } + public string DisplayName { get; set; } /// /// Is achievement hidden? 0 = false, else true. /// [JsonPropertyName("hidden")] - public int Hidden { get; set; } - + public int Hidden { get; set; } + /// /// Path to icon when unlocked (colored). /// [JsonPropertyName("icon")] - public string Icon { get; set; } + public string Icon { get; set; } /// /// Path to icon when locked (grayed out). /// - // ReSharper disable once StringLiteralTypo [JsonPropertyName("icongray")] - public string IconGray { get; set; } + public string IconGray { get; set; } /// /// Internal name. /// [JsonPropertyName("name")] - public string Name { get; set; } + public string Name { get; set; } } - + public class Item { [JsonPropertyName("Timestamp")] @@ -201,10 +200,9 @@ namespace GoldbergGUI.Core.Models // [JsonConverter(typeof(FluffyParseStringConverter))] public long DropMaxPerWindow { get; set; } - // ReSharper disable once StringLiteralTypo [JsonPropertyName("workshopid")] // [JsonConverter(typeof(FluffyParseStringConverter))] - public long WorkshopId { get; set; } + public long Workshopid { get; set; } [JsonPropertyName("tw_unique_to_own")] // [JsonConverter(typeof(PurpleParseStringConverter))] diff --git a/GoldbergGUI.Core/Models/SteamAppModel.cs b/GoldbergGUI.Core/Models/SteamAppModel.cs index 4329b75..6fbf277 100644 --- a/GoldbergGUI.Core/Models/SteamAppModel.cs +++ b/GoldbergGUI.Core/Models/SteamAppModel.cs @@ -1,6 +1,7 @@ -using SQLite; using System.Collections.Generic; using System.Text.Json.Serialization; +using System.Text.RegularExpressions; +using GoldbergGUI.Core.Utils; // ReSharper disable UnusedMember.Global // ReSharper disable ClassNeverInstantiated.Global @@ -10,41 +11,44 @@ using System.Text.Json.Serialization; // ReSharper disable InconsistentNaming namespace GoldbergGUI.Core.Models { - [Table("steamapp")] public class SteamApp { - [JsonPropertyName("appid")] - [Column("appid")] - [PrimaryKey] - public int AppId { get; set; } + private string _name; + private string _comparableName; + [JsonPropertyName("appid")] public int AppId { get; set; } /// /// Name of Steam app /// [JsonPropertyName("name")] - [Column("name")] - public string Name { get; set; } - - [Column("comparable_name")] - public string ComparableName { get; set; } + public string Name + { + get => _name; + set + { + _name = value; + _comparableName = Regex.Replace(value, Misc.AlphaNumOnlyRegex, "").ToLower(); + } + } + + /// + /// Trimmed and cleaned name of Steam app, used for comparisons. + /// + public bool CompareName(string value) => _comparableName.Equals(value); /// /// App type (Game, DLC, ...) /// - [Column("type")] - public string AppType { get; set; } + public AppType type { get; set; } public override string ToString() { return $"{AppId}={Name}"; } - [JsonPropertyName("last_modified")] - [Ignore] - public long LastModified { get; set; } + [JsonPropertyName("last_modified")] public long LastModified { get; set; } [JsonPropertyName("price_change_number")] - [Ignore] public long PriceChangeNumber { get; set; } } @@ -72,4 +76,19 @@ namespace GoldbergGUI.Core.Models { [JsonPropertyName("response")] public override AppList AppList { get; set; } } + + public class AppType + { + private AppType(string value) => Value = value; + + public string Value { get; } + + public static AppType Game { get; } = new AppType("game"); + public static AppType DLC { get; } = new AppType("dlc"); + public static AppType Music { get; } = new AppType("music"); + public static AppType Demo { get; } = new AppType("demo"); + public static AppType Ad { get; } = new AppType("advertising"); + public static AppType Mod { get; } = new AppType("mod"); + public static AppType Video { get; } = new AppType("video"); + } } \ No newline at end of file diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index 6b7bcfa..7ae2c3c 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -1,6 +1,3 @@ -using GoldbergGUI.Core.Models; -using GoldbergGUI.Core.Utils; -using MvvmCross.Logging; using System; using System.Collections.Generic; using System.IO; @@ -10,6 +7,9 @@ using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; +using GoldbergGUI.Core.Models; +using GoldbergGUI.Core.Utils; +using MvvmCross.Logging; namespace GoldbergGUI.Core.Services { @@ -23,13 +23,16 @@ namespace GoldbergGUI.Core.Services public Task Save(string path, GoldbergConfiguration configuration); public Task GetGlobalSettings(); public Task SetGlobalSettings(GoldbergGlobalConfiguration configuration); + public bool GoldbergApplied(string path); + + // public Task Download(); + // public Task Extract(string archivePath); public Task GenerateInterfacesFile(string filePath); public List Languages(); } // ReSharper disable once UnusedType.Global - // ReSharper disable once ClassNeverInstantiated.Global public class GoldbergService : IGoldbergService { private IMvxLog _log; @@ -208,8 +211,7 @@ namespace GoldbergGUI.Core.Services { _log.Info("Reading configuration..."); var appId = -1; - var achievementList = new List(); - var dlcList = new List(); + var dlcList = new List(); var steamAppidTxt = Path.Combine(path, "steam_appid.txt"); if (File.Exists(steamAppidTxt)) { @@ -222,21 +224,7 @@ namespace GoldbergGUI.Core.Services _log.Info(@"""steam_appid.txt"" missing! Skipping..."); } - var achievementJson = Path.Combine(path, "steam_settings", "achievements.json"); - if (File.Exists(achievementJson)) - { - _log.Info("Getting achievements..."); - var json = await File.ReadAllTextAsync(achievementJson) - .ConfigureAwait(false); - achievementList = System.Text.Json.JsonSerializer.Deserialize>(json); - } - else - { - _log.Info(@"""steam_settings/achievements.json"" missing! Skipping..."); - } - var dlcTxt = Path.Combine(path, "steam_settings", "DLC.txt"); - var appPathTxt = Path.Combine(path, "steam_settings", "app_paths.txt"); if (File.Exists(dlcTxt)) { _log.Info("Getting DLCs..."); @@ -247,27 +235,12 @@ namespace GoldbergGUI.Core.Services { var match = expression.Match(line); if (match.Success) - dlcList.Add(new DlcApp() + dlcList.Add(new SteamApp { AppId = Convert.ToInt32(match.Groups["id"].Value), Name = match.Groups["name"].Value }); } - - // ReSharper disable once InvertIf - if (File.Exists(appPathTxt)) - { - var appPathAllLinesAsync = await File.ReadAllLinesAsync(appPathTxt).ConfigureAwait(false); - var appPathExpression = new Regex(@"(?.*) *= *(?.*)"); - foreach (var line in appPathAllLinesAsync) - { - var match = appPathExpression.Match(line); - if (!match.Success) continue; - var i = dlcList.FindIndex(x => - x.AppId.Equals(Convert.ToInt32(match.Groups["id"].Value))); - dlcList[i].AppPath = match.Groups["appPath"].Value; - } - } } else { @@ -277,7 +250,6 @@ namespace GoldbergGUI.Core.Services return new GoldbergConfiguration { AppId = appId, - Achievements = achievementList, DlcList = dlcList, Offline = File.Exists(Path.Combine(path, "steam_settings", "offline.txt")), DisableNetworking = File.Exists(Path.Combine(path, "steam_settings", "disable_networking.txt")), @@ -305,7 +277,6 @@ namespace GoldbergGUI.Core.Services { CopyDllFiles(path, x64Name); } - _log.Info("DLL setup finished!"); // Create steam_settings folder if missing _log.Info("Saving settings..."); @@ -318,135 +289,50 @@ namespace GoldbergGUI.Core.Services await File.WriteAllTextAsync(Path.Combine(path, "steam_appid.txt"), c.AppId.ToString()) .ConfigureAwait(false); - // Achievements + Images - if (c.Achievements.Count > 0) - { - _log.Info("Downloading images..."); - var imagePath = Path.Combine(path, "steam_settings", "images"); - Directory.CreateDirectory(imagePath); - - foreach (var achievement in c.Achievements) - { - await DownloadImageAsync(imagePath, achievement.Icon); - await DownloadImageAsync(imagePath, achievement.IconGray); - - // Update achievement list to point to local images instead - achievement.Icon = $"images/{Path.GetFileName(achievement.Icon)}"; - achievement.IconGray = $"images/{Path.GetFileName(achievement.IconGray)}"; - } - - _log.Info("Saving achievements..."); - - var achievementJson = System.Text.Json.JsonSerializer.Serialize( - c.Achievements, - new System.Text.Json.JsonSerializerOptions - { - Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = true - }); - await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "achievements.json"), achievementJson) - .ConfigureAwait(false); - - _log.Info("Finished saving achievements."); - } - else - { - _log.Info("No achievements set! Removing achievement files..."); - var imagePath = Path.Combine(path, "steam_settings", "images"); - if (Directory.Exists(imagePath)) - { - Directory.Delete(imagePath); - } - var achievementPath = Path.Combine(path, "steam_settings", "achievements"); - if (File.Exists(achievementPath)) - { - File.Delete(achievementPath); - } - _log.Info("Removed achievement files."); - } - - // DLC + App path + // DLC if (c.DlcList.Count > 0) { - _log.Info("Saving DLC settings..."); - var dlcContent = ""; - //var depotContent = ""; - var appPathContent = ""; - c.DlcList.ForEach(x => - { - dlcContent += $"{x}\n"; - //depotContent += $"{x.DepotId}\n"; - if (!string.IsNullOrEmpty(x.AppPath)) - appPathContent += $"{x.AppId}={x.AppPath}\n"; - }); - await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "DLC.txt"), dlcContent) + var dlcString = ""; + c.DlcList.ForEach(x => dlcString += $"{x}\n"); + await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "DLC.txt"), dlcString) .ConfigureAwait(false); - - /*if (!string.IsNullOrEmpty(depotContent)) - { - await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "depots.txt"), depotContent) - .ConfigureAwait(false); - }*/ - - - if (!string.IsNullOrEmpty(appPathContent)) - { - await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "app_paths.txt"), appPathContent) - .ConfigureAwait(false); - } - else - { - if (File.Exists(Path.Combine(path, "steam_settings", "app_paths.txt"))) - File.Delete(Path.Combine(path, "steam_settings", "app_paths.txt")); - } - _log.Info("Saved DLC settings."); } else { - _log.Info("No DLC set! Removing DLC configuration files..."); if (File.Exists(Path.Combine(path, "steam_settings", "DLC.txt"))) File.Delete(Path.Combine(path, "steam_settings", "DLC.txt")); - if (File.Exists(Path.Combine(path, "steam_settings", "app_paths.txt"))) - File.Delete(Path.Combine(path, "steam_settings", "app_paths.txt")); - _log.Info("Removed DLC configuration files."); } // Offline if (c.Offline) { - _log.Info("Create offline.txt"); await File.Create(Path.Combine(path, "steam_settings", "offline.txt")).DisposeAsync() .ConfigureAwait(false); } else { - _log.Info("Delete offline.txt if it exists"); File.Delete(Path.Combine(path, "steam_settings", "offline.txt")); } // Disable Networking if (c.DisableNetworking) { - _log.Info("Create disable_networking.txt"); await File.Create(Path.Combine(path, "steam_settings", "disable_networking.txt")).DisposeAsync() .ConfigureAwait(false); } else { - _log.Info("Delete disable_networking.txt if it exists"); File.Delete(Path.Combine(path, "steam_settings", "disable_networking.txt")); } // Disable Overlay if (c.DisableOverlay) { - _log.Info("Create disable_overlay.txt"); await File.Create(Path.Combine(path, "steam_settings", "disable_overlay.txt")).DisposeAsync() .ConfigureAwait(false); } else { - _log.Info("Delete disable_overlay.txt if it exists"); File.Delete(Path.Combine(path, "steam_settings", "disable_overlay.txt")); } } @@ -455,21 +341,17 @@ namespace GoldbergGUI.Core.Services { var steamApiDll = Path.Combine(path, $"{name}.dll"); var originalDll = Path.Combine(path, $"{name}_o.dll"); - var guiBackup = Path.Combine(path, $".{name}.dll.GOLDBERGGUIBACKUP"); + var guiBackup = Path.Combine(path, $"{name}.dll.GOLDBERGGUIBACKUP"); var goldbergDll = Path.Combine(_goldbergPath, $"{name}.dll"); if (!File.Exists(originalDll)) - { - _log.Info("Back up original Steam API DLL..."); File.Move(steamApiDll, originalDll); - } else { File.Move(steamApiDll, guiBackup, true); File.SetAttributes(guiBackup, FileAttributes.Hidden); } - _log.Info("Copy Goldberg DLL to target path..."); File.Copy(goldbergDll, steamApiDll); } @@ -533,7 +415,6 @@ namespace GoldbergGUI.Core.Services var contentLength = headResponse.Content.Headers.ContentLength; await client.GetFileAsync(downloadUrl, fileStream).ContinueWith(async t => { - // ReSharper disable once AccessToDisposedClosure await fileStream.DisposeAsync().ConfigureAwait(false); var fileLength = new FileInfo(_goldbergZipPath).Length; // Environment.Exit(128); @@ -683,22 +564,5 @@ namespace GoldbergGUI.Core.Services return success; } - - private async Task DownloadImageAsync(string imageFolder, string imageUrl) - { - var fileName = Path.GetFileName(imageUrl); - var targetPath = Path.Combine(imageFolder, fileName); - if (File.Exists(targetPath)) - { - return; - } - else if (imageUrl.StartsWith("images/")) - { - _log.Warn($"Previously downloaded image '{imageUrl}' is now missing!"); - } - - var wc = new System.Net.WebClient(); - await wc.DownloadFileTaskAsync(new Uri(imageUrl, UriKind.Absolute), targetPath); - } } } \ No newline at end of file diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index 74c369b..b3d77e2 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -1,18 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading.Tasks; using AngleSharp.Dom; using AngleSharp.Html.Parser; using GoldbergGUI.Core.Models; using GoldbergGUI.Core.Utils; using MvvmCross.Logging; using NinjaNye.SearchExtensions; -using SQLite; using SteamStorefrontAPI; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Text.Json; -using System.Text.RegularExpressions; -using System.Threading.Tasks; namespace GoldbergGUI.Core.Services { @@ -20,21 +21,23 @@ namespace GoldbergGUI.Core.Services public interface ISteamService { public Task Initialize(IMvxLog log); - public Task> GetListOfAppsByName(string name); - public Task GetAppByName(string name); - public Task GetAppById(int appid); - public Task> GetListOfAchievements(SteamApp steamApp); - public Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb); + public IEnumerable GetListOfAppsByName(string name); + public SteamApp GetAppByName(string name); + public SteamApp GetAppById(int appid); + public Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb); } class SteamCache { + public string Filename { get; } public string SteamUri { get; } public Type ApiVersion { get; } - public string SteamAppType { get; } + public AppType SteamAppType { get; } + public HashSet Cache { get; set; } = new HashSet(); - public SteamCache(string uri, Type apiVersion, string steamAppType) + public SteamCache(string filename, string uri, Type apiVersion, AppType steamAppType) { + Filename = filename; SteamUri = uri; ApiVersion = apiVersion; SteamAppType = steamAppType; @@ -46,30 +49,32 @@ namespace GoldbergGUI.Core.Services public class SteamService : ISteamService { // ReSharper disable StringLiteralTypo - private readonly Dictionary _caches = - new Dictionary + private readonly Dictionary _caches = + new Dictionary { { - AppTypeGame, + AppType.Game, new SteamCache( + "steamapps_games.json", "https://api.steampowered.com/IStoreService/GetAppList/v1/" + "?max_results=50000" + "&include_games=1" + "&key=" + Secrets.SteamWebApiKey(), typeof(SteamAppsV1), - AppTypeGame + AppType.Game ) }, { - AppTypeDlc, + AppType.DLC, new SteamCache( + "steamapps_dlc.json", "https://api.steampowered.com/IStoreService/GetAppList/v1/" + "?max_results=50000" + "&include_games=0" + "&include_dlc=1" + "&key=" + Secrets.SteamWebApiKey(), typeof(SteamAppsV1), - AppTypeDlc + AppType.DLC ) } }; @@ -79,152 +84,141 @@ namespace GoldbergGUI.Core.Services private const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/87.0.4280.88 Safari/537.36"; - private const string AppTypeGame = "game"; - private const string AppTypeDlc = "dlc"; - private const string Database = "steamapps.cache"; - private const string GameSchemaUrl = "https://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2/"; private IMvxLog _log; - private SQLiteAsyncConnection _db; - public async Task Initialize(IMvxLog log) { + //var (path, uri, jsonType, appType) = _caches[0]; static SteamApps DeserializeSteamApps(Type type, string cacheString) { - return type == typeof(SteamAppsV2) - ? (SteamApps)JsonSerializer.Deserialize(cacheString) - : JsonSerializer.Deserialize(cacheString); + if (type == typeof(SteamAppsV1)) + return JsonSerializer.Deserialize(cacheString); + else if (type == typeof(SteamAppsV2)) + return JsonSerializer.Deserialize(cacheString); + return null; } - _log = log; - _db = new SQLiteAsyncConnection(Database); - //_db.CreateTable(); - await _db.CreateTableAsync() - //.ContinueWith(x => _log.Debug("Table success!")) - .ConfigureAwait(false); - - var countAsync = await _db.Table().CountAsync().ConfigureAwait(false); - if (DateTime.Now.Subtract(File.GetLastWriteTimeUtc(Database)).TotalDays >= 1 || countAsync == 0) + foreach (var (k, c) in _caches) { - foreach (var (appType, steamCache) in _caches) + _log = log; + _log.Info($"Updating cache ({k.Value})..."); + var updateNeeded = + DateTime.Now.Subtract(File.GetLastWriteTimeUtc(c.Filename)).TotalDays >= 1; + SteamApps steamApps; + try { - _log.Info($"Updating cache ({appType})..."); - bool haveMoreResults; - long lastAppId = 0; - var client = new HttpClient(); - var cacheRaw = new HashSet(); - do - { - var response = lastAppId > 0 - ? await client.GetAsync($"{steamCache.SteamUri}&last_appid={lastAppId}") - .ConfigureAwait(false) - : await client.GetAsync(steamCache.SteamUri).ConfigureAwait(false); - var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - var steamApps = DeserializeSteamApps(steamCache.ApiVersion, responseBody); - foreach (var appListApp in steamApps.AppList.Apps) cacheRaw.Add(appListApp); - haveMoreResults = steamApps.AppList.HaveMoreResults; - lastAppId = steamApps.AppList.LastAppid; - } while (haveMoreResults); + var temp = await GetCache(updateNeeded, c.SteamUri, c.Filename) + .ConfigureAwait(false); + steamApps = DeserializeSteamApps(c.ApiVersion, temp); + } + catch (JsonException) + { + _log.Error("Local cache broken, forcing update..."); + var temp = await GetCache(true, c.SteamUri, c.Filename).ConfigureAwait(false); + steamApps = DeserializeSteamApps(c.ApiVersion, temp); + } + try + { + var cacheRaw = new HashSet(steamApps.AppList.Apps); var cache = new HashSet(); foreach (var steamApp in cacheRaw) { - steamApp.AppType = steamCache.SteamAppType; - steamApp.ComparableName = PrepareStringToCompare(steamApp.Name); + steamApp.type = c.SteamAppType; cache.Add(steamApp); } - await _db.InsertAllAsync(cache, "OR IGNORE").ConfigureAwait(false); + c.Cache = cache; + + _log.Info("Loaded cache into memory!"); + } + catch (NullReferenceException e) + { + Console.WriteLine(e); + throw; } } } - public async Task> GetListOfAppsByName(string name) + private async Task GetCache(bool updateNeeded, string steamUri, string cachePath) { - var query = await _db.Table() - .Where(x => x.AppType == AppTypeGame).ToListAsync().ConfigureAwait(false); - var listOfAppsByName = query.Search(x => x.Name) + string cacheString; + if (updateNeeded) + { + _log.Info("Getting content from API..."); + var client = new HttpClient(); + var response = await client.GetAsync(steamUri).ConfigureAwait(false); + var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + _log.Info("Got content from API successfully. Writing to file..."); + await File.WriteAllTextAsync(cachePath, responseBody, Encoding.UTF8).ConfigureAwait(false); + + _log.Info("Cache written to file successfully."); + cacheString = responseBody; + } + else + { + _log.Info("Cache already up to date!"); + cacheString = await File.ReadAllTextAsync(cachePath).ConfigureAwait(false); + } + + return cacheString; + } + + public IEnumerable GetListOfAppsByName(string name) + { + var listOfAppsByName = _caches[AppType.Game].Cache.Search(x => x.Name) .SetCulture(StringComparison.OrdinalIgnoreCase) .ContainingAll(name.Split(' ')); return listOfAppsByName; } - public async Task GetAppByName(string name) + public SteamApp GetAppByName(string name) { _log.Info($"Trying to get app {name}"); - var comparableName = PrepareStringToCompare(name); - var app = await _db.Table() - .FirstOrDefaultAsync(x => x.AppType == AppTypeGame && x.ComparableName.Equals(comparableName)) - .ConfigureAwait(false); + var comparableName = Regex.Replace(name, Misc.AlphaNumOnlyRegex, "").ToLower(); + var app = _caches[AppType.Game].Cache.FirstOrDefault(x => x.CompareName(comparableName)); if (app != null) _log.Info($"Successfully got app {app}"); return app; } - public async Task GetAppById(int appid) + public SteamApp GetAppById(int appid) { _log.Info($"Trying to get app with ID {appid}"); - var app = await _db.Table().Where(x => x.AppType == AppTypeGame) - .FirstOrDefaultAsync(x => x.AppId.Equals(appid)).ConfigureAwait(false); + var app = _caches[AppType.Game].Cache.FirstOrDefault(x => x.AppId.Equals(appid)); if (app != null) _log.Info($"Successfully got app {app}"); return app; } - public async Task> GetListOfAchievements(SteamApp steamApp) + public async Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb) { - var achievementList = new List(); - if (steamApp == null) - { - return achievementList; - } - - _log.Info($"Getting achievements for App {steamApp}"); - - var client = new HttpClient(); - client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent); - var apiUrl = $"{GameSchemaUrl}?key={Secrets.SteamWebApiKey()}&appid={steamApp.AppId}&l=en"; - - var response = await client.GetAsync(apiUrl); - var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - - var jsonResponse = JsonDocument.Parse(responseBody); - var achievementData = jsonResponse.RootElement.GetProperty("game") - .GetProperty("availableGameStats") - .GetProperty("achievements"); - - achievementList = JsonSerializer.Deserialize>(achievementData.GetRawText()); - return achievementList; - } - - public async Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb) - { - var dlcList = new List(); + var dlcList = new List(); if (steamApp != null) { _log.Info($"Get DLC for App {steamApp}"); var task = AppDetails.GetAsync(steamApp.AppId); var steamAppDetails = await task.ConfigureAwait(true); - if (steamAppDetails.Type == AppTypeGame) + if (steamAppDetails.Type == AppType.Game.Value) { - steamAppDetails.DLC.ForEach(async x => + steamAppDetails.DLC.ForEach(x => { - var result = await _db.Table().Where(z => z.AppType == AppTypeDlc) - .FirstOrDefaultAsync(y => y.AppId.Equals(x)).ConfigureAwait(true) - ?? new SteamApp() { AppId = x, Name = $"Unknown DLC {x}", ComparableName = $"unknownDlc{x}", AppType = AppTypeDlc }; - dlcList.Add(new DlcApp(result)); - _log.Debug($"{result.AppId}={result.Name}"); + var result = _caches[AppType.DLC].Cache.FirstOrDefault(y => y.AppId.Equals(x)) + ?? new SteamApp {AppId = x, Name = $"Unknown DLC {x}"}; + dlcList.Add(result); }); + dlcList.ForEach(x => _log.Debug($"{x.AppId}={x.Name}")); _log.Info("Got DLC successfully..."); // Get DLC from SteamDB - // Get Cloudflare cookie (not implemented) + // Get Cloudflare cookie // Scrape and parse HTML page // Add missing to DLC list // Return current list if we don't intend to use SteamDB if (!useSteamDb) return dlcList; - + try { var steamDbUri = new Uri($"https://steamdb.info/app/{steamApp.AppId}/dlc/"); @@ -257,7 +251,7 @@ namespace GoldbergGUI.Core.Services var dlcName = query3 != null ? query3[1].Text().Replace("\n", "").Trim() : $"Unknown DLC {dlcId}"; - var dlcApp = new DlcApp { AppId = Convert.ToInt32(dlcId), Name = dlcName }; + var dlcApp = new SteamApp {AppId = Convert.ToInt32(dlcId), Name = dlcName}; var i = dlcList.FindIndex(x => x.AppId.Equals(dlcApp.AppId)); if (i > -1) { @@ -295,10 +289,5 @@ namespace GoldbergGUI.Core.Services return dlcList; } - - private static string PrepareStringToCompare(string name) - { - return Regex.Replace(name, Misc.AlphaNumOnlyRegex, "").ToLower(); - } } } \ No newline at end of file diff --git a/GoldbergGUI.Core/Utils/CustomMvxAppStart.cs b/GoldbergGUI.Core/Utils/CustomMvxAppStart.cs index 8fa735e..d5fcc3a 100644 --- a/GoldbergGUI.Core/Utils/CustomMvxAppStart.cs +++ b/GoldbergGUI.Core/Utils/CustomMvxAppStart.cs @@ -1,7 +1,8 @@ +using System; +using System.Threading.Tasks; using MvvmCross.Exceptions; using MvvmCross.Navigation; using MvvmCross.ViewModels; -using System.Threading.Tasks; namespace GoldbergGUI.Core.Utils { diff --git a/GoldbergGUI.Core/Utils/Misc.cs b/GoldbergGUI.Core/Utils/Misc.cs index 9e315d3..6bf7df2 100644 --- a/GoldbergGUI.Core/Utils/Misc.cs +++ b/GoldbergGUI.Core/Utils/Misc.cs @@ -7,15 +7,15 @@ namespace GoldbergGUI.Core.Utils public class GlobalHelp { - public static string Header => + public static string Header => "Information\n"; - public static string TextPreLink => + public static string TextPreLink => "Usually these settings are saved under"; public static string Link => "%APPDATA%\\Goldberg SteamEmu Saves\\settings"; - public static string TextPostLink => + public static string TextPostLink => ", which makes these " + "available for every game that uses the Goldberg Emulator. However, if you want to set specific settings " + "for certain games (e.g. different language), you can remove the \"Global\" checkmark next to the option " + diff --git a/GoldbergGUI.Core/ViewModels/MainViewModel.cs b/GoldbergGUI.Core/ViewModels/MainViewModel.cs index 54580c7..ef36c1d 100644 --- a/GoldbergGUI.Core/ViewModels/MainViewModel.cs +++ b/GoldbergGUI.Core/ViewModels/MainViewModel.cs @@ -1,12 +1,4 @@ -using GoldbergGUI.Core.Models; -using GoldbergGUI.Core.Services; -using GoldbergGUI.Core.Utils; -using Microsoft.Win32; -using MvvmCross.Commands; -using MvvmCross.Logging; -using MvvmCross.Navigation; -using MvvmCross.ViewModels; -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; @@ -17,6 +9,14 @@ using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; +using GoldbergGUI.Core.Models; +using GoldbergGUI.Core.Services; +using GoldbergGUI.Core.Utils; +using Microsoft.Win32; +using MvvmCross.Commands; +using MvvmCross.Logging; +using MvvmCross.Navigation; +using MvvmCross.ViewModels; namespace GoldbergGUI.Core.ViewModels { @@ -30,8 +30,7 @@ namespace GoldbergGUI.Core.ViewModels private int _appId; //private SteamApp _currentGame; - private ObservableCollection _achievements; - private ObservableCollection _dlcs; + private ObservableCollection _dlcs; private string _accountName; private long _steamId; private bool _offline; @@ -131,7 +130,7 @@ namespace GoldbergGUI.Core.ViewModels } // ReSharper disable once InconsistentNaming - public ObservableCollection DLCs + public ObservableCollection DLCs { get => _dlcs; set @@ -143,16 +142,6 @@ namespace GoldbergGUI.Core.ViewModels } } - public ObservableCollection Achievements - { - get => _achievements; - set - { - _achievements = value; - RaisePropertyChanged(() => Achievements); - } - } - public string AccountName { get => _accountName; @@ -275,7 +264,7 @@ namespace GoldbergGUI.Core.ViewModels public static string AboutVersionText => FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; - + public static GlobalHelp G => new GlobalHelp(); // COMMANDS // @@ -332,7 +321,7 @@ namespace GoldbergGUI.Core.ViewModels MainWindowEnabled = false; StatusText = "Trying to find AppID..."; - var appByName = await _steam.GetAppByName(_gameName).ConfigureAwait(false); + var appByName = _steam.GetAppByName(_gameName); if (appByName != null) { GameName = appByName.Name; @@ -340,7 +329,7 @@ namespace GoldbergGUI.Core.ViewModels } else { - var list = await _steam.GetListOfAppsByName(GameName).ConfigureAwait(false); + var list = _steam.GetListOfAppsByName(GameName); var steamApps = list as SteamApp[] ?? list.ToArray(); if (steamApps.Length == 1) { @@ -375,37 +364,10 @@ namespace GoldbergGUI.Core.ViewModels return; } - var steamApp = await _steam.GetAppById(AppId).ConfigureAwait(false); + var steamApp = await Task.Run(() => _steam.GetAppById(AppId)).ConfigureAwait(false); if (steamApp != null) GameName = steamApp.Name; } - public IMvxCommand GetListOfAchievementsCommand => new MvxAsyncCommand(GetListOfAchievements); - - private async Task GetListOfAchievements() - { - if (AppId <= 0) - { - _log.Error("Invalid Steam App!"); - return; - } - - MainWindowEnabled = false; - StatusText = "Trying to get list of achievements..."; - var listOfAchievements = await _steam.GetListOfAchievements(new SteamApp { AppId = AppId, Name = GameName }); - Achievements = new MvxObservableCollection(listOfAchievements); - MainWindowEnabled = true; - - if (Achievements.Count > 0) - { - var empty = Achievements.Count == 1 ? "" : "s"; - StatusText = $"Successfully got {Achievements.Count} achievement{empty}! Ready."; - } - else - { - StatusText = "No achievements found! Ready."; - } - } - public IMvxCommand GetListOfDlcCommand => new MvxAsyncCommand(GetListOfDlc); private async Task GetListOfDlc() @@ -418,9 +380,9 @@ namespace GoldbergGUI.Core.ViewModels MainWindowEnabled = false; StatusText = "Trying to get list of DLCs..."; - var listOfDlc = await _steam.GetListOfDlc(new SteamApp { AppId = AppId, Name = GameName }, true) + var listOfDlc = await _steam.GetListOfDlc(new SteamApp {AppId = AppId, Name = GameName}, true) .ConfigureAwait(false); - DLCs = new MvxObservableCollection(listOfDlc); + DLCs = new MvxObservableCollection(listOfDlc); MainWindowEnabled = true; if (DLCs.Count > 0) { @@ -440,8 +402,8 @@ namespace GoldbergGUI.Core.ViewModels _log.Info("Saving global settings..."); var globalConfiguration = new GoldbergGlobalConfiguration { - AccountName = AccountName, - UserSteamId = SteamId, + AccountName = AccountName, + UserSteamId = SteamId, Language = SelectedLanguage }; await _goldberg.SetGlobalSettings(globalConfiguration).ConfigureAwait(false); @@ -452,14 +414,13 @@ namespace GoldbergGUI.Core.ViewModels MainWindowEnabled = false; StatusText = "Saving..."; await _goldberg.Save(dirPath, new GoldbergConfiguration - { - AppId = AppId, - Achievements = Achievements.ToList(), - DlcList = DLCs.ToList(), - Offline = Offline, - DisableNetworking = DisableNetworking, - DisableOverlay = DisableOverlay - } + { + AppId = AppId, + DlcList = DLCs.ToList(), + Offline = Offline, + DisableNetworking = DisableNetworking, + DisableOverlay = DisableOverlay + } ).ConfigureAwait(false); GoldbergApplied = _goldberg.GoldbergApplied(dirPath); MainWindowEnabled = true; @@ -517,19 +478,17 @@ namespace GoldbergGUI.Core.ViewModels { var result = Clipboard.GetText(); var expression = new Regex(@"(?.*) *= *(?.*)"); - var pastedDlc = (from line in result.Split(new[] { "\n", "\r\n" }, - StringSplitOptions.RemoveEmptyEntries) - select expression.Match(line) into match - where match.Success - select new DlcApp - { - AppId = Convert.ToInt32(match.Groups["id"].Value), - Name = match.Groups["name"].Value - }).ToList(); + var pastedDlc = (from line in result.Split(new[] {"\n", "\r\n"}, + StringSplitOptions.RemoveEmptyEntries) select expression.Match(line) into match + where match.Success select new SteamApp + { + AppId = Convert.ToInt32(match.Groups["id"].Value), + Name = match.Groups["name"].Value + }).ToList(); if (pastedDlc.Count > 0) { DLCs.Clear(); - DLCs = new ObservableCollection(pastedDlc); + DLCs = new ObservableCollection(pastedDlc); //var empty = DLCs.Count == 1 ? "" : "s"; //StatusText = $"Successfully got {DLCs.Count} DLC{empty} from clipboard! Ready."; var statusTextCount = DLCs.Count == 1 ? "one DLC" : $"{DLCs.Count} DLCs"; @@ -565,8 +524,7 @@ namespace GoldbergGUI.Core.ViewModels DllPath = "Path to game's steam_api(64).dll..."; GameName = "Game name..."; AppId = -1; - Achievements = new ObservableCollection(); - DLCs = new ObservableCollection(); + DLCs = new ObservableCollection(); AccountName = "Account name..."; SteamId = -1; Offline = false; @@ -586,8 +544,7 @@ namespace GoldbergGUI.Core.ViewModels private void SetFormFromConfig(GoldbergConfiguration config) { AppId = config.AppId; - Achievements = new ObservableCollection(config.Achievements); - DLCs = new ObservableCollection(config.DlcList); + DLCs = new ObservableCollection(config.DlcList); Offline = config.Offline; DisableNetworking = config.DisableNetworking; DisableOverlay = config.DisableOverlay; diff --git a/GoldbergGUI.Core/ViewModels/SearchResultViewModel.cs b/GoldbergGUI.Core/ViewModels/SearchResultViewModel.cs index 35f4dc2..dd46194 100644 --- a/GoldbergGUI.Core/ViewModels/SearchResultViewModel.cs +++ b/GoldbergGUI.Core/ViewModels/SearchResultViewModel.cs @@ -1,10 +1,10 @@ +using System.Collections.Generic; +using System.Threading.Tasks; using GoldbergGUI.Core.Models; using MvvmCross.Commands; using MvvmCross.Logging; using MvvmCross.Navigation; using MvvmCross.ViewModels; -using System.Collections.Generic; -using System.Threading.Tasks; namespace GoldbergGUI.Core.ViewModels { @@ -14,7 +14,7 @@ namespace GoldbergGUI.Core.ViewModels private readonly IMvxLog _log; private IEnumerable _apps; - public SearchResultViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : + public SearchResultViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService) { _log = logProvider.GetLogFor(typeof(SearchResultViewModel)); @@ -25,7 +25,7 @@ namespace GoldbergGUI.Core.ViewModels { Apps = parameter; } - + public IEnumerable Apps { get => _apps; @@ -35,7 +35,7 @@ namespace GoldbergGUI.Core.ViewModels RaisePropertyChanged(() => Apps); } } - + public SteamApp Selected { get; diff --git a/GoldbergGUI.WPF/App.xaml.cs b/GoldbergGUI.WPF/App.xaml.cs index 5d14782..7b2e5c1 100644 --- a/GoldbergGUI.WPF/App.xaml.cs +++ b/GoldbergGUI.WPF/App.xaml.cs @@ -1,4 +1,5 @@ using MvvmCross.Core; +using MvvmCross.Platforms.Wpf.Core; using MvvmCross.Platforms.Wpf.Views; namespace GoldbergGUI.WPF diff --git a/GoldbergGUI.WPF/AssemblyInfo.cs b/GoldbergGUI.WPF/AssemblyInfo.cs index 4f943de..4a05c7d 100644 --- a/GoldbergGUI.WPF/AssemblyInfo.cs +++ b/GoldbergGUI.WPF/AssemblyInfo.cs @@ -2,9 +2,9 @@ using System.Windows; [assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) + //(used if a resource is not found in the page, + // or application resource dictionaries) ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) )] \ No newline at end of file diff --git a/GoldbergGUI.WPF/GoldbergGUI.WPF.csproj b/GoldbergGUI.WPF/GoldbergGUI.WPF.csproj index 6e1a7aa..eb4287b 100644 --- a/GoldbergGUI.WPF/GoldbergGUI.WPF.csproj +++ b/GoldbergGUI.WPF/GoldbergGUI.WPF.csproj @@ -2,11 +2,11 @@ WinExe - net8.0-windows + netcoreapp3.1 true - 0.3.0 + 0.1.0 + 0.1.0 Jeddunk - AnyCPU;x86;x64 diff --git a/GoldbergGUI.WPF/Setup.cs b/GoldbergGUI.WPF/Setup.cs index c9f6d78..e11f828 100644 --- a/GoldbergGUI.WPF/Setup.cs +++ b/GoldbergGUI.WPF/Setup.cs @@ -1,17 +1,20 @@ +using System; +using System.IO; +using System.Windows.Controls; +using System.Windows.Threading; using MvvmCross.Logging; using MvvmCross.Platforms.Wpf.Core; using Serilog; -using System.IO; namespace GoldbergGUI.WPF { public class Setup : MvxWpfSetup { public override MvxLogProviderType GetDefaultLogProviderType() => MvxLogProviderType.Serilog; - + protected override IMvxLogProvider CreateLogProvider() { - var logPath = Path.Combine(Directory.GetCurrentDirectory(), "goldberg_.log"); + var logPath = Path.Combine(Directory.GetCurrentDirectory(),"goldberg_.log"); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() diff --git a/GoldbergGUI.WPF/Views/MainView.xaml b/GoldbergGUI.WPF/Views/MainView.xaml index 83b115d..b8d16ce 100644 --- a/GoldbergGUI.WPF/Views/MainView.xaml +++ b/GoldbergGUI.WPF/Views/MainView.xaml @@ -7,9 +7,6 @@ xmlns:viewmodel="clr-namespace:GoldbergGUI.Core.ViewModels;assembly=GoldbergGUI.Core" mc:Ignorable="d" d:DesignHeight="500" d:DesignWidth="400" d:DataContext="{d:DesignInstance Type=viewmodel:MainViewModel }"> - - - @@ -32,7 +29,7 @@ - + @@ -42,71 +39,30 @@