From bc19e79d6973eaa28c390d72cb41f3133eaf5203 Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Sun, 21 Mar 2021 15:20:46 +0100 Subject: [PATCH 01/24] Added SQLite support --- GoldbergGUI.Core/GoldbergGUI.Core.csproj | 1 + GoldbergGUI.Core/Models/SteamAppModel.cs | 57 ++++++--- GoldbergGUI.Core/Services/SteamService.cs | 134 +++++++++------------- 3 files changed, 93 insertions(+), 99 deletions(-) diff --git a/GoldbergGUI.Core/GoldbergGUI.Core.csproj b/GoldbergGUI.Core/GoldbergGUI.Core.csproj index 79fab83..5eab489 100644 --- a/GoldbergGUI.Core/GoldbergGUI.Core.csproj +++ b/GoldbergGUI.Core/GoldbergGUI.Core.csproj @@ -12,6 +12,7 @@ + diff --git a/GoldbergGUI.Core/Models/SteamAppModel.cs b/GoldbergGUI.Core/Models/SteamAppModel.cs index 6fbf277..b5f245f 100644 --- a/GoldbergGUI.Core/Models/SteamAppModel.cs +++ b/GoldbergGUI.Core/Models/SteamAppModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; using System.Text.RegularExpressions; using GoldbergGUI.Core.Utils; +using SQLite; // ReSharper disable UnusedMember.Global // ReSharper disable ClassNeverInstantiated.Global @@ -11,44 +12,51 @@ using GoldbergGUI.Core.Utils; // ReSharper disable InconsistentNaming namespace GoldbergGUI.Core.Models { + [Table("steamapp")] public class SteamApp { private string _name; - private string _comparableName; - [JsonPropertyName("appid")] public int AppId { get; set; } + + [JsonPropertyName("appid")] + [Column("appid")] + [PrimaryKey] + public int AppId { get; set; } /// /// Name of Steam app /// [JsonPropertyName("name")] + [Column("name")] public string Name { get => _name; set { _name = value; - _comparableName = Regex.Replace(value, Misc.AlphaNumOnlyRegex, "").ToLower(); + 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); + + [Column("comparable_name")] + public string ComparableName { get; private set; } /// /// App type (Game, DLC, ...) /// - public AppType type { get; set; } + [Column("type")] + public string type { get; set; } public override string ToString() { return $"{AppId}={Name}"; } - [JsonPropertyName("last_modified")] public long LastModified { get; set; } + [JsonPropertyName("last_modified")] + [Ignore] + public long LastModified { get; set; } [JsonPropertyName("price_change_number")] + [Ignore] public long PriceChangeNumber { get; set; } } @@ -77,18 +85,29 @@ namespace GoldbergGUI.Core.Models [JsonPropertyName("response")] public override AppList AppList { get; set; } } + /*[Table("apptype")] public class AppType { - private AppType(string value) => Value = value; + private AppType(string value) + { + var db = new SQLiteConnection("steamapps.db"); + db.CreateTable(); + Value = value; + } + [PrimaryKey, AutoIncrement] + [Column("id")] + public int Id { get; } + + [Column("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"); - } + [Ignore] public static AppType Game { get; } = new AppType("game"); + [Ignore] public static AppType DLC { get; } = new AppType("dlc"); + [Ignore] public static AppType Music { get; } = new AppType("music"); + [Ignore] public static AppType Demo { get; } = new AppType("demo"); + [Ignore] public static AppType Ad { get; } = new AppType("advertising"); + [Ignore] public static AppType Mod { get; } = new AppType("mod"); + [Ignore] public static AppType Video { get; } = new AppType("video"); + }*/ } \ No newline at end of file diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index 300961e..eab75cf 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -3,7 +3,6 @@ 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; @@ -13,6 +12,7 @@ using GoldbergGUI.Core.Models; using GoldbergGUI.Core.Utils; using MvvmCross.Logging; using NinjaNye.SearchExtensions; +using SQLite; using SteamStorefrontAPI; namespace GoldbergGUI.Core.Services @@ -29,15 +29,12 @@ namespace GoldbergGUI.Core.Services class SteamCache { - public string Filename { get; } public string SteamUri { get; } public Type ApiVersion { get; } - public AppType SteamAppType { get; } - public HashSet Cache { get; set; } = new HashSet(); + public string SteamAppType { get; } - public SteamCache(string filename, string uri, Type apiVersion, AppType steamAppType) + public SteamCache(string uri, Type apiVersion, string steamAppType) { - Filename = filename; SteamUri = uri; ApiVersion = apiVersion; SteamAppType = steamAppType; @@ -49,32 +46,30 @@ namespace GoldbergGUI.Core.Services public class SteamService : ISteamService { // ReSharper disable StringLiteralTypo - private readonly Dictionary _caches = - new Dictionary + private readonly Dictionary _caches = + new Dictionary { { - AppType.Game, + AppTypeGame, new SteamCache( - "steamapps_games.json", "https://api.steampowered.com/IStoreService/GetAppList/v1/" + "?max_results=50000" + "&include_games=1" + "&key=" + Secrets.SteamWebApiKey(), typeof(SteamAppsV1), - AppType.Game + AppTypeGame ) }, { - AppType.DLC, + AppTypeDlc, 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), - AppType.DLC + AppTypeDlc ) } }; @@ -85,90 +80,66 @@ namespace GoldbergGUI.Core.Services "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.db"; + private IMvxLog _log; + private SQLiteConnection _db; + public async Task Initialize(IMvxLog log) { //var (path, uri, jsonType, appType) = _caches[0]; static SteamApps DeserializeSteamApps(Type type, string cacheString) { - if (type == typeof(SteamAppsV1)) - return JsonSerializer.Deserialize(cacheString); - else if (type == typeof(SteamAppsV2)) - return JsonSerializer.Deserialize(cacheString); - return null; + return type == typeof(SteamAppsV2) + ? (SteamApps) JsonSerializer.Deserialize(cacheString) + : JsonSerializer.Deserialize(cacheString); } - foreach (var (k, c) in _caches) - { - _log = log; - _log.Info($"Updating cache ({k.Value})..."); - var updateNeeded = - DateTime.Now.Subtract(File.GetLastWriteTimeUtc(c.Filename)).TotalDays >= 1; - SteamApps steamApps; - try - { - 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); - } + _log = log; + _db = new SQLiteConnection(Database); + _db.CreateTable(); - try + if (DateTime.Now.Subtract(File.GetLastWriteTimeUtc(Database)).TotalDays >= 1 || !_db.Table().Any()) + { + foreach (var (appType, steamCache) in _caches) { - var cacheRaw = new HashSet(steamApps.AppList.Apps); + _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 cache = new HashSet(); foreach (var steamApp in cacheRaw) { - steamApp.type = c.SteamAppType; + steamApp.type = steamCache.SteamAppType; cache.Add(steamApp); } - c.Cache = cache; - - _log.Info("Loaded cache into memory!"); - } - catch (NullReferenceException e) - { - Console.WriteLine(e); - throw; + _db.InsertAll(cache); } } } - private async Task GetCache(bool updateNeeded, string steamUri, string cachePath) - { - 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) + var listOfAppsByName = _db.Table() + .Where(x => x.type == AppTypeGame).Search(x => x.Name) .SetCulture(StringComparison.OrdinalIgnoreCase) .ContainingAll(name.Split(' ')); return listOfAppsByName; @@ -178,7 +149,8 @@ namespace GoldbergGUI.Core.Services { _log.Info($"Trying to get app {name}"); var comparableName = Regex.Replace(name, Misc.AlphaNumOnlyRegex, "").ToLower(); - var app = _caches[AppType.Game].Cache.FirstOrDefault(x => x.CompareName(comparableName)); + var app = _db.Table().Where(x => x.type == AppTypeGame) + .FirstOrDefault(x => x.ComparableName.Equals(comparableName)); if (app != null) _log.Info($"Successfully got app {app}"); return app; } @@ -186,7 +158,8 @@ namespace GoldbergGUI.Core.Services public SteamApp GetAppById(int appid) { _log.Info($"Trying to get app with ID {appid}"); - var app = _caches[AppType.Game].Cache.FirstOrDefault(x => x.AppId.Equals(appid)); + var app = _db.Table().Where(x => x.type == AppTypeGame) + .FirstOrDefault(x => x.AppId.Equals(appid)); if (app != null) _log.Info($"Successfully got app {app}"); return app; } @@ -199,11 +172,12 @@ namespace GoldbergGUI.Core.Services { var task = AppDetails.GetAsync(steamApp.AppId); var steamAppDetails = await task.ConfigureAwait(true); - if (steamAppDetails.Type == AppType.Game.Value) + if (steamAppDetails.Type == AppTypeGame) { steamAppDetails.DLC.ForEach(x => { - var result = _caches[AppType.DLC].Cache.FirstOrDefault(y => y.AppId.Equals(x)) + var result = _db.Table().Where(z => z.type == AppTypeDlc) + .FirstOrDefault(y => y.AppId.Equals(x)) ?? new SteamApp {AppId = x, Name = $"Unknown DLC {x}"}; dlcList.Add(result); }); @@ -215,7 +189,7 @@ namespace GoldbergGUI.Core.Services // Get Cloudflare cookie // Scrape and parse HTML page // Add missing to DLC list - + // ReSharper disable once InvertIf if (useSteamDb) { From 13eb05fff9fa517e13a3f089f512529a1584c281 Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Sun, 21 Mar 2021 15:53:56 +0100 Subject: [PATCH 02/24] Fixed crash when trying to get app by name from db --- GoldbergGUI.Core/Models/SteamAppModel.cs | 42 ++--------------------- GoldbergGUI.Core/Services/SteamService.cs | 5 +-- 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/GoldbergGUI.Core/Models/SteamAppModel.cs b/GoldbergGUI.Core/Models/SteamAppModel.cs index b5f245f..eb1583f 100644 --- a/GoldbergGUI.Core/Models/SteamAppModel.cs +++ b/GoldbergGUI.Core/Models/SteamAppModel.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -using System.Text.RegularExpressions; -using GoldbergGUI.Core.Utils; using SQLite; // ReSharper disable UnusedMember.Global @@ -15,8 +13,6 @@ namespace GoldbergGUI.Core.Models [Table("steamapp")] public class SteamApp { - private string _name; - [JsonPropertyName("appid")] [Column("appid")] [PrimaryKey] @@ -27,18 +23,10 @@ namespace GoldbergGUI.Core.Models /// [JsonPropertyName("name")] [Column("name")] - public string Name - { - get => _name; - set - { - _name = value; - ComparableName = Regex.Replace(value, Misc.AlphaNumOnlyRegex, "").ToLower(); - } - } + public string Name { get; set; } [Column("comparable_name")] - public string ComparableName { get; private set; } + public string ComparableName { get; set; } /// /// App type (Game, DLC, ...) @@ -84,30 +72,4 @@ namespace GoldbergGUI.Core.Models { [JsonPropertyName("response")] public override AppList AppList { get; set; } } - - /*[Table("apptype")] - public class AppType - { - private AppType(string value) - { - var db = new SQLiteConnection("steamapps.db"); - db.CreateTable(); - Value = value; - } - - [PrimaryKey, AutoIncrement] - [Column("id")] - public int Id { get; } - - [Column("value")] - public string Value { get; } - - [Ignore] public static AppType Game { get; } = new AppType("game"); - [Ignore] public static AppType DLC { get; } = new AppType("dlc"); - [Ignore] public static AppType Music { get; } = new AppType("music"); - [Ignore] public static AppType Demo { get; } = new AppType("demo"); - [Ignore] public static AppType Ad { get; } = new AppType("advertising"); - [Ignore] public static AppType Mod { get; } = new AppType("mod"); - [Ignore] public static AppType Video { get; } = new AppType("video"); - }*/ } \ No newline at end of file diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index eab75cf..7b8e338 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -128,6 +128,7 @@ namespace GoldbergGUI.Core.Services foreach (var steamApp in cacheRaw) { steamApp.type = steamCache.SteamAppType; + steamApp.ComparableName = Regex.Replace(steamApp.Name, Misc.AlphaNumOnlyRegex, "").ToLower(); cache.Add(steamApp); } @@ -149,8 +150,8 @@ namespace GoldbergGUI.Core.Services { _log.Info($"Trying to get app {name}"); var comparableName = Regex.Replace(name, Misc.AlphaNumOnlyRegex, "").ToLower(); - var app = _db.Table().Where(x => x.type == AppTypeGame) - .FirstOrDefault(x => x.ComparableName.Equals(comparableName)); + var app = _db.Table() + .FirstOrDefault(x => x.type == AppTypeGame && x.ComparableName.Equals(comparableName)); if (app != null) _log.Info($"Successfully got app {app}"); return app; } From 3bee1a5508cb59fbd38f3d2333f1f538b891137c Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Sun, 21 Mar 2021 16:15:17 +0100 Subject: [PATCH 03/24] minor changes --- GoldbergGUI.Core/Services/GoldbergService.cs | 2 -- GoldbergGUI.Core/Services/SteamService.cs | 12 ++++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index 299ba96..81fec86 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -23,8 +23,6 @@ namespace GoldbergGUI.Core.Services 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(); } diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index 7b8e338..d16c9db 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -82,7 +82,7 @@ namespace GoldbergGUI.Core.Services private const string AppTypeGame = "game"; private const string AppTypeDlc = "dlc"; - private const string Database = "steamapps.db"; + private const string Database = "steamapps.cache"; private IMvxLog _log; @@ -90,7 +90,6 @@ namespace GoldbergGUI.Core.Services public async Task Initialize(IMvxLog log) { - //var (path, uri, jsonType, appType) = _caches[0]; static SteamApps DeserializeSteamApps(Type type, string cacheString) { return type == typeof(SteamAppsV2) @@ -128,7 +127,7 @@ namespace GoldbergGUI.Core.Services foreach (var steamApp in cacheRaw) { steamApp.type = steamCache.SteamAppType; - steamApp.ComparableName = Regex.Replace(steamApp.Name, Misc.AlphaNumOnlyRegex, "").ToLower(); + steamApp.ComparableName = PrepareStringToCompare(steamApp.Name); cache.Add(steamApp); } @@ -149,7 +148,7 @@ namespace GoldbergGUI.Core.Services public SteamApp GetAppByName(string name) { _log.Info($"Trying to get app {name}"); - var comparableName = Regex.Replace(name, Misc.AlphaNumOnlyRegex, "").ToLower(); + var comparableName = PrepareStringToCompare(name); var app = _db.Table() .FirstOrDefault(x => x.type == AppTypeGame && x.ComparableName.Equals(comparableName)); if (app != null) _log.Info($"Successfully got app {app}"); @@ -256,5 +255,10 @@ 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 From c823aa15fb1daac3b5c243546c25b887aa30615d Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Sun, 21 Mar 2021 16:55:58 +0100 Subject: [PATCH 04/24] sqlite functions are now (mostly) async --- GoldbergGUI.Core/Services/SteamService.cs | 34 +++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index d16c9db..8acb058 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net.Http; using System.Text.Json; using System.Text.RegularExpressions; @@ -86,7 +85,7 @@ namespace GoldbergGUI.Core.Services private IMvxLog _log; - private SQLiteConnection _db; + private SQLiteAsyncConnection _db; public async Task Initialize(IMvxLog log) { @@ -98,10 +97,14 @@ namespace GoldbergGUI.Core.Services } _log = log; - _db = new SQLiteConnection(Database); - _db.CreateTable(); + _db = new SQLiteAsyncConnection(Database); + //_db.CreateTable(); + await _db.CreateTableAsync() + //.ContinueWith(x => _log.Debug("Table success!")) + .ConfigureAwait(false); - if (DateTime.Now.Subtract(File.GetLastWriteTimeUtc(Database)).TotalDays >= 1 || !_db.Table().Any()) + var countAsync = await _db.Table().CountAsync().ConfigureAwait(false); + if (DateTime.Now.Subtract(File.GetLastWriteTimeUtc(Database)).TotalDays >= 1 || countAsync == 0) { foreach (var (appType, steamCache) in _caches) { @@ -131,15 +134,16 @@ namespace GoldbergGUI.Core.Services cache.Add(steamApp); } - _db.InsertAll(cache); + await _db.InsertAllAsync(cache).ConfigureAwait(false); } } } public IEnumerable GetListOfAppsByName(string name) { - var listOfAppsByName = _db.Table() - .Where(x => x.type == AppTypeGame).Search(x => x.Name) + var query = _db.Table() + .Where(x => x.type == AppTypeGame).ToListAsync().Result; + var listOfAppsByName = query.Search(x => x.Name) .SetCulture(StringComparison.OrdinalIgnoreCase) .ContainingAll(name.Split(' ')); return listOfAppsByName; @@ -150,7 +154,7 @@ namespace GoldbergGUI.Core.Services _log.Info($"Trying to get app {name}"); var comparableName = PrepareStringToCompare(name); var app = _db.Table() - .FirstOrDefault(x => x.type == AppTypeGame && x.ComparableName.Equals(comparableName)); + .FirstOrDefaultAsync(x => x.type == AppTypeGame && x.ComparableName.Equals(comparableName)).Result; if (app != null) _log.Info($"Successfully got app {app}"); return app; } @@ -159,7 +163,7 @@ namespace GoldbergGUI.Core.Services { _log.Info($"Trying to get app with ID {appid}"); var app = _db.Table().Where(x => x.type == AppTypeGame) - .FirstOrDefault(x => x.AppId.Equals(appid)); + .FirstOrDefaultAsync(x => x.AppId.Equals(appid)).Result; if (app != null) _log.Info($"Successfully got app {app}"); return app; } @@ -174,19 +178,19 @@ namespace GoldbergGUI.Core.Services var steamAppDetails = await task.ConfigureAwait(true); if (steamAppDetails.Type == AppTypeGame) { - steamAppDetails.DLC.ForEach(x => + steamAppDetails.DLC.ForEach(async x => { - var result = _db.Table().Where(z => z.type == AppTypeDlc) - .FirstOrDefault(y => y.AppId.Equals(x)) + var result = await _db.Table().Where(z => z.type == AppTypeDlc) + .FirstOrDefaultAsync(y => y.AppId.Equals(x)).ConfigureAwait(true) ?? new SteamApp {AppId = x, Name = $"Unknown DLC {x}"}; dlcList.Add(result); + _log.Debug($"{result.AppId}={result.Name}"); }); - dlcList.ForEach(x => _log.Debug($"{x.AppId}={x.Name}")); _log.Info("Got DLC successfully..."); // Get DLC from SteamDB - // Get Cloudflare cookie + // Get Cloudflare cookie (not implemented) // Scrape and parse HTML page // Add missing to DLC list From b2cdd34cf8056b043e343af224baceea3316d34e Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Wed, 24 Mar 2021 10:21:59 +0100 Subject: [PATCH 05/24] Fixed issue with "UNIQUE" appid in cache during initialization. Adjusted async code to new changes. --- GoldbergGUI.Core/Services/SteamService.cs | 28 ++++++++++---------- GoldbergGUI.Core/ViewModels/MainViewModel.cs | 6 ++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index 8acb058..7b17e85 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -20,9 +20,9 @@ namespace GoldbergGUI.Core.Services public interface ISteamService { public Task Initialize(IMvxLog log); - public IEnumerable GetListOfAppsByName(string name); - public SteamApp GetAppByName(string name); - public SteamApp GetAppById(int appid); + public Task> GetListOfAppsByName(string name); + public Task GetAppByName(string name); + public Task GetAppById(int appid); public Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb); } @@ -78,7 +78,6 @@ 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"; @@ -134,36 +133,37 @@ namespace GoldbergGUI.Core.Services cache.Add(steamApp); } - await _db.InsertAllAsync(cache).ConfigureAwait(false); + await _db.InsertAllAsync(cache, "OR IGNORE").ConfigureAwait(false); } } } - public IEnumerable GetListOfAppsByName(string name) + public async Task> GetListOfAppsByName(string name) { - var query = _db.Table() - .Where(x => x.type == AppTypeGame).ToListAsync().Result; + var query = await _db.Table() + .Where(x => x.type == AppTypeGame).ToListAsync().ConfigureAwait(false); var listOfAppsByName = query.Search(x => x.Name) .SetCulture(StringComparison.OrdinalIgnoreCase) .ContainingAll(name.Split(' ')); return listOfAppsByName; } - public SteamApp GetAppByName(string name) + public async Task GetAppByName(string name) { _log.Info($"Trying to get app {name}"); var comparableName = PrepareStringToCompare(name); - var app = _db.Table() - .FirstOrDefaultAsync(x => x.type == AppTypeGame && x.ComparableName.Equals(comparableName)).Result; + var app = await _db.Table() + .FirstOrDefaultAsync(x => x.type == AppTypeGame && x.ComparableName.Equals(comparableName)) + .ConfigureAwait(false); if (app != null) _log.Info($"Successfully got app {app}"); return app; } - public SteamApp GetAppById(int appid) + public async Task GetAppById(int appid) { _log.Info($"Trying to get app with ID {appid}"); - var app = _db.Table().Where(x => x.type == AppTypeGame) - .FirstOrDefaultAsync(x => x.AppId.Equals(appid)).Result; + var app = await _db.Table().Where(x => x.type == AppTypeGame) + .FirstOrDefaultAsync(x => x.AppId.Equals(appid)).ConfigureAwait(false); if (app != null) _log.Info($"Successfully got app {app}"); return app; } diff --git a/GoldbergGUI.Core/ViewModels/MainViewModel.cs b/GoldbergGUI.Core/ViewModels/MainViewModel.cs index ef36c1d..6f71093 100644 --- a/GoldbergGUI.Core/ViewModels/MainViewModel.cs +++ b/GoldbergGUI.Core/ViewModels/MainViewModel.cs @@ -321,7 +321,7 @@ namespace GoldbergGUI.Core.ViewModels MainWindowEnabled = false; StatusText = "Trying to find AppID..."; - var appByName = _steam.GetAppByName(_gameName); + var appByName = await _steam.GetAppByName(_gameName).ConfigureAwait(false); if (appByName != null) { GameName = appByName.Name; @@ -329,7 +329,7 @@ namespace GoldbergGUI.Core.ViewModels } else { - var list = _steam.GetListOfAppsByName(GameName); + var list = await _steam.GetListOfAppsByName(GameName).ConfigureAwait(false); var steamApps = list as SteamApp[] ?? list.ToArray(); if (steamApps.Length == 1) { @@ -364,7 +364,7 @@ namespace GoldbergGUI.Core.ViewModels return; } - var steamApp = await Task.Run(() => _steam.GetAppById(AppId)).ConfigureAwait(false); + var steamApp = await _steam.GetAppById(AppId).ConfigureAwait(false); if (steamApp != null) GameName = steamApp.Name; } From 8e62880e23500c286c8a5ef367ea9ac5763a4531 Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Tue, 6 Apr 2021 16:55:53 +0200 Subject: [PATCH 06/24] Show error message if Goldberg could not be downloaded/extracted, prompting the user to do so manually. Try to skip getting DLC from SteamDB on error. --- GoldbergGUI.Core/Services/GoldbergService.cs | 56 +++++++++--- GoldbergGUI.Core/Services/SteamService.cs | 94 +++++++++++--------- 2 files changed, 96 insertions(+), 54 deletions(-) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index 299ba96..4de2f33 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -6,6 +6,7 @@ using System.Linq; 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; @@ -85,7 +86,10 @@ namespace GoldbergGUI.Core.Services _log = log; var download = await Download().ConfigureAwait(false); - if (download) await Extract(_goldbergZipPath).ConfigureAwait(false); + if (download) + { + await Extract(_goldbergZipPath).ConfigureAwait(false); + } return await GetGlobalSettings().ConfigureAwait(false); } @@ -379,15 +383,24 @@ namespace GoldbergGUI.Core.Services private async Task StartDownload(string downloadUrl) { - var client = new HttpClient(); - _log.Debug(downloadUrl); - await using var fileStream = File.OpenWrite(_goldbergZipPath); - //client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead) - var task = client.GetFileAsync(downloadUrl, fileStream).ConfigureAwait(false); - await task; - if (task.GetAwaiter().IsCompleted) + try { - _log.Info("Download finished!"); + var client = new HttpClient(); + _log.Debug(downloadUrl); + await using var fileStream = File.OpenWrite(_goldbergZipPath); + //client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead) + var task = client.GetFileAsync(downloadUrl, fileStream).ConfigureAwait(false); + await task; + if (task.GetAwaiter().IsCompleted) + { + _log.Info("Download finished!"); + } + } + catch (Exception e) + { + ShowErrorMessage(); + _log.Error(e.ToString); + Environment.Exit(1); } } @@ -398,12 +411,33 @@ namespace GoldbergGUI.Core.Services _log.Debug("Start extraction..."); await Task.Run(() => { - Directory.Delete(_goldbergPath, true); - ZipFile.ExtractToDirectory(archivePath, _goldbergPath); + try + { + Directory.Delete(_goldbergPath, true); + ZipFile.ExtractToDirectory(archivePath, _goldbergPath); + } + catch (Exception e) + { + ShowErrorMessage(); + _log.Error(e.ToString); + Environment.Exit(1); + // throw; + } }).ConfigureAwait(false); _log.Debug("Extraction done!"); } + private void ShowErrorMessage() + { + if (Directory.Exists(_goldbergPath)) + { + Directory.Delete(_goldbergPath, true); + } + Directory.CreateDirectory(_goldbergPath); + MessageBox.Show("Could not setup Goldberg Emulator!\n" + + "Please download it manually and extract its content into the \"goldberg\" subfolder!"); + } + // https://gitlab.com/Mr_Goldberg/goldberg_emulator/-/blob/master/generate_interfaces_file.cpp // (maybe) check DLL date first public async Task GenerateInterfacesFile(string filePath) diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index 300961e..3e23be6 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -215,57 +215,65 @@ namespace GoldbergGUI.Core.Services // Get Cloudflare cookie // Scrape and parse HTML page // Add missing to DLC list - + // ReSharper disable once InvertIf if (useSteamDb) { - var steamDbUri = new Uri($"https://steamdb.info/app/{steamApp.AppId}/dlc/"); - - var client = new HttpClient(); - client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent); - - _log.Info("Get SteamDB App"); - var httpCall = client.GetAsync(steamDbUri); - var response = await httpCall.ConfigureAwait(false); - _log.Debug(httpCall.Status.ToString()); - _log.Debug(response.EnsureSuccessStatusCode().ToString()); - - var readAsStringAsync = response.Content.ReadAsStringAsync(); - var responseBody = await readAsStringAsync.ConfigureAwait(false); - _log.Debug(readAsStringAsync.Status.ToString()); - - var parser = new HtmlParser(); - var doc = parser.ParseDocument(responseBody); - - var query1 = doc.QuerySelector("#dlc"); - if (query1 != null) + try { - var query2 = query1.QuerySelectorAll(".app"); - foreach (var element in query2) - { - var dlcId = element.GetAttribute("data-appid"); - var query3 = element.QuerySelectorAll("td"); - var dlcName = query3 != null - ? query3[1].Text().Replace("\n", "").Trim() - : $"Unknown DLC {dlcId}"; - var dlcApp = new SteamApp {AppId = Convert.ToInt32(dlcId), Name = dlcName}; - var i = dlcList.FindIndex(x => x.AppId.Equals(dlcApp.AppId)); - if (i > -1) - { - if (dlcList[i].Name.Contains("Unknown DLC")) dlcList[i] = dlcApp; - } - else - { - dlcList.Add(dlcApp); - } - } + var steamDbUri = new Uri($"https://steamdb.info/app/{steamApp.AppId}/dlc/"); - dlcList.ForEach(x => _log.Debug($"{x.AppId}={x.Name}")); - _log.Info("Got DLC from SteamDB successfully..."); + var client = new HttpClient(); + client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent); + + _log.Info("Get SteamDB App"); + var httpCall = client.GetAsync(steamDbUri); + var response = await httpCall.ConfigureAwait(false); + _log.Debug(httpCall.Status.ToString()); + _log.Debug(response.EnsureSuccessStatusCode().ToString()); + + var readAsStringAsync = response.Content.ReadAsStringAsync(); + var responseBody = await readAsStringAsync.ConfigureAwait(false); + _log.Debug(readAsStringAsync.Status.ToString()); + + var parser = new HtmlParser(); + var doc = parser.ParseDocument(responseBody); + + var query1 = doc.QuerySelector("#dlc"); + if (query1 != null) + { + var query2 = query1.QuerySelectorAll(".app"); + foreach (var element in query2) + { + var dlcId = element.GetAttribute("data-appid"); + var query3 = element.QuerySelectorAll("td"); + var dlcName = query3 != null + ? query3[1].Text().Replace("\n", "").Trim() + : $"Unknown DLC {dlcId}"; + var dlcApp = new SteamApp {AppId = Convert.ToInt32(dlcId), Name = dlcName}; + var i = dlcList.FindIndex(x => x.AppId.Equals(dlcApp.AppId)); + if (i > -1) + { + if (dlcList[i].Name.Contains("Unknown DLC")) dlcList[i] = dlcApp; + } + else + { + dlcList.Add(dlcApp); + } + } + + dlcList.ForEach(x => _log.Debug($"{x.AppId}={x.Name}")); + _log.Info("Got DLC from SteamDB successfully..."); + } + else + { + _log.Error("Could not get DLC from SteamDB!"); + } } - else + catch (Exception e) { _log.Error("Could not get DLC from SteamDB!"); + _log.Error(e.ToString); } } } From 653e97beab1d78fbf7fb813223783760c644c4e9 Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Wed, 7 Apr 2021 13:52:56 +0200 Subject: [PATCH 07/24] Improve verbosity of logs --- GoldbergGUI.Core/Services/SteamService.cs | 100 +++++++++++----------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index 3e23be6..b3d77e2 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -193,10 +193,10 @@ namespace GoldbergGUI.Core.Services public async Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb) { - _log.Info("Get DLC"); 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 == AppType.Game.Value) @@ -216,66 +216,66 @@ namespace GoldbergGUI.Core.Services // Scrape and parse HTML page // Add missing to DLC list - // ReSharper disable once InvertIf - if (useSteamDb) + // Return current list if we don't intend to use SteamDB + if (!useSteamDb) return dlcList; + + try { - try + var steamDbUri = new Uri($"https://steamdb.info/app/{steamApp.AppId}/dlc/"); + + var client = new HttpClient(); + client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent); + + _log.Info($"Get SteamDB App {steamApp}"); + var httpCall = client.GetAsync(steamDbUri); + var response = await httpCall.ConfigureAwait(false); + _log.Debug(httpCall.Status.ToString()); + _log.Debug(response.EnsureSuccessStatusCode().ToString()); + + var readAsStringAsync = response.Content.ReadAsStringAsync(); + var responseBody = await readAsStringAsync.ConfigureAwait(false); + _log.Debug(readAsStringAsync.Status.ToString()); + + var parser = new HtmlParser(); + var doc = parser.ParseDocument(responseBody); + + var query1 = doc.QuerySelector("#dlc"); + if (query1 != null) { - var steamDbUri = new Uri($"https://steamdb.info/app/{steamApp.AppId}/dlc/"); - - var client = new HttpClient(); - client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent); - - _log.Info("Get SteamDB App"); - var httpCall = client.GetAsync(steamDbUri); - var response = await httpCall.ConfigureAwait(false); - _log.Debug(httpCall.Status.ToString()); - _log.Debug(response.EnsureSuccessStatusCode().ToString()); - - var readAsStringAsync = response.Content.ReadAsStringAsync(); - var responseBody = await readAsStringAsync.ConfigureAwait(false); - _log.Debug(readAsStringAsync.Status.ToString()); - - var parser = new HtmlParser(); - var doc = parser.ParseDocument(responseBody); - - var query1 = doc.QuerySelector("#dlc"); - if (query1 != null) + _log.Info("Got list of DLC from SteamDB."); + var query2 = query1.QuerySelectorAll(".app"); + foreach (var element in query2) { - var query2 = query1.QuerySelectorAll(".app"); - foreach (var element in query2) + var dlcId = element.GetAttribute("data-appid"); + var query3 = element.QuerySelectorAll("td"); + var dlcName = query3 != null + ? query3[1].Text().Replace("\n", "").Trim() + : $"Unknown DLC {dlcId}"; + var dlcApp = new SteamApp {AppId = Convert.ToInt32(dlcId), Name = dlcName}; + var i = dlcList.FindIndex(x => x.AppId.Equals(dlcApp.AppId)); + if (i > -1) { - var dlcId = element.GetAttribute("data-appid"); - var query3 = element.QuerySelectorAll("td"); - var dlcName = query3 != null - ? query3[1].Text().Replace("\n", "").Trim() - : $"Unknown DLC {dlcId}"; - var dlcApp = new SteamApp {AppId = Convert.ToInt32(dlcId), Name = dlcName}; - var i = dlcList.FindIndex(x => x.AppId.Equals(dlcApp.AppId)); - if (i > -1) - { - if (dlcList[i].Name.Contains("Unknown DLC")) dlcList[i] = dlcApp; - } - else - { - dlcList.Add(dlcApp); - } + if (dlcList[i].Name.Contains("Unknown DLC")) dlcList[i] = dlcApp; } + else + { + dlcList.Add(dlcApp); + } + } - dlcList.ForEach(x => _log.Debug($"{x.AppId}={x.Name}")); - _log.Info("Got DLC from SteamDB successfully..."); - } - else - { - _log.Error("Could not get DLC from SteamDB!"); - } + dlcList.ForEach(x => _log.Debug($"{x.AppId}={x.Name}")); + _log.Info("Got DLC from SteamDB successfully..."); } - catch (Exception e) + else { _log.Error("Could not get DLC from SteamDB!"); - _log.Error(e.ToString); } } + catch (Exception e) + { + _log.Error("Could not get DLC from SteamDB! Skipping..."); + _log.Error(e.ToString); + } } else { From 62abe0e21248f33aea0151ed9ee623fe63800baf Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Wed, 7 Apr 2021 14:22:45 +0200 Subject: [PATCH 08/24] Improved extraction. --- GoldbergGUI.Core/Services/GoldbergService.cs | 90 ++++++++++++++------ 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index 4de2f33..a6f5617 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -23,7 +23,9 @@ 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); @@ -48,7 +50,9 @@ namespace GoldbergGUI.Core.Services private readonly string _accountNamePath = Path.Combine(GlobalSettingsPath, "settings/account_name.txt"); private readonly string _userSteamIdPath = Path.Combine(GlobalSettingsPath, "settings/user_steam_id.txt"); private readonly string _languagePath = Path.Combine(GlobalSettingsPath, "settings/language.txt"); - private readonly string _customBroadcastIpsPath = Path.Combine(GlobalSettingsPath, "settings/custom_broadcasts.txt"); + + private readonly string _customBroadcastIpsPath = + Path.Combine(GlobalSettingsPath, "settings/custom_broadcasts.txt"); // ReSharper disable StringLiteralTypo private readonly List _interfaceNames = new List @@ -90,6 +94,7 @@ namespace GoldbergGUI.Core.Services { await Extract(_goldbergZipPath).ConfigureAwait(false); } + return await GetGlobalSettings().ConfigureAwait(false); } @@ -110,6 +115,7 @@ namespace GoldbergGUI.Core.Services { _log.Error("Invalid User Steam ID!"); } + if (File.Exists(_languagePath)) language = File.ReadLines(_languagePath).First().Trim(); if (File.Exists(_customBroadcastIpsPath)) customBroadcastIps.AddRange( @@ -146,21 +152,23 @@ namespace GoldbergGUI.Core.Services await File.Create(_accountNamePath).DisposeAsync().ConfigureAwait(false); await File.WriteAllTextAsync(_accountNamePath, DefaultAccountName).ConfigureAwait(false); } + // User SteamID if (userSteamId >= 76561197960265729 && userSteamId <= 76561202255233023) { _log.Info("Setting user Steam ID..."); - if (!File.Exists(_userSteamIdPath)) + if (!File.Exists(_userSteamIdPath)) await File.Create(_userSteamIdPath).DisposeAsync().ConfigureAwait(false); await File.WriteAllTextAsync(_userSteamIdPath, userSteamId.ToString()).ConfigureAwait(false); } else { _log.Info("Invalid user Steam ID! Skipping..."); - if (!File.Exists(_userSteamIdPath)) + if (!File.Exists(_userSteamIdPath)) await File.Create(_userSteamIdPath).DisposeAsync().ConfigureAwait(false); await File.WriteAllTextAsync(_userSteamIdPath, DefaultSteamId.ToString()).ConfigureAwait(false); } + // Language if (!string.IsNullOrEmpty(language)) { @@ -176,11 +184,12 @@ namespace GoldbergGUI.Core.Services await File.Create(_languagePath).DisposeAsync().ConfigureAwait(false); await File.WriteAllTextAsync(_languagePath, DefaultLanguage).ConfigureAwait(false); } + // Custom Broadcast IPs if (customBroadcastIps != null && customBroadcastIps.Count > 0) { _log.Info("Setting custom broadcast IPs..."); - var result = + var result = customBroadcastIps.Aggregate("", (current, address) => $"{current}{address}\n"); if (!File.Exists(_customBroadcastIpsPath)) await File.Create(_customBroadcastIpsPath).DisposeAsync().ConfigureAwait(false); @@ -274,7 +283,8 @@ namespace GoldbergGUI.Core.Services } // create steam_appid.txt - await File.WriteAllTextAsync(Path.Combine(path, "steam_appid.txt"), c.AppId.ToString()).ConfigureAwait(false); + await File.WriteAllTextAsync(Path.Combine(path, "steam_appid.txt"), c.AppId.ToString()) + .ConfigureAwait(false); // DLC if (c.DlcList.Count > 0) @@ -366,16 +376,24 @@ namespace GoldbergGUI.Core.Services var match = regex.Match(body); if (File.Exists(jobIdPath)) { - _log.Info("Check if update is needed..."); - var jobIdLocal = Convert.ToInt32(File.ReadLines(jobIdPath).First().Trim()); - var jobIdRemote = Convert.ToInt32(match.Groups["jobid"].Value); - _log.Debug($"job_id: local {jobIdLocal}; remote {jobIdRemote}"); - if (jobIdLocal.Equals(jobIdRemote)) + try { - _log.Info("Latest Goldberg emulator is already available! Skipping..."); - return false; + _log.Info("Check if update is needed..."); + var jobIdLocal = Convert.ToInt32(File.ReadLines(jobIdPath).First().Trim()); + var jobIdRemote = Convert.ToInt32(match.Groups["jobid"].Value); + _log.Debug($"job_id: local {jobIdLocal}; remote {jobIdRemote}"); + if (jobIdLocal.Equals(jobIdRemote)) + { + _log.Info("Latest Goldberg emulator is already available! Skipping..."); + return false; + } + } + catch (Exception) + { + _log.Error("An error occured, local Goldberg setup might be broken!"); } } + _log.Info("Starting download..."); await StartDownload(match.Value).ConfigureAwait(false); return true; @@ -408,23 +426,44 @@ namespace GoldbergGUI.Core.Services // Extract all from archive to subfolder ./goldberg/ private async Task Extract(string archivePath) { + var errorOccured = false; _log.Debug("Start extraction..."); - await Task.Run(() => + Directory.Delete(_goldbergPath, true); + Directory.CreateDirectory(_goldbergPath); + using (var archive = await Task.Run(() => ZipFile.OpenRead(archivePath)).ConfigureAwait(false)) { - try + foreach (var entry in archive.Entries) { - Directory.Delete(_goldbergPath, true); - ZipFile.ExtractToDirectory(archivePath, _goldbergPath); + await Task.Run(() => + { + try + { + var fullPath = Path.Combine(_goldbergPath, entry.FullName); + if (string.IsNullOrEmpty(entry.Name)) + { + Directory.CreateDirectory(fullPath); + } + else + { + entry.ExtractToFile(fullPath, true); + } + } + catch (Exception e) + { + errorOccured = true; + _log.Error($"Error while trying to extract {entry.FullName}"); + _log.Error(e.ToString); + } + }).ConfigureAwait(false); } - catch (Exception e) - { - ShowErrorMessage(); - _log.Error(e.ToString); - Environment.Exit(1); - // throw; - } - }).ConfigureAwait(false); - _log.Debug("Extraction done!"); + } + + if (errorOccured) + { + ShowErrorMessage(); + _log.Warn("Error occured while extraction! Please setup Goldberg manually"); + } + _log.Info("Archive extracted successfully!"); } private void ShowErrorMessage() @@ -433,6 +472,7 @@ namespace GoldbergGUI.Core.Services { Directory.Delete(_goldbergPath, true); } + Directory.CreateDirectory(_goldbergPath); MessageBox.Show("Could not setup Goldberg Emulator!\n" + "Please download it manually and extract its content into the \"goldberg\" subfolder!"); From 373876c0743b4cde16e7a6aef75ad79115321ffa Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Wed, 7 Apr 2021 14:43:39 +0200 Subject: [PATCH 09/24] Downloaded Goldberg archive will now be validated by checking its file size. --- GoldbergGUI.Core/Services/GoldbergService.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index a6f5617..6beb4bf 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -407,11 +407,24 @@ namespace GoldbergGUI.Core.Services _log.Debug(downloadUrl); await using var fileStream = File.OpenWrite(_goldbergZipPath); //client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead) + var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, downloadUrl); + var headResponse = await client.SendAsync(httpRequestMessage).ConfigureAwait(false); + var contentLength = headResponse.Content.Headers.ContentLength; var task = client.GetFileAsync(downloadUrl, fileStream).ConfigureAwait(false); await task; if (task.GetAwaiter().IsCompleted) { - _log.Info("Download finished!"); + await fileStream.DisposeAsync().ConfigureAwait(false); + var fileLength = new FileInfo(_goldbergZipPath).Length; + // Environment.Exit(128); + if (contentLength == fileLength) + { + _log.Info("Download finished!"); + } + else + { + throw new Exception("File size does not match!"); + } } } catch (Exception e) @@ -463,7 +476,7 @@ namespace GoldbergGUI.Core.Services ShowErrorMessage(); _log.Warn("Error occured while extraction! Please setup Goldberg manually"); } - _log.Info("Archive extracted successfully!"); + _log.Info("Extraction was successful!"); } private void ShowErrorMessage() From e4dfd38dd355faccbbd6ec7d92db8be7555206aa Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Wed, 7 Apr 2021 14:51:49 +0200 Subject: [PATCH 10/24] Improved log verbosity. --- GoldbergGUI.Core/Services/GoldbergService.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index 6beb4bf..7ae2c3c 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -113,7 +113,8 @@ namespace GoldbergGUI.Core.Services !long.TryParse(File.ReadLines(_userSteamIdPath).First().Trim(), out steamId) && steamId < 76561197960265729 && steamId > 76561202255233023) { - _log.Error("Invalid User Steam ID!"); + _log.Error("Invalid User Steam ID! Using default Steam ID..."); + steamId = DefaultSteamId; } if (File.Exists(_languagePath)) language = File.ReadLines(_languagePath).First().Trim(); @@ -121,6 +122,7 @@ namespace GoldbergGUI.Core.Services customBroadcastIps.AddRange( File.ReadLines(_customBroadcastIpsPath).Select(line => line.Trim())); }).ConfigureAwait(false); + _log.Info("Got global settings."); return new GoldbergGlobalConfiguration { AccountName = accountName, @@ -200,6 +202,7 @@ namespace GoldbergGUI.Core.Services _log.Info("Empty list of custom broadcast IPs! Skipping..."); await Task.Run(() => File.Delete(_customBroadcastIpsPath)).ConfigureAwait(false); } + _log.Info("Setting global configuration finished."); } // If first time, call GenerateInterfaces @@ -410,9 +413,7 @@ namespace GoldbergGUI.Core.Services var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, downloadUrl); var headResponse = await client.SendAsync(httpRequestMessage).ConfigureAwait(false); var contentLength = headResponse.Content.Headers.ContentLength; - var task = client.GetFileAsync(downloadUrl, fileStream).ConfigureAwait(false); - await task; - if (task.GetAwaiter().IsCompleted) + await client.GetFileAsync(downloadUrl, fileStream).ContinueWith(async t => { await fileStream.DisposeAsync().ConfigureAwait(false); var fileLength = new FileInfo(_goldbergZipPath).Length; @@ -425,7 +426,7 @@ namespace GoldbergGUI.Core.Services { throw new Exception("File size does not match!"); } - } + }).ConfigureAwait(false); } catch (Exception e) { From e17b0a18caebf3f43820ed881125439b514d9901 Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Wed, 28 Apr 2021 13:33:04 +0200 Subject: [PATCH 11/24] Changed how DLCs are referred as. Optional DLC settings can now be shown (loading and saving is not implemented yet) --- GoldbergGUI.Core/Models/GoldbergModel.cs | 29 +++++--------------- GoldbergGUI.Core/Services/GoldbergService.cs | 4 +-- GoldbergGUI.Core/Services/SteamService.cs | 12 ++++---- GoldbergGUI.Core/ViewModels/MainViewModel.cs | 14 +++++----- GoldbergGUI.WPF/Views/MainView.xaml | 10 +++++-- 5 files changed, 30 insertions(+), 39 deletions(-) diff --git a/GoldbergGUI.Core/Models/GoldbergModel.cs b/GoldbergGUI.Core/Models/GoldbergModel.cs index 58e0f89..40972bc 100644 --- a/GoldbergGUI.Core/Models/GoldbergModel.cs +++ b/GoldbergGUI.Core/Models/GoldbergModel.cs @@ -32,13 +32,13 @@ namespace GoldbergGUI.Core.Models /// /// List of DLC /// - public List DlcList { get; set; } + public List DlcList { get; set; } - public List Depots { 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; } @@ -65,20 +65,11 @@ namespace GoldbergGUI.Core.Models public GoldbergGlobalConfiguration OverwrittenGlobalConfiguration { get; set; } } - public class Depot + public class DlcApp : SteamApp { - /// - /// ID of Depot. - /// - 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 int? DepotId { get; set; } + public string DepotName { get; set; } + public string AppPath { get; set; } } public class Group @@ -97,12 +88,6 @@ 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 { /// diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index aae4872..2d84f36 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -207,7 +207,7 @@ namespace GoldbergGUI.Core.Services { _log.Info("Reading configuration..."); var appId = -1; - var dlcList = new List(); + var dlcList = new List(); var steamAppidTxt = Path.Combine(path, "steam_appid.txt"); if (File.Exists(steamAppidTxt)) { @@ -235,7 +235,7 @@ namespace GoldbergGUI.Core.Services { AppId = Convert.ToInt32(match.Groups["id"].Value), Name = match.Groups["name"].Value - }); + } as DlcApp); } } else diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index b002d71..f3042d7 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -23,7 +23,7 @@ namespace GoldbergGUI.Core.Services public Task> GetListOfAppsByName(string name); public Task GetAppByName(string name); public Task GetAppById(int appid); - public Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb); + public Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb); } class SteamCache @@ -168,9 +168,9 @@ namespace GoldbergGUI.Core.Services return app; } - public async Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb) + 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}"); @@ -183,7 +183,7 @@ namespace GoldbergGUI.Core.Services var result = await _db.Table().Where(z => z.type == AppTypeDlc) .FirstOrDefaultAsync(y => y.AppId.Equals(x)).ConfigureAwait(true) ?? new SteamApp {AppId = x, Name = $"Unknown DLC {x}"}; - dlcList.Add(result); + dlcList.Add(result as DlcApp); _log.Debug($"{result.AppId}={result.Name}"); }); @@ -233,11 +233,11 @@ namespace GoldbergGUI.Core.Services var i = dlcList.FindIndex(x => x.AppId.Equals(dlcApp.AppId)); if (i > -1) { - if (dlcList[i].Name.Contains("Unknown DLC")) dlcList[i] = dlcApp; + if (dlcList[i].Name.Contains("Unknown DLC")) dlcList[i] = dlcApp as DlcApp; } else { - dlcList.Add(dlcApp); + dlcList.Add(dlcApp as DlcApp); } } diff --git a/GoldbergGUI.Core/ViewModels/MainViewModel.cs b/GoldbergGUI.Core/ViewModels/MainViewModel.cs index 6f71093..b99a2cc 100644 --- a/GoldbergGUI.Core/ViewModels/MainViewModel.cs +++ b/GoldbergGUI.Core/ViewModels/MainViewModel.cs @@ -30,7 +30,7 @@ namespace GoldbergGUI.Core.ViewModels private int _appId; //private SteamApp _currentGame; - private ObservableCollection _dlcs; + private ObservableCollection _dlcs; private string _accountName; private long _steamId; private bool _offline; @@ -130,7 +130,7 @@ namespace GoldbergGUI.Core.ViewModels } // ReSharper disable once InconsistentNaming - public ObservableCollection DLCs + public ObservableCollection DLCs { get => _dlcs; set @@ -382,7 +382,7 @@ namespace GoldbergGUI.Core.ViewModels StatusText = "Trying to get list of DLCs..."; 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) { @@ -480,7 +480,7 @@ namespace GoldbergGUI.Core.ViewModels 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 SteamApp + where match.Success select new DlcApp { AppId = Convert.ToInt32(match.Groups["id"].Value), Name = match.Groups["name"].Value @@ -488,7 +488,7 @@ namespace GoldbergGUI.Core.ViewModels 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"; @@ -524,7 +524,7 @@ namespace GoldbergGUI.Core.ViewModels DllPath = "Path to game's steam_api(64).dll..."; GameName = "Game name..."; AppId = -1; - DLCs = new ObservableCollection(); + DLCs = new ObservableCollection(); AccountName = "Account name..."; SteamId = -1; Offline = false; @@ -544,7 +544,7 @@ namespace GoldbergGUI.Core.ViewModels private void SetFormFromConfig(GoldbergConfiguration config) { AppId = config.AppId; - DLCs = new ObservableCollection(config.DlcList); + DLCs = new ObservableCollection(config.DlcList); Offline = config.Offline; DisableNetworking = config.DisableNetworking; DisableOverlay = config.DisableOverlay; diff --git a/GoldbergGUI.WPF/Views/MainView.xaml b/GoldbergGUI.WPF/Views/MainView.xaml index b8d16ce..e14ebc1 100644 --- a/GoldbergGUI.WPF/Views/MainView.xaml +++ b/GoldbergGUI.WPF/Views/MainView.xaml @@ -7,6 +7,9 @@ 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 }"> + + + @@ -53,10 +56,13 @@ + + + - public List DlcList { get; set; } - //public List Depots { get; set; } + public List Depots { get; set; } public List SubscribedGroups { get; set; } @@ -67,10 +67,7 @@ namespace GoldbergGUI.Core.Models public class DlcApp : SteamApp { - /// - /// ID of depot (optional) - /// - public int? DepotId { get; set; } + //public int? DepotId { get; set; } //public string DepotName { get; set; } /// /// Path to DLC (relative to Steam API DLL) (optional) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index 1283ce9..fa4b47f 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -29,6 +29,7 @@ namespace GoldbergGUI.Core.Services } // ReSharper disable once UnusedType.Global + // ReSharper disable once ClassNeverInstantiated.Global public class GoldbergService : IGoldbergService { private IMvxLog _log; @@ -221,6 +222,7 @@ namespace GoldbergGUI.Core.Services } 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..."); @@ -237,6 +239,20 @@ namespace GoldbergGUI.Core.Services Name = match.Groups["name"].Value }); } + + // ReSharper disable once InvertIf + if (File.Exists(appPathTxt)) + { + var appPathAllLinesAsync = await File.ReadAllLinesAsync(dlcTxt).ConfigureAwait(false); + var appPathExpression = new Regex(@"(?.*) *= *(?.*)"); + foreach (var line in appPathAllLinesAsync) + { + var match = appPathExpression.Match(line); + if (match.Success) + dlcList[Convert.ToInt32(match.Groups["id"].Value)].AppPath = + match.Groups["appPath"].Value; + } + } } else { @@ -273,6 +289,7 @@ namespace GoldbergGUI.Core.Services { CopyDllFiles(path, x64Name); } + _log.Info("DLL setup finished!"); // Create steam_settings folder if missing _log.Info("Saving settings..."); @@ -285,50 +302,81 @@ namespace GoldbergGUI.Core.Services await File.WriteAllTextAsync(Path.Combine(path, "steam_appid.txt"), c.AppId.ToString()) .ConfigureAwait(false); - // DLC + // DLC + App path if (c.DlcList.Count > 0) { - var dlcString = ""; - c.DlcList.ForEach(x => dlcString += $"{x}\n"); - await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "DLC.txt"), dlcString) + _log.Info("Saving DLC settings..."); + var dlcContent = ""; + //var depotContent = ""; + var appPathContent = ""; + c.DlcList.ForEach(x => + { + dlcContent += $"{x}\n"; + //depotContent += $"{x.DepotId}\n"; + appPathContent += $"{x.AppId}={x.AppPath}\n"; + }); + await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "DLC.txt"), dlcContent) .ConfigureAwait(false); + + /*if (!string.Equals(depotContent, "")) + { + await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "depots.txt"), depotContent) + .ConfigureAwait(false); + }*/ + + if (!string.Equals(appPathContent, "")) + { + await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "app_paths.txt"), appPathContent) + .ConfigureAwait(false); + } + _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")); } } @@ -337,17 +385,21 @@ 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); } @@ -411,6 +463,7 @@ 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); diff --git a/GoldbergGUI.WPF/Views/MainView.xaml b/GoldbergGUI.WPF/Views/MainView.xaml index 69cdf2d..818b49c 100644 --- a/GoldbergGUI.WPF/Views/MainView.xaml +++ b/GoldbergGUI.WPF/Views/MainView.xaml @@ -56,12 +56,12 @@ - - - + + - + [Column("type")] - public string type { get; set; } + public string AppType { get; set; } public override string ToString() { diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index 83fdf90..a80ef7f 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -128,7 +128,7 @@ namespace GoldbergGUI.Core.Services var cache = new HashSet(); foreach (var steamApp in cacheRaw) { - steamApp.type = steamCache.SteamAppType; + steamApp.AppType = steamCache.SteamAppType; steamApp.ComparableName = PrepareStringToCompare(steamApp.Name); cache.Add(steamApp); } @@ -141,7 +141,7 @@ namespace GoldbergGUI.Core.Services public async Task> GetListOfAppsByName(string name) { var query = await _db.Table() - .Where(x => x.type == AppTypeGame).ToListAsync().ConfigureAwait(false); + .Where(x => x.AppType == AppTypeGame).ToListAsync().ConfigureAwait(false); var listOfAppsByName = query.Search(x => x.Name) .SetCulture(StringComparison.OrdinalIgnoreCase) .ContainingAll(name.Split(' ')); @@ -153,7 +153,7 @@ namespace GoldbergGUI.Core.Services _log.Info($"Trying to get app {name}"); var comparableName = PrepareStringToCompare(name); var app = await _db.Table() - .FirstOrDefaultAsync(x => x.type == AppTypeGame && x.ComparableName.Equals(comparableName)) + .FirstOrDefaultAsync(x => x.AppType == AppTypeGame && x.ComparableName.Equals(comparableName)) .ConfigureAwait(false); if (app != null) _log.Info($"Successfully got app {app}"); return app; @@ -162,7 +162,7 @@ namespace GoldbergGUI.Core.Services public async Task GetAppById(int appid) { _log.Info($"Trying to get app with ID {appid}"); - var app = await _db.Table().Where(x => x.type == AppTypeGame) + var app = await _db.Table().Where(x => x.AppType == AppTypeGame) .FirstOrDefaultAsync(x => x.AppId.Equals(appid)).ConfigureAwait(false); if (app != null) _log.Info($"Successfully got app {app}"); return app; @@ -180,10 +180,10 @@ namespace GoldbergGUI.Core.Services { steamAppDetails.DLC.ForEach(async x => { - var result = await _db.Table().Where(z => z.type == AppTypeDlc) + var result = await _db.Table().Where(z => z.AppType == AppTypeDlc) .FirstOrDefaultAsync(y => y.AppId.Equals(x)).ConfigureAwait(true) - ?? new DlcApp() {AppId = x, Name = $"Unknown DLC {x}"}; - dlcList.Add(result); + ?? new SteamApp() { AppId = x, Name = $"Unknown DLC {x}", ComparableName = $"unknownDlc{x}", AppType = AppTypeDlc }; + dlcList.Add(new DlcApp(result)); _log.Debug($"{result.AppId}={result.Name}"); }); From 71021ee767d4657cf46677d2417770bd7c9689e8 Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Wed, 22 Dec 2021 13:15:28 +0100 Subject: [PATCH 16/24] code cleanup; build x86 per default --- GoldbergGUI.Core/GoldbergGUI.Core.csproj | 1 + GoldbergGUI.Core/Models/GoldbergModel.cs | 46 +++++++-------- GoldbergGUI.Core/Models/SteamAppModel.cs | 2 +- GoldbergGUI.Core/Services/GoldbergService.cs | 14 ++--- GoldbergGUI.Core/Services/SteamService.cs | 20 +++---- GoldbergGUI.Core/Utils/CustomMvxAppStart.cs | 3 +- GoldbergGUI.Core/Utils/Misc.cs | 6 +- GoldbergGUI.Core/ViewModels/MainViewModel.cs | 56 ++++++++++--------- .../ViewModels/SearchResultViewModel.cs | 10 ++-- GoldbergGUI.WPF/App.xaml.cs | 1 - GoldbergGUI.WPF/AssemblyInfo.cs | 8 +-- GoldbergGUI.WPF/GoldbergGUI.WPF.csproj | 1 + GoldbergGUI.WPF/Setup.cs | 9 +-- GoldbergGUI.sln | 24 +++++++- 14 files changed, 110 insertions(+), 91 deletions(-) diff --git a/GoldbergGUI.Core/GoldbergGUI.Core.csproj b/GoldbergGUI.Core/GoldbergGUI.Core.csproj index b807a17..164c22f 100644 --- a/GoldbergGUI.Core/GoldbergGUI.Core.csproj +++ b/GoldbergGUI.Core/GoldbergGUI.Core.csproj @@ -4,6 +4,7 @@ netcoreapp3.1 0.2.0 Jeddunk + AnyCPU;x86;x64 diff --git a/GoldbergGUI.Core/Models/GoldbergModel.cs b/GoldbergGUI.Core/Models/GoldbergModel.cs index b0f01d2..d6a40fe 100644 --- a/GoldbergGUI.Core/Models/GoldbergModel.cs +++ b/GoldbergGUI.Core/Models/GoldbergModel.cs @@ -35,21 +35,21 @@ namespace GoldbergGUI.Core.Models /// List of DLC /// public List DlcList { get; set; } - + public List Depots { get; set; } - + public List SubscribedGroups { 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,7 +63,7 @@ namespace GoldbergGUI.Core.Models /// Disable overlay (experimental only). /// public bool DisableOverlay { get; set; } - + public GoldbergGlobalConfiguration OverwrittenGlobalConfiguration { get; set; } } @@ -73,12 +73,12 @@ namespace GoldbergGUI.Core.Models public DlcApp(SteamApp steamApp) { - this.AppId = steamApp.AppId; - this.Name = steamApp.Name; - this.ComparableName = steamApp.ComparableName; - this.AppType = steamApp.AppType; - this.LastModified = steamApp.LastModified; - this.PriceChangeNumber = steamApp.PriceChangeNumber; + AppId = steamApp.AppId; + Name = steamApp.Name; + ComparableName = steamApp.ComparableName; + AppType = steamApp.AppType; + LastModified = steamApp.LastModified; + PriceChangeNumber = steamApp.PriceChangeNumber; } /// @@ -109,40 +109,40 @@ namespace GoldbergGUI.Core.Models /// 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. /// [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")] diff --git a/GoldbergGUI.Core/Models/SteamAppModel.cs b/GoldbergGUI.Core/Models/SteamAppModel.cs index b3ea457..4329b75 100644 --- a/GoldbergGUI.Core/Models/SteamAppModel.cs +++ b/GoldbergGUI.Core/Models/SteamAppModel.cs @@ -1,6 +1,6 @@ +using SQLite; using System.Collections.Generic; using System.Text.Json.Serialization; -using SQLite; // ReSharper disable UnusedMember.Global // ReSharper disable ClassNeverInstantiated.Global diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index 5af721b..bbe0be1 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -1,3 +1,6 @@ +using GoldbergGUI.Core.Models; +using GoldbergGUI.Core.Utils; +using MvvmCross.Logging; using System; using System.Collections.Generic; using System.IO; @@ -7,9 +10,6 @@ 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 { @@ -249,7 +249,7 @@ namespace GoldbergGUI.Core.Services { var match = appPathExpression.Match(line); if (!match.Success) continue; - var i = dlcList.FindIndex(x => + var i = dlcList.FindIndex(x => x.AppId.Equals(Convert.ToInt32(match.Groups["id"].Value))); dlcList[i].AppPath = match.Groups["appPath"].Value; } @@ -319,14 +319,14 @@ namespace GoldbergGUI.Core.Services }); await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "DLC.txt"), dlcContent) .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) diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index a80ef7f..5e79f4d 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -1,10 +1,3 @@ -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; using AngleSharp.Dom; using AngleSharp.Html.Parser; using GoldbergGUI.Core.Models; @@ -13,6 +6,13 @@ 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 { @@ -91,7 +91,7 @@ namespace GoldbergGUI.Core.Services static SteamApps DeserializeSteamApps(Type type, string cacheString) { return type == typeof(SteamAppsV2) - ? (SteamApps) JsonSerializer.Deserialize(cacheString) + ? (SteamApps)JsonSerializer.Deserialize(cacheString) : JsonSerializer.Deserialize(cacheString); } @@ -196,7 +196,7 @@ namespace GoldbergGUI.Core.Services // 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/"); @@ -229,7 +229,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 DlcApp { AppId = Convert.ToInt32(dlcId), Name = dlcName }; var i = dlcList.FindIndex(x => x.AppId.Equals(dlcApp.AppId)); if (i > -1) { diff --git a/GoldbergGUI.Core/Utils/CustomMvxAppStart.cs b/GoldbergGUI.Core/Utils/CustomMvxAppStart.cs index d5fcc3a..8fa735e 100644 --- a/GoldbergGUI.Core/Utils/CustomMvxAppStart.cs +++ b/GoldbergGUI.Core/Utils/CustomMvxAppStart.cs @@ -1,8 +1,7 @@ -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 6bf7df2..9e315d3 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 b99a2cc..6f57aae 100644 --- a/GoldbergGUI.Core/ViewModels/MainViewModel.cs +++ b/GoldbergGUI.Core/ViewModels/MainViewModel.cs @@ -1,4 +1,12 @@ -using System; +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.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; @@ -9,14 +17,6 @@ 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 { @@ -264,7 +264,7 @@ namespace GoldbergGUI.Core.ViewModels public static string AboutVersionText => FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; - + public static GlobalHelp G => new GlobalHelp(); // COMMANDS // @@ -380,7 +380,7 @@ 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); MainWindowEnabled = true; @@ -402,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); @@ -414,13 +414,13 @@ namespace GoldbergGUI.Core.ViewModels MainWindowEnabled = false; StatusText = "Saving..."; await _goldberg.Save(dirPath, new GoldbergConfiguration - { - AppId = AppId, - 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; @@ -478,13 +478,15 @@ 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 DlcApp + { + AppId = Convert.ToInt32(match.Groups["id"].Value), + Name = match.Groups["name"].Value + }).ToList(); if (pastedDlc.Count > 0) { DLCs.Clear(); diff --git a/GoldbergGUI.Core/ViewModels/SearchResultViewModel.cs b/GoldbergGUI.Core/ViewModels/SearchResultViewModel.cs index dd46194..35f4dc2 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 7b2e5c1..5d14782 100644 --- a/GoldbergGUI.WPF/App.xaml.cs +++ b/GoldbergGUI.WPF/App.xaml.cs @@ -1,5 +1,4 @@ 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 4a05c7d..4f943de 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 62b3ad1..d0e7dcb 100644 --- a/GoldbergGUI.WPF/GoldbergGUI.WPF.csproj +++ b/GoldbergGUI.WPF/GoldbergGUI.WPF.csproj @@ -6,6 +6,7 @@ true 0.2.0 Jeddunk + AnyCPU;x86;x64 diff --git a/GoldbergGUI.WPF/Setup.cs b/GoldbergGUI.WPF/Setup.cs index e11f828..c9f6d78 100644 --- a/GoldbergGUI.WPF/Setup.cs +++ b/GoldbergGUI.WPF/Setup.cs @@ -1,20 +1,17 @@ -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.sln b/GoldbergGUI.sln index 12167e8..2d2b88a 100644 --- a/GoldbergGUI.sln +++ b/GoldbergGUI.sln @@ -3,24 +3,44 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30717.126 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoldbergGUI.Core", "GoldbergGUI.Core\GoldbergGUI.Core.csproj", "{FB205F05-83DE-4D87-8CE2-F7DA320944FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GoldbergGUI.Core", "GoldbergGUI.Core\GoldbergGUI.Core.csproj", "{FB205F05-83DE-4D87-8CE2-F7DA320944FD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoldbergGUI.WPF", "GoldbergGUI.WPF\GoldbergGUI.WPF.csproj", "{84ED15D3-725C-43B1-B8C7-51759CAABBAA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GoldbergGUI.WPF", "GoldbergGUI.WPF\GoldbergGUI.WPF.csproj", "{84ED15D3-725C-43B1-B8C7-51759CAABBAA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Debug|x64.ActiveCfg = Debug|x64 + {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Debug|x64.Build.0 = Debug|x64 + {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Debug|x86.ActiveCfg = Debug|x86 + {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Debug|x86.Build.0 = Debug|x86 {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Release|Any CPU.Build.0 = Release|Any CPU + {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Release|x64.ActiveCfg = Release|x64 + {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Release|x64.Build.0 = Release|x64 + {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Release|x86.ActiveCfg = Release|x86 + {FB205F05-83DE-4D87-8CE2-F7DA320944FD}.Release|x86.Build.0 = Release|x86 {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Debug|x64.ActiveCfg = Debug|Any CPU + {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Debug|x64.Build.0 = Debug|Any CPU + {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Debug|x86.ActiveCfg = Debug|x86 + {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Debug|x86.Build.0 = Debug|x86 {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Release|Any CPU.ActiveCfg = Release|Any CPU {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Release|Any CPU.Build.0 = Release|Any CPU + {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Release|x64.ActiveCfg = Release|Any CPU + {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Release|x64.Build.0 = Release|Any CPU + {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Release|x86.ActiveCfg = Release|x86 + {84ED15D3-725C-43B1-B8C7-51759CAABBAA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 9545f83833af42b2eafdd26f2c7b7cb7fce180e7 Mon Sep 17 00:00:00 2001 From: UrbanCMC Date: Fri, 28 Jan 2022 15:50:40 +0100 Subject: [PATCH 17/24] Added support for retrieving game achievements --- GoldbergGUI.Core/Services/GoldbergService.cs | 79 ++++++++++++++++++++ GoldbergGUI.Core/Services/SteamService.cs | 28 +++++++ GoldbergGUI.Core/ViewModels/MainViewModel.cs | 30 ++++++++ GoldbergGUI.WPF/Views/MainView.xaml | 12 ++- 4 files changed, 147 insertions(+), 2 deletions(-) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index bbe0be1..6b7bcfa 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -208,6 +208,7 @@ namespace GoldbergGUI.Core.Services { _log.Info("Reading configuration..."); var appId = -1; + var achievementList = new List(); var dlcList = new List(); var steamAppidTxt = Path.Combine(path, "steam_appid.txt"); if (File.Exists(steamAppidTxt)) @@ -221,6 +222,19 @@ 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)) @@ -263,6 +277,7 @@ 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")), @@ -303,6 +318,53 @@ 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 if (c.DlcList.Count > 0) { @@ -621,5 +683,22 @@ 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 5e79f4d..74c369b 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -23,6 +23,7 @@ namespace GoldbergGUI.Core.Services 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); } @@ -81,6 +82,7 @@ namespace GoldbergGUI.Core.Services 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; @@ -168,6 +170,32 @@ namespace GoldbergGUI.Core.Services return app; } + public async Task> GetListOfAchievements(SteamApp steamApp) + { + 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(); diff --git a/GoldbergGUI.Core/ViewModels/MainViewModel.cs b/GoldbergGUI.Core/ViewModels/MainViewModel.cs index 6f57aae..7eb9993 100644 --- a/GoldbergGUI.Core/ViewModels/MainViewModel.cs +++ b/GoldbergGUI.Core/ViewModels/MainViewModel.cs @@ -30,6 +30,7 @@ namespace GoldbergGUI.Core.ViewModels private int _appId; //private SteamApp _currentGame; + private List achievements; private ObservableCollection _dlcs; private string _accountName; private long _steamId; @@ -368,6 +369,32 @@ namespace GoldbergGUI.Core.ViewModels 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..."; + achievements = await _steam.GetListOfAchievements(new SteamApp { AppId = AppId, Name = GameName }); + 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() @@ -416,6 +443,7 @@ namespace GoldbergGUI.Core.ViewModels await _goldberg.Save(dirPath, new GoldbergConfiguration { AppId = AppId, + Achievements = achievements.ToList(), DlcList = DLCs.ToList(), Offline = Offline, DisableNetworking = DisableNetworking, @@ -526,6 +554,7 @@ namespace GoldbergGUI.Core.ViewModels DllPath = "Path to game's steam_api(64).dll..."; GameName = "Game name..."; AppId = -1; + achievements = new List(); DLCs = new ObservableCollection(); AccountName = "Account name..."; SteamId = -1; @@ -546,6 +575,7 @@ namespace GoldbergGUI.Core.ViewModels private void SetFormFromConfig(GoldbergConfiguration config) { AppId = config.AppId; + achievements = new List(config.Achievements); DLCs = new ObservableCollection(config.DlcList); Offline = config.Offline; DisableNetworking = config.DisableNetworking; diff --git a/GoldbergGUI.WPF/Views/MainView.xaml b/GoldbergGUI.WPF/Views/MainView.xaml index 818b49c..13b5ccb 100644 --- a/GoldbergGUI.WPF/Views/MainView.xaml +++ b/GoldbergGUI.WPF/Views/MainView.xaml @@ -61,8 +61,16 @@ - -