diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bd149f0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "SteamStorefrontAPI"] + path = SteamStorefrontAPI + url = https://git.jeddunk.xyz/jeddunk/SteamStorefrontAPI.git diff --git a/README.md b/README.md index 0ec37d3..ae07184 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ # Auto-CreamAPI 2 +[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H4330U3) Set your game automatically up for use with CreamAPI. -[![](https://jeddunk.xyz/jetbrains-small.png)](https://www.jetbrains.com/?from=Auto-CreamAPI) -[Made with software provided by JetBrains s.r.o.](https://www.jetbrains.com/?from=Auto-CreamAPI) - ## Features * Setup CreamAPI’s DLLs and configuration file automatically. * Find the AppID by providing the game’s name without having to look it up manually. @@ -12,11 +10,9 @@ Set your game automatically up for use with CreamAPI. * Set flags like "offline mode" and "extra protection" and select a language from a list. ## Installation - Download the latest release and extract it into any folder (e.g. `%USERPROFILE%\Desktop\auto-creamapi`). ## Usage - * Double-click `auto-creamapi.exe` to open the application. (When starting it for the first time, it might take a few seconds since it needs to cache a list of games available on the Steam Store and download the latest CreamAPI DLL files.) * Click on the *Open File* button on the top right and select the *steam_api.dll* or *steam_api64.dll* @@ -29,9 +25,13 @@ Download the latest release and extract it into any folder (e.g. `%USERPROFILE%\ * Click on *"Save"*. ## License - Auto-CreamAPI itself is licensed under the GNU General Public License v3.0 -CreamAPI © 2016-2019, deadmau5. All Rights Reserved. +CreamAPI © 2016-2020, deadmau5. All Rights Reserved. *Dependencies will be listed ASAP.* + +## Software used + +[![](https://jeddunk.xyz/jetbrains-small.png)](https://www.jetbrains.com/?from=Auto-CreamAPI) +[Made with software provided by JetBrains s.r.o.](https://www.jetbrains.com/?from=Auto-CreamAPI) diff --git a/auto-creamapi.sln b/auto-creamapi.sln index c220862..78be129 100644 --- a/auto-creamapi.sln +++ b/auto-creamapi.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30413.136 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "auto-creamapi", "auto-creamapi\auto-creamapi.csproj", "{26060B32-199E-4366-8FDE-6B1E10E0EF62}" EndProject diff --git a/auto-creamapi/App.xaml.cs b/auto-creamapi/App.xaml.cs index 95e07c3..d78cbe5 100644 --- a/auto-creamapi/App.xaml.cs +++ b/auto-creamapi/App.xaml.cs @@ -10,7 +10,7 @@ namespace auto_creamapi { protected override void RegisterSetup() { - this.RegisterSetupType>(); + this.RegisterSetupType(); } } } \ No newline at end of file diff --git a/auto-creamapi/Converters/ListOfDLcToStringConverter.cs b/auto-creamapi/Converters/ListOfDLcToStringConverter.cs index aabfdd6..5691ee6 100644 --- a/auto-creamapi/Converters/ListOfDLcToStringConverter.cs +++ b/auto-creamapi/Converters/ListOfDLcToStringConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IO; @@ -19,6 +20,7 @@ namespace auto_creamapi.Converters protected override string Convert(ObservableCollection value, Type targetType, object parameter, CultureInfo culture) { + if (value == null) return ""; MyLogger.Log.Debug("ListOfDLcToStringConverter: Convert"); var dlcListToString = DlcListToString(value); return dlcListToString.GetType() == targetType ? dlcListToString : ""; @@ -29,30 +31,32 @@ namespace auto_creamapi.Converters { MyLogger.Log.Debug("ListOfDLcToStringConverter: ConvertBack"); var stringToDlcList = StringToDlcList(value); - return stringToDlcList.GetType() == targetType ? stringToDlcList : new ObservableCollection(); + return stringToDlcList.GetType() == targetType ? stringToDlcList : []; } private static ObservableCollection StringToDlcList(string value) { var result = new ObservableCollection(); - var expression = new Regex(@"(?.*) *= *(?.*)"); + var expression = new Regex("(?.*) *= *(?.*)"); using var reader = new StringReader(value); string line; while ((line = reader.ReadLine()) != null) { var match = expression.Match(line); if (match.Success) + { result.Add(new SteamApp { AppId = int.Parse(match.Groups["id"].Value), Name = match.Groups["name"].Value }); + } } return result; } - private static string DlcListToString(ObservableCollection value) + private static string DlcListToString(IEnumerable value) { var result = ""; //value.ForEach(x => result += $"{x}\n"); diff --git a/auto-creamapi/Core/App.cs b/auto-creamapi/Core/MainApplication.cs similarity index 90% rename from auto-creamapi/Core/App.cs rename to auto-creamapi/Core/MainApplication.cs index f64d409..2201b8a 100644 --- a/auto-creamapi/Core/App.cs +++ b/auto-creamapi/Core/MainApplication.cs @@ -4,7 +4,7 @@ using MvvmCross.ViewModels; namespace auto_creamapi.Core { - public class App : MvxApplication + public class MainApplication : MvxApplication { public override void Initialize() { diff --git a/auto-creamapi/MainWindow.xaml b/auto-creamapi/MainWindow.xaml index ffcc975..7212a0c 100644 --- a/auto-creamapi/MainWindow.xaml +++ b/auto-creamapi/MainWindow.xaml @@ -6,6 +6,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" - Title="Auto-CreamAPI 2" MinWidth="420" MinHeight="600" Width="420" Height="600"> + Title="Auto-CreamAPI 2" MinWidth="420" MinHeight="640" Width="560" Height="720"> \ No newline at end of file diff --git a/auto-creamapi/Models/CreamConfigModel.cs b/auto-creamapi/Models/CreamConfigModel.cs index 5c771e5..b48634b 100644 --- a/auto-creamapi/Models/CreamConfigModel.cs +++ b/auto-creamapi/Models/CreamConfigModel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace auto_creamapi.Models { @@ -15,6 +16,20 @@ namespace auto_creamapi.Models public bool ExtraProtection { get; set; } public bool ForceOffline { get; set; } public List DlcList { get; set; } + + public override string ToString() + { + var value = $"AppID: {AppId}\n" + + $"Language: {Language}\n" + + $"UnlockAll: {UnlockAll}\n" + + $"ExtraProtection: {ExtraProtection}\n" + + $"ForceOffline: {ForceOffline}\n" + + $"DLC ({DlcList.Count}):\n[\n"; + if (DlcList.Count > 0) + value = DlcList.Aggregate(value, (current, x) => current + $" {x.AppId}={x.Name},\n"); + value += "]"; + return value; + } } public sealed class CreamConfigModel diff --git a/auto-creamapi/Models/SteamAppModel.cs b/auto-creamapi/Models/SteamAppModel.cs index 5c85687..3f74258 100644 --- a/auto-creamapi/Models/SteamAppModel.cs +++ b/auto-creamapi/Models/SteamAppModel.cs @@ -1,24 +1,36 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +using System.Text.RegularExpressions; +using auto_creamapi.Utils; namespace auto_creamapi.Models { public class SteamApp { + private string _name; + private string _comparableName; [JsonPropertyName("appid")] public int AppId { get; set; } - [JsonPropertyName("name")] public string Name { get; set; } + [JsonPropertyName("name")] + public string Name + { + get => _name; + set + { + _name = value; + _comparableName = Regex.Replace(value, Misc.SpecialCharsRegex, "").ToLower(); + } + } + + public bool CompareName(string value) + { + return _comparableName.Equals(value); + } public override string ToString() { - //return $"AppId: {AppId}, Name: {Name}"; return $"{AppId}={Name}"; } - - public bool CompareId(SteamApp steamApp) - { - return AppId.Equals(steamApp.AppId); - } } public class AppList diff --git a/auto-creamapi/README.md b/auto-creamapi/README.md index 0ec37d3..ae07184 100644 --- a/auto-creamapi/README.md +++ b/auto-creamapi/README.md @@ -1,10 +1,8 @@ # Auto-CreamAPI 2 +[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H4330U3) Set your game automatically up for use with CreamAPI. -[![](https://jeddunk.xyz/jetbrains-small.png)](https://www.jetbrains.com/?from=Auto-CreamAPI) -[Made with software provided by JetBrains s.r.o.](https://www.jetbrains.com/?from=Auto-CreamAPI) - ## Features * Setup CreamAPI’s DLLs and configuration file automatically. * Find the AppID by providing the game’s name without having to look it up manually. @@ -12,11 +10,9 @@ Set your game automatically up for use with CreamAPI. * Set flags like "offline mode" and "extra protection" and select a language from a list. ## Installation - Download the latest release and extract it into any folder (e.g. `%USERPROFILE%\Desktop\auto-creamapi`). ## Usage - * Double-click `auto-creamapi.exe` to open the application. (When starting it for the first time, it might take a few seconds since it needs to cache a list of games available on the Steam Store and download the latest CreamAPI DLL files.) * Click on the *Open File* button on the top right and select the *steam_api.dll* or *steam_api64.dll* @@ -29,9 +25,13 @@ Download the latest release and extract it into any folder (e.g. `%USERPROFILE%\ * Click on *"Save"*. ## License - Auto-CreamAPI itself is licensed under the GNU General Public License v3.0 -CreamAPI © 2016-2019, deadmau5. All Rights Reserved. +CreamAPI © 2016-2020, deadmau5. All Rights Reserved. *Dependencies will be listed ASAP.* + +## Software used + +[![](https://jeddunk.xyz/jetbrains-small.png)](https://www.jetbrains.com/?from=Auto-CreamAPI) +[Made with software provided by JetBrains s.r.o.](https://www.jetbrains.com/?from=Auto-CreamAPI) diff --git a/auto-creamapi/Services/CacheService.cs b/auto-creamapi/Services/CacheService.cs index 3d18e68..223bbe5 100644 --- a/auto-creamapi/Services/CacheService.cs +++ b/auto-creamapi/Services/CacheService.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.Linq; using System.Net.Http; using System.Text; using System.Text.Json; @@ -17,15 +19,13 @@ namespace auto_creamapi.Services { public interface ICacheService { - public List Languages { get; } - public Task Initialize(); //public Task UpdateCache(); public IEnumerable GetListOfAppsByName(string name); public SteamApp GetAppByName(string name); public SteamApp GetAppById(int appid); - public Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb); + public Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb, bool ignoreUnknown); } public class CacheService : ICacheService @@ -33,31 +33,7 @@ namespace auto_creamapi.Services private const string CachePath = "steamapps.json"; private const string SteamUri = "https://api.steampowered.com/ISteamApps/GetAppList/v2/"; - 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 SpecialCharsRegex = "[^0-9a-zA-Z]+"; - - private List _cache = new List(); - - /*private static readonly Lazy Lazy = - new Lazy(() => new CacheService()); - - public static CacheService Instance => Lazy.Value;*/ - - public CacheService() - { - Languages = Misc.DefaultLanguages; - } - - /*public async void Initialize() - { - //Languages = _defaultLanguages; - await UpdateCache(); - }*/ - - public List Languages { get; } + private HashSet _cache = []; public async Task Initialize() { @@ -66,17 +42,7 @@ namespace auto_creamapi.Services string cacheString; if (updateNeeded) { - MyLogger.Log.Information("Getting content from API..."); - var client = new HttpClient(); - var httpCall = client.GetAsync(SteamUri); - var response = await httpCall; - var readAsStringAsync = response.Content.ReadAsStringAsync(); - var responseBody = await readAsStringAsync; - MyLogger.Log.Information("Got content from API successfully. Writing to file..."); - - await File.WriteAllTextAsync(CachePath, responseBody, Encoding.UTF8); - cacheString = responseBody; - MyLogger.Log.Information("Cache written to file successfully."); + cacheString = await UpdateCache().ConfigureAwait(false); } else { @@ -84,12 +50,27 @@ namespace auto_creamapi.Services // ReSharper disable once MethodHasAsyncOverload cacheString = File.ReadAllText(CachePath); } - var steamApps = JsonSerializer.Deserialize(cacheString); - _cache = steamApps.AppList.Apps; + _cache = new HashSet(steamApps.AppList.Apps); MyLogger.Log.Information("Loaded cache into memory!"); } + private static async Task UpdateCache() + { + MyLogger.Log.Information("Getting content from API..."); + var client = new HttpClient(); + var httpCall = client.GetAsync(SteamUri); + var response = await httpCall.ConfigureAwait(false); + var readAsStringAsync = response.Content.ReadAsStringAsync(); + var responseBody = await readAsStringAsync.ConfigureAwait(false); + MyLogger.Log.Information("Got content from API successfully. Writing to file..."); + + await File.WriteAllTextAsync(CachePath, responseBody, Encoding.UTF8).ConfigureAwait(false); + var cacheString = responseBody; + MyLogger.Log.Information("Cache written to file successfully."); + return cacheString; + } + public IEnumerable GetListOfAppsByName(string name) { var listOfAppsByName = _cache.Search(x => x.Name) @@ -100,107 +81,149 @@ namespace auto_creamapi.Services public SteamApp GetAppByName(string name) { - MyLogger.Log.Information($"Trying to get app {name}"); - var app = _cache.Find(x => - Regex.Replace(x.Name, SpecialCharsRegex, "").ToLower() - .Equals(Regex.Replace(name, SpecialCharsRegex, "").ToLower())); - if (app != null) MyLogger.Log.Information($"Successfully got app {app}"); + MyLogger.Log.Information("Trying to get app {Name}", name); + var comparableName = Regex.Replace(name, Misc.SpecialCharsRegex, "").ToLower(); + var app = _cache.FirstOrDefault(x => x.CompareName(comparableName)); + if (app != null) MyLogger.Log.Information("Successfully got app {App}", app); return app; } public SteamApp GetAppById(int appid) { - MyLogger.Log.Information($"Trying to get app with ID {appid}"); - var app = _cache.Find(x => x.AppId.Equals(appid)); - if (app != null) MyLogger.Log.Information($"Successfully got app {app}"); + MyLogger.Log.Information("Trying to get app with ID {AppId}", appid); + var app = _cache.FirstOrDefault(x => x.AppId.Equals(appid)); + if (app != null) MyLogger.Log.Information("Successfully got app {App}", app); return app; } - public async Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb) + public async Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb, bool ignoreUnknown) { - MyLogger.Log.Information("Get DLC"); + MyLogger.Log.Debug("Start: GetListOfDlc"); var dlcList = new List(); - if (steamApp != null) + try { - var task = AppDetails.GetAsync(steamApp.AppId); - var steamAppDetails = await task; - steamAppDetails?.DLC.ForEach(x => + if (steamApp != null) { - var result = _cache.Find(y => y.AppId.Equals(x)) ?? - new SteamApp {AppId = x, Name = $"Unknown DLC {x}"}; - dlcList.Add(result); - }); - - dlcList.ForEach(x => MyLogger.Log.Debug($"{x.AppId}={x.Name}")); - MyLogger.Log.Information("Got DLC successfully..."); - - // Get DLC from SteamDB - // Get Cloudflare cookie - // Scrape and parse HTML page - // Add missing to DLC list - if (useSteamDb) - { - var steamDbUri = new Uri($"https://steamdb.info/app/{steamApp.AppId}/dlc/"); - - /* var handler = new ClearanceHandler(); - - var client = new HttpClient(handler); - - var content = client.GetStringAsync(steamDbUri).Result; - MyLogger.Log.Debug(content); */ - - var client = new HttpClient(); - client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent); - - MyLogger.Log.Information("Get SteamDB App"); - var httpCall = client.GetAsync(steamDbUri); - var response = await httpCall; - MyLogger.Log.Debug(httpCall.Status.ToString()); - MyLogger.Log.Debug(response.EnsureSuccessStatusCode().ToString()); - - var readAsStringAsync = response.Content.ReadAsStringAsync(); - var responseBody = await readAsStringAsync; - MyLogger.Log.Debug(readAsStringAsync.Status.ToString()); - - var parser = new HtmlParser(); - var doc = parser.ParseDocument(responseBody); - // Console.WriteLine(doc.DocumentElement.OuterHtml); - - var query1 = doc.QuerySelector("#dlc"); - if (query1 != null) + var steamAppDetails = await AppDetails.GetAsync(steamApp.AppId).ConfigureAwait(false); + if (steamAppDetails != null) { - var query2 = query1.QuerySelectorAll(".app"); - foreach (var element in query2) + MyLogger.Log.Debug("Type for Steam App {Name}: \"{Type}\"", steamApp.Name, + steamAppDetails.Type); + if (steamAppDetails.Type == "game" || steamAppDetails.Type == "demo") { - var dlcId = element.GetAttribute("data-appid"); - var dlcName = $"Unknown DLC {dlcId}"; - var query3 = element.QuerySelectorAll("td"); - if (query3 != null) dlcName = query3[1].Text().Replace("\n", "").Trim(); - - var dlcApp = new SteamApp {AppId = Convert.ToInt32(dlcId), Name = dlcName}; - var i = dlcList.FindIndex(x => x.CompareId(dlcApp)); - if (i > -1) + steamAppDetails.DLC.ForEach(x => { - if (dlcList[i].Name.Contains("Unknown DLC")) dlcList[i] = dlcApp; + var result = _cache.FirstOrDefault(y => y.AppId.Equals(x)); + if (result == null) return; + var dlcDetails = AppDetails.GetAsync(x).Result; + dlcList.Add(dlcDetails != null + ? new SteamApp { AppId = dlcDetails.SteamAppId, Name = dlcDetails.Name } + : new SteamApp { AppId = x, Name = $"Unknown DLC {x}" }); + }); + + dlcList.ForEach(x => MyLogger.Log.Debug("{AppId}={Name}", x.AppId, x.Name)); + MyLogger.Log.Information("Got DLC successfully..."); + + // Return if Steam DB is deactivated + if (!useSteamDb) return dlcList; + + string steamDbUrl = $"https://steamdb.info/app/{steamApp.AppId}/dlc/"; + + var client = new HttpClient(); + string archiveJson = await client.GetStringAsync($"https://archive.org/wayback/available?url={steamDbUrl}"); + var archiveResult = JsonSerializer.Deserialize(archiveJson); + + if (archiveResult == null || archiveResult.ArchivedSnapshots.Closest?.Status != "200") + { + return dlcList; + } + + //language=regex + const string pattern = @"^(https?:\/\/web\.archive\.org\/web\/\d+)(\/.+)$"; + const string substitution = "$1id_$2"; + const RegexOptions options = RegexOptions.Multiline; + + Regex regex = new(pattern, options); + string newUrl = regex.Replace(archiveResult.ArchivedSnapshots.Closest.Url, substitution); + + //client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent); + + MyLogger.Log.Information("Get SteamDB App"); + var httpCall = client.GetAsync(newUrl); + var response = await httpCall.ConfigureAwait(false); + MyLogger.Log.Debug("{Status}", httpCall.Status.ToString()); + MyLogger.Log.Debug("{Boolean}", response.IsSuccessStatusCode.ToString()); + + response.EnsureSuccessStatusCode(); + + var readAsStringAsync = response.Content.ReadAsStringAsync(); + var responseBody = await readAsStringAsync.ConfigureAwait(false); + MyLogger.Log.Debug("{Status}", readAsStringAsync.Status.ToString()); + + var parser = new HtmlParser(); + var doc = parser.ParseDocument(responseBody); + // Console.WriteLine(doc.DocumentElement.OuterHtml); + + 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 + ? $"Unknown DLC {dlcId}" + : query3[1].Text().Replace("\n", "").Trim(); + + if (ignoreUnknown && dlcName.Contains("SteamDB Unknown App")) + { + MyLogger.Log.Information("Skipping SteamDB Unknown App {DlcId}", dlcId); + } + else + { + 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 => MyLogger.Log.Debug("{AppId}={Name}", x.AppId, x.Name)); + MyLogger.Log.Information("Got DLC from SteamDB successfully..."); } else { - dlcList.Add(dlcApp); + MyLogger.Log.Error("Could not get DLC from SteamDB!"); } } - - dlcList.ForEach(x => MyLogger.Log.Debug($"{x.AppId}={x.Name}")); - MyLogger.Log.Information("Got DLC from SteamDB successfully..."); + else + { + MyLogger.Log.Error("Could not get DLC: Steam App is not of type: \"Game\""); + } } else { - MyLogger.Log.Error("Could not get DLC from SteamDB1"); + MyLogger.Log.Error("Could not get DLC: Could not get Steam App details"); } } + else + { + MyLogger.Log.Error("Could not get DLC: Invalid Steam App"); + } + + //return dlcList; } - else + catch (Exception e) { - MyLogger.Log.Error("Could not get DLC: Invalid Steam App"); + MyLogger.Log.Error("Could not get DLC!"); + MyLogger.Log.Debug(e.Demystify(), "Exception thrown!"); } return dlcList; diff --git a/auto-creamapi/Services/CreamConfigService.cs b/auto-creamapi/Services/CreamConfigService.cs index 712e193..d21f13d 100644 --- a/auto-creamapi/Services/CreamConfigService.cs +++ b/auto-creamapi/Services/CreamConfigService.cs @@ -38,7 +38,7 @@ namespace auto_creamapi.Services bool unlockAll, bool extraProtection, bool forceOffline, - ObservableCollection dlcList); + IEnumerable dlcList); public bool ConfigExists(); } @@ -47,7 +47,7 @@ namespace auto_creamapi.Services { private string _configFilePath; - public CreamConfig Config { get; set; } + public CreamConfig Config { get; private set; } public void Initialize() { @@ -65,7 +65,7 @@ namespace auto_creamapi.Services _configFilePath = configFilePath; if (File.Exists(configFilePath)) { - MyLogger.Log.Information($"Config file found @ {configFilePath}, parsing..."); + MyLogger.Log.Information("Config file found @ {ConfigFilePath}, parsing...", configFilePath); var parser = new FileIniDataParser(); var data = parser.ReadFile(_configFilePath, Encoding.UTF8); @@ -83,7 +83,7 @@ namespace auto_creamapi.Services } else { - MyLogger.Log.Information($"Config file does not exist @ {configFilePath}, skipping..."); + MyLogger.Log.Information("Config file does not exist @ {ConfigFilePath}, skipping...", configFilePath); ResetConfigData(); } } @@ -144,7 +144,7 @@ namespace auto_creamapi.Services bool unlockAll, bool extraProtection, bool forceOffline, - ObservableCollection dlcList) + IEnumerable dlcList) { Config.AppId = appId; Config.Language = language; @@ -162,7 +162,7 @@ namespace auto_creamapi.Services private void ResetConfigData() { Config.AppId = -1; - Config.Language = ""; + Config.Language = Misc.DefaultLanguageSelection; Config.UnlockAll = false; Config.ExtraProtection = false; Config.ForceOffline = false; @@ -191,21 +191,7 @@ namespace auto_creamapi.Services public override string ToString() { - var str = $"INI file: {_configFilePath}, " + - $"AppID: {Config.AppId}, " + - $"Language: {Config.Language}, " + - $"UnlockAll: {Config.UnlockAll}, " + - $"ExtraProtection: {Config.ExtraProtection}, " + - $"ForceOffline: {Config.ForceOffline}, " + - $"DLC ({Config.DlcList.Count}):\n[\n"; - if (Config.DlcList.Count > 0) - str = Config.DlcList.Aggregate(str, (current, x) => current + $" {x.AppId}={x.Name},\n"); - /*foreach (var (key, value) in Config.DlcList) - { - str += $" {key}={value},\n"; - }*/ - - str += "]"; + var str = $"INI file: {_configFilePath}\n{Config}"; return str; } diff --git a/auto-creamapi/Services/CreamDllService.cs b/auto-creamapi/Services/CreamDllService.cs index de0470c..de3c9b7 100644 --- a/auto-creamapi/Services/CreamDllService.cs +++ b/auto-creamapi/Services/CreamDllService.cs @@ -15,6 +15,7 @@ namespace auto_creamapi.Services public void Save(); public void CheckIfDllExistsAtTarget(); public bool CreamApiApplied(); + public bool CreamApiApplied(string arch); } public class CreamDllService : ICreamDllService @@ -23,7 +24,7 @@ namespace auto_creamapi.Services private const string X64Arch = "x64"; private static readonly string HashPath = Path.Combine(Directory.GetCurrentDirectory(), "cream_api.md5"); - private readonly Dictionary _creamDlls = new Dictionary(); + private Dictionary _creamDlls; private bool _x64Exists; private bool _x86Exists; @@ -33,9 +34,11 @@ namespace auto_creamapi.Services public async Task Initialize() { MyLogger.Log.Debug("CreamDllService: Initialize begin"); - - _creamDlls.Add(X86Arch, new CreamDll("steam_api.dll", "steam_api_o.dll")); - _creamDlls.Add(X64Arch, new CreamDll("steam_api64.dll", "steam_api64_o.dll")); + _creamDlls = new Dictionary + { + {X86Arch, new CreamDll("steam_api.dll", "steam_api_o.dll")}, + {X64Arch, new CreamDll("steam_api64.dll", "steam_api64_o.dll")} + }; if (!File.Exists(HashPath)) { @@ -45,7 +48,7 @@ namespace auto_creamapi.Services { $"{_creamDlls[X86Arch].Hash} {_creamDlls[X86Arch].Filename}", $"{_creamDlls[X64Arch].Hash} {_creamDlls[X64Arch].Filename}" - }); + }).ConfigureAwait(false); } MyLogger.Log.Debug("CreamDllService: Initialize end"); @@ -59,12 +62,12 @@ namespace auto_creamapi.Services public void CheckIfDllExistsAtTarget() { - var x86file = Path.Combine(TargetPath, "steam_api.dll"); - var x64file = Path.Combine(TargetPath, "steam_api64.dll"); - _x86Exists = File.Exists(x86file); - _x64Exists = File.Exists(x64file); - if (_x86Exists) MyLogger.Log.Information($"x86 SteamAPI DLL found: {x86file}"); - if (_x64Exists) MyLogger.Log.Information($"x64 SteamAPI DLL found: {x64file}"); + var x86File = Path.Combine(TargetPath, "steam_api.dll"); + var x64File = Path.Combine(TargetPath, "steam_api64.dll"); + _x86Exists = File.Exists(x86File); + _x64Exists = File.Exists(x64File); + if (_x86Exists) MyLogger.Log.Information("x86 SteamAPI DLL found: {X}", x86File); + if (_x64Exists) MyLogger.Log.Information("x64 SteamAPI DLL found: {X}", x64File); } public bool CreamApiApplied() @@ -80,7 +83,7 @@ namespace auto_creamapi.Services var targetSteamApiDll = Path.Combine(TargetPath, _creamDlls[arch].Filename); var targetSteamApiOrigDll = Path.Combine(TargetPath, _creamDlls[arch].OrigFilename); var targetSteamApiDllBackup = Path.Combine(TargetPath, $"{_creamDlls[arch].Filename}.backup"); - MyLogger.Log.Information($"Setting up CreamAPI DLL @ {TargetPath} (arch :{arch})"); + MyLogger.Log.Information("Setting up CreamAPI DLL @ {TargetPath} (arch :{Arch})", TargetPath, arch); // Create backup of steam_api.dll File.Copy(targetSteamApiDll, targetSteamApiDllBackup, true); // Check if steam_api_o.dll already exists @@ -98,18 +101,15 @@ namespace auto_creamapi.Services return a & b; } - private string GetHash(string filename) + private static string GetHash(string filename) { - if (File.Exists(filename)) - { - using var md5 = MD5.Create(); - using var stream = File.OpenRead(filename); - return BitConverter - .ToString(md5.ComputeHash(stream)) - .Replace("-", string.Empty); - } + if (!File.Exists(filename)) return ""; + using var md5 = MD5.Create(); + using var stream = File.OpenRead(filename); + return BitConverter + .ToString(md5.ComputeHash(stream)) + .Replace("-", string.Empty); - return ""; } } } \ No newline at end of file diff --git a/auto-creamapi/Services/DownloadCreamApiService.cs b/auto-creamapi/Services/DownloadCreamApiService.cs index 34126f1..ba9f75c 100644 --- a/auto-creamapi/Services/DownloadCreamApiService.cs +++ b/auto-creamapi/Services/DownloadCreamApiService.cs @@ -10,27 +10,20 @@ using auto_creamapi.Messenger; using auto_creamapi.Utils; using HttpProgress; using MvvmCross.Plugin.Messenger; -using SharpCompress.Archives; -using SharpCompress.Common; -using SharpCompress.Readers; +using SevenZip; namespace auto_creamapi.Services { public interface IDownloadCreamApiService { - /*public void Initialize(); - public Task InitializeAsync();*/ public Task Download(string username, string password); - public void Extract(string filename); + public Task Extract(string filename); } public class DownloadCreamApiService : IDownloadCreamApiService { private const string ArchivePassword = "cs.rin.ru"; - - //private string _filename; private readonly IMvxMessenger _messenger; - //private DownloadWindow _wnd; public DownloadCreamApiService(IMvxMessenger messenger) { @@ -43,6 +36,8 @@ namespace auto_creamapi.Services var container = new CookieContainer(); var handler = new HttpClientHandler {CookieContainer = container}; var client = new HttpClient(handler); + client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) " + + "Gecko/20100101 Firefox/86.0"); var formContent = new FormUrlEncodedContent(new[] { new KeyValuePair("username", username), @@ -51,23 +46,26 @@ namespace auto_creamapi.Services new KeyValuePair("login", "login") }); MyLogger.Log.Debug("Download: post login"); - var response1 = await client.PostAsync("https://cs.rin.ru/forum/ucp.php?mode=login", formContent); - MyLogger.Log.Debug($"Login Status Code: {response1.EnsureSuccessStatusCode().StatusCode.ToString()}"); + var response1 = await client.PostAsync("https://cs.rin.ru/forum/ucp.php?mode=login", formContent) + .ConfigureAwait(false); + MyLogger.Log.Debug("Login Status Code: {StatusCode}", + response1.EnsureSuccessStatusCode().StatusCode); var cookie = container.GetCookies(new Uri("https://cs.rin.ru/forum/ucp.php?mode=login")) .FirstOrDefault(c => c.Name.Contains("_sid")); - MyLogger.Log.Debug($"Login Cookie: {cookie}"); - var response2 = await client.GetAsync("https://cs.rin.ru/forum/viewtopic.php?t=70576"); - MyLogger.Log.Debug( - $"Download Page Status Code: {response2.EnsureSuccessStatusCode().StatusCode.ToString()}"); + MyLogger.Log.Debug("Login Cookie: {Cookie}", cookie); + var response2 = await client.GetAsync("https://cs.rin.ru/forum/viewtopic.php?t=70576") + .ConfigureAwait(false); + MyLogger.Log.Debug("Download Page Status Code: {StatusCode}", + response2.EnsureSuccessStatusCode().StatusCode); var content = response2.Content.ReadAsStringAsync(); - var contentResult = await content; + var contentResult = await content.ConfigureAwait(false); var expression = new Regex(".*\\/download\\/file\\.php\\?id=.*)\">(?.*)<\\/a>.*"); using var reader = new StringReader(contentResult); string line; var archiveFileList = new Dictionary(); - while ((line = await reader.ReadLineAsync()) != null) + while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) { var match = expression.Match(line); // ReSharper disable once InvertIf @@ -75,16 +73,15 @@ namespace auto_creamapi.Services { archiveFileList.Add(match.Groups["filename"].Value, $"https://cs.rin.ru/forum{match.Groups["url"].Value}"); - MyLogger.Log.Debug(archiveFileList.LastOrDefault().Key); + MyLogger.Log.Debug("{X}", archiveFileList.LastOrDefault().Key); } } MyLogger.Log.Debug("Choosing first element from list..."); var (filename, url) = archiveFileList.FirstOrDefault(); - //filename = filename; if (File.Exists(filename)) { - MyLogger.Log.Information($"{filename} already exists, skipping download..."); + MyLogger.Log.Information("{Filename} already exists, skipping download...", filename); return filename; } @@ -93,33 +90,53 @@ namespace auto_creamapi.Services x => _messenger.Publish(new ProgressMessage(this, "Downloading...", filename, x))); await using var fileStream = File.OpenWrite(filename); var task = client.GetAsync(url, fileStream, progress); - var response = await task; + await task.ConfigureAwait(false); if (task.IsCompletedSuccessfully) _messenger.Publish(new ProgressMessage(this, "Downloading...", filename, 1.0)); MyLogger.Log.Information("Download done."); return filename; } - public void Extract(string filename) + public async Task Extract(string filename) { MyLogger.Log.Debug("Extract"); - MyLogger.Log.Information($@"Start extraction of ""{filename}""..."); - var options = new ReaderOptions {Password = ArchivePassword}; - var archive = ArchiveFactory.Open(filename, options); - var expression1 = new Regex(@"nonlog_build\\steam_api(?:64)?\.dll"); + var cwd = Directory.GetCurrentDirectory(); + const string nonlogBuild = "nonlog_build"; + const string steamApi64Dll = "steam_api64.dll"; + const string steamApiDll = "steam_api.dll"; + MyLogger.Log.Information(@"Start extraction of ""{Filename}""...", filename); + var nonlogBuildPath = Path.Combine(cwd, nonlogBuild); + if (Directory.Exists(nonlogBuildPath)) + Directory.Delete(nonlogBuildPath, true); _messenger.Publish(new ProgressMessage(this, "Extracting...", filename, 1.0)); - foreach (var entry in archive.Entries) - // ReSharper disable once InvertIf - if (!entry.IsDirectory && expression1.IsMatch(entry.Key)) - { - MyLogger.Log.Debug(entry.Key); - entry.WriteToDirectory(Directory.GetCurrentDirectory(), new ExtractionOptions - { - ExtractFullPath = false, - Overwrite = true - }); - } + SevenZipBase.SetLibraryPath(Path.Combine(cwd, "resources/7z.dll")); + using (var extractor = + new SevenZipExtractor(filename, ArchivePassword, InArchiveFormat.Rar) + {PreserveDirectoryStructure = false}) + { + await extractor.ExtractFilesAsync( + cwd, + $@"{nonlogBuild}\{steamApi64Dll}", + $@"{nonlogBuild}\{steamApiDll}" + ).ConfigureAwait(false); + } + if (File.Exists(Path.Combine(nonlogBuildPath, steamApi64Dll))) + File.Move( + Path.Combine(cwd, nonlogBuild, steamApi64Dll), + Path.Combine(cwd, steamApi64Dll), + true + ); + + if (File.Exists(Path.Combine(nonlogBuildPath, steamApiDll))) + File.Move( + Path.Combine(nonlogBuildPath, steamApiDll), + Path.Combine(cwd, steamApiDll), + true + ); + + if (Directory.Exists(nonlogBuildPath)) + Directory.Delete(nonlogBuildPath, true); MyLogger.Log.Information("Extraction done!"); } } diff --git a/auto-creamapi/Setup.cs b/auto-creamapi/Setup.cs new file mode 100644 index 0000000..9a5682a --- /dev/null +++ b/auto-creamapi/Setup.cs @@ -0,0 +1,29 @@ +using auto_creamapi.Core; +using auto_creamapi.Utils; +using Microsoft.Extensions.Logging; +using MvvmCross.Platforms.Wpf.Core; +using Serilog; +using Serilog.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace auto_creamapi +{ + public class Setup : MvxWpfSetup + { + protected override ILoggerFactory CreateLogFactory() + { + Log.Logger = MyLogger.Log; + + return new SerilogLoggerFactory(); + } + + protected override ILoggerProvider CreateLogProvider() + { + return new SerilogLoggerProvider(); + } + } +} diff --git a/auto-creamapi/Utils/AvailabeArchive.cs b/auto-creamapi/Utils/AvailabeArchive.cs new file mode 100644 index 0000000..f7561b1 --- /dev/null +++ b/auto-creamapi/Utils/AvailabeArchive.cs @@ -0,0 +1,35 @@ +using System.Text.Json.Serialization; + +namespace auto_creamapi.Utils +{ + + public class AvailableArchive + { + [JsonPropertyName("url")] + public string Url { get; set; } + + [JsonPropertyName("archived_snapshots")] + public ArchivedSnapshot ArchivedSnapshots { get; set; } + } + + public class ArchivedSnapshot + { + [JsonPropertyName("closest")] + public Closest Closest { get; set; } + } + + public class Closest + { + [JsonPropertyName("status")] + public string Status { get; set; } + + [JsonPropertyName("available")] + public bool Available { get; set; } + + [JsonPropertyName("url")] + public string Url { get; set; } + + [JsonPropertyName("timestamp")] + public string Timestamp { get; set; } + } +} diff --git a/auto-creamapi/Utils/ISecrets.cs b/auto-creamapi/Utils/ISecrets.cs new file mode 100644 index 0000000..01840ac --- /dev/null +++ b/auto-creamapi/Utils/ISecrets.cs @@ -0,0 +1,8 @@ +namespace auto_creamapi.Utils +{ + public interface ISecrets + { + public string ForumUsername(); + public string ForumPassword(); + } +} \ No newline at end of file diff --git a/auto-creamapi/Utils/Misc.cs b/auto-creamapi/Utils/Misc.cs index 6a04bbd..5e0883c 100644 --- a/auto-creamapi/Utils/Misc.cs +++ b/auto-creamapi/Utils/Misc.cs @@ -1,10 +1,13 @@ using System.Collections.Generic; +using System.Collections.ObjectModel; namespace auto_creamapi.Utils { - public class Misc + public static class Misc { - public static readonly List DefaultLanguages = new List(new[] + public const string SpecialCharsRegex = "[^0-9a-zA-Z]+"; + public const string DefaultLanguageSelection = "english"; + public static readonly ObservableCollection DefaultLanguages = new(new[] { "arabic", "bulgarian", diff --git a/auto-creamapi/Utils/MyLogger.cs b/auto-creamapi/Utils/MyLogger.cs index 9e1b19a..903e5a9 100644 --- a/auto-creamapi/Utils/MyLogger.cs +++ b/auto-creamapi/Utils/MyLogger.cs @@ -1,12 +1,14 @@ using Serilog; using Serilog.Core; +using Serilog.Exceptions; namespace auto_creamapi.Utils { - public class MyLogger + public static class MyLogger { public static readonly Logger Log = new LoggerConfiguration() .MinimumLevel.Debug() + .Enrich.WithExceptionDetails() .WriteTo.Console() .WriteTo.File("autocreamapi.log", rollingInterval: RollingInterval.Day) .CreateLogger(); diff --git a/auto-creamapi/Utils/Secrets.EXAMPLE.cs b/auto-creamapi/Utils/Secrets.EXAMPLE.cs deleted file mode 100644 index f885562..0000000 --- a/auto-creamapi/Utils/Secrets.EXAMPLE.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace auto_creamapi.Utils -{ - /// - /// To use this: - /// Rename file Secrets.EXAMPLE.cs to Secrets.cs - /// Rename class Secrets_REMOVETHIS to Secrets - /// Enter the relevant info below - /// - public class Secrets_REMOVETHIS - { - public const string Username = "Enter username here"; - public const string Password = "Enter password here"; - } -} \ No newline at end of file diff --git a/auto-creamapi/ViewModels/DownloadViewModel.cs b/auto-creamapi/ViewModels/DownloadViewModel.cs index 63475f3..238509c 100644 --- a/auto-creamapi/ViewModels/DownloadViewModel.cs +++ b/auto-creamapi/ViewModels/DownloadViewModel.cs @@ -1,8 +1,10 @@ +using System; using System.Threading.Tasks; +using System.Windows; using auto_creamapi.Messenger; using auto_creamapi.Services; using auto_creamapi.Utils; -using MvvmCross.Logging; +using Microsoft.Extensions.Logging; using MvvmCross.Navigation; using MvvmCross.Plugin.Messenger; using MvvmCross.ViewModels; @@ -14,18 +16,22 @@ namespace auto_creamapi.ViewModels private readonly IDownloadCreamApiService _download; private readonly IMvxNavigationService _navigationService; private readonly MvxSubscriptionToken _token; + private readonly ILogger _logger; private string _filename; private string _info; private double _progress; - public DownloadViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService, - IDownloadCreamApiService download, IMvxMessenger messenger) : base(logProvider, navigationService) + private readonly Secrets _secrets = new(); + + public DownloadViewModel(ILoggerFactory loggerFactory, IMvxNavigationService navigationService, + IDownloadCreamApiService download, IMvxMessenger messenger) : base(loggerFactory, navigationService) { _navigationService = navigationService; + _logger = loggerFactory.CreateLogger(); _download = download; _token = messenger.Subscribe(OnProgressMessage); - MyLogger.Log.Debug(messenger.CountSubscriptionsFor().ToString()); + _logger.LogDebug("{Count}", messenger.CountSubscriptionsFor()); } public string InfoLabel @@ -61,24 +67,38 @@ namespace auto_creamapi.ViewModels public string ProgressPercent => _progress.ToString("P2"); - public override async Task Initialize() + public override void Prepare() { - await base.Initialize(); InfoLabel = "Please wait..."; FilenameLabel = ""; Progress = 0.0; - var download = _download.Download(Secrets.ForumUsername, Secrets.ForumPassword); - var filename = await download; - /*var extract = _download.Extract(filename); - await extract;*/ - await Task.Run(() => _download.Extract(filename)); - _token.Dispose(); - await _navigationService.Close(this); + } + + public override async Task Initialize() + { + try + { + await base.Initialize().ConfigureAwait(false); + var download = _download.Download(_secrets.ForumUsername(), _secrets.ForumPassword()); + var filename = await download.ConfigureAwait(false); + var extract = _download.Extract(filename); + await extract.ConfigureAwait(false); + _token.Dispose(); + await _navigationService.Close(this).ConfigureAwait(false); + } + catch (Exception e) + { + MessageBox.Show("Could not download CreamAPI!\nPlease add CreamAPI DLLs manually!\nShutting down...", + "Error", MessageBoxButton.OK, MessageBoxImage.Error); + _token.Dispose(); + await _navigationService.Close(this).ConfigureAwait(false); + Console.WriteLine(e); + throw; + } } private void OnProgressMessage(ProgressMessage obj) { - //MyLogger.Log.Debug($"{obj.Filename}: {obj.BytesTransferred}"); InfoLabel = obj.Info; FilenameLabel = obj.Filename; Progress = obj.PercentComplete; diff --git a/auto-creamapi/ViewModels/MainViewModel.cs b/auto-creamapi/ViewModels/MainViewModel.cs index 5748753..296164d 100644 --- a/auto-creamapi/ViewModels/MainViewModel.cs +++ b/auto-creamapi/ViewModels/MainViewModel.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using auto_creamapi.Models; using auto_creamapi.Services; using auto_creamapi.Utils; +using Microsoft.Extensions.Logging; using Microsoft.Win32; using MvvmCross.Commands; using MvvmCross.Navigation; @@ -16,10 +17,10 @@ namespace auto_creamapi.ViewModels { public class MainViewModel : MvxViewModel { - private const string DefaultLanguageSelection = "english"; private readonly ICacheService _cache; private readonly ICreamConfigService _config; + private readonly ILogger _logger; private readonly ICreamDllService _dll; private readonly IMvxNavigationService _navigationService; private int _appId; @@ -27,7 +28,7 @@ namespace auto_creamapi.ViewModels private ObservableCollection _dlcs; private bool _dllApplied; private string _dllPath; - private bool _extraprotection; + private bool _extraProtection; private string _gameName; private string _lang; private ObservableCollection _languages; @@ -36,26 +37,46 @@ namespace auto_creamapi.ViewModels private bool _mainWindowEnabled; private bool _offline; private string _status; - private bool _unlockall; + private bool _unlockAll; private bool _useSteamDb; + + private bool _ignoreUnknown; //private const string DlcRegexPattern = @"(?.*) *= *(?.*)"; public MainViewModel(ICacheService cache, ICreamConfigService config, ICreamDllService dll, - IMvxNavigationService navigationService) + IMvxNavigationService navigationService, ILoggerFactory loggerFactory) { _navigationService = navigationService; + _logger = loggerFactory.CreateLogger(); _cache = cache; _config = config; _dll = dll; //_download = download; } + public override async void Prepare() + { + base.Prepare(); + _config.Initialize(); + var tasks = new List { _cache.Initialize() }; + if (!File.Exists("steam_api.dll") | !File.Exists("steam_api64.dll")) + tasks.Add(_navigationService.Navigate()); + //tasks.Add(_navigationService.Navigate()); + tasks.Add(_dll.Initialize()); + await Task.WhenAll(tasks).ConfigureAwait(false); + Languages = new ObservableCollection(Misc.DefaultLanguages); + ResetForm(); + UseSteamDb = true; + MainWindowEnabled = true; + Status = "Ready."; + } + // // COMMANDS // // - public IMvxCommand OpenFileCommand => new MvxCommand(OpenFile); + public IMvxCommand OpenFileCommand => new MvxAsyncCommand(OpenFile); - public IMvxCommand SearchCommand => new MvxAsyncCommand(async () => await Search()); //Command(Search); + public IMvxCommand SearchCommand => new MvxAsyncCommand(async () => await Search().ConfigureAwait(false)); //Command(Search); public IMvxCommand GetListOfDlcCommand => new MvxAsyncCommand(GetListOfDlc); @@ -65,6 +86,8 @@ namespace auto_creamapi.ViewModels public IMvxCommand GoToForumThreadCommand => new MvxCommand(GoToForumThread); + public IMvxCommand GoToSteamdbCommand => new MvxCommand(GoToSteamdb); + // // ATTRIBUTES // // public bool MainWindowEnabled @@ -94,7 +117,6 @@ namespace auto_creamapi.ViewModels { _gameName = value; RaisePropertyChanged(() => GameName); - //MyLogger.Log.Debug($"GameName: {value}"); } } @@ -105,7 +127,7 @@ namespace auto_creamapi.ViewModels { _appId = value; RaisePropertyChanged(() => AppId); - if (value > 0) SetNameById(); + SetNameById(); } } @@ -116,7 +138,6 @@ namespace auto_creamapi.ViewModels { _lang = value; RaisePropertyChanged(() => Lang); - //MyLogger.Log.Debug($"Lang: {value}"); } } @@ -130,23 +151,23 @@ namespace auto_creamapi.ViewModels } } - public bool Extraprotection + public bool ExtraProtection { - get => _extraprotection; + get => _extraProtection; set { - _extraprotection = value; - RaisePropertyChanged(() => Extraprotection); + _extraProtection = value; + RaisePropertyChanged(() => ExtraProtection); } } - public bool Unlockall + public bool UnlockAll { - get => _unlockall; + get => _unlockAll; set { - _unlockall = value; - RaisePropertyChanged(() => Unlockall); + _unlockAll = value; + RaisePropertyChanged(() => UnlockAll); } } @@ -210,28 +231,17 @@ namespace auto_creamapi.ViewModels } } - public override async Task Initialize() + public bool IgnoreUnknown { - _config.Initialize(); - /*await base.Initialize(); - await _cache.Initialize(); - if (!File.Exists("steam_api.dll") | !File.Exists("steam_api64.dll")) - await _navigationService.Navigate(); - await _dll.Initialize();*/ - var tasks = new List {base.Initialize(), _cache.Initialize()}; - if (!File.Exists("steam_api.dll") | !File.Exists("steam_api64.dll")) - tasks.Add(_navigationService.Navigate()); - tasks.Add(_dll.Initialize()); - await Task.WhenAll(tasks); - Languages = new ObservableCollection(_cache.Languages); - ResetForm(); - Lang = DefaultLanguageSelection; - UseSteamDb = true; - MainWindowEnabled = true; - Status = "Ready."; + get => _ignoreUnknown; + set + { + _ignoreUnknown = value; + RaisePropertyChanged(() => IgnoreUnknown); + } } - private void OpenFile() + private async Task OpenFile() { Status = "Waiting for file..."; var dialog = new OpenFileDialog @@ -252,7 +262,22 @@ namespace auto_creamapi.ViewModels ResetForm(); _dll.TargetPath = dirPath; _dll.CheckIfDllExistsAtTarget(); - CheckExistence(); + CheckSetupStatus(); + if (!ConfigExists) + { + var separator = Path.DirectorySeparatorChar; + var strings = new List(dirPath.Split(separator)); + var index = strings.Contains("common") ? strings.FindIndex(x => x.Equals("common")) + 1 : -1; + if (index == -1) + index = strings.Contains("steamapps") + ? strings.FindIndex(x => x.Equals("steamapps")) + 2 + : -1; + var s = index > -1 ? strings[index] : null; + if (s != null) GameName = s; + await Search().ConfigureAwait(false); + // await GetListOfDlc().ConfigureAwait(false); + } + Status = "Ready."; } } @@ -274,9 +299,10 @@ namespace auto_creamapi.ViewModels } else { + MainWindowEnabled = false; var navigate = _navigationService.Navigate, SteamApp>( _cache.GetListOfAppsByName(GameName)); - await navigate; + await navigate.ConfigureAwait(false); var navigateResult = navigate.Result; if (navigateResult != null) { @@ -284,27 +310,31 @@ namespace auto_creamapi.ViewModels AppId = navigateResult.AppId; } } + + // await GetListOfDlc().ConfigureAwait(false); } else { - MyLogger.Log.Warning("Empty game name, cannot initiate search!"); + _logger.LogWarning("Empty game name, cannot initiate search!"); } + + MainWindowEnabled = true; } private async Task GetListOfDlc() { - Status = "Trying to get DLC..."; + Status = "Trying to get DLC, please wait..."; if (AppId > 0) { - var app = new SteamApp {AppId = AppId, Name = GameName}; - var task = _cache.GetListOfDlc(app, UseSteamDb); + var app = new SteamApp { AppId = AppId, Name = GameName }; + var task = _cache.GetListOfDlc(app, UseSteamDb, IgnoreUnknown); MainWindowEnabled = false; - var listOfDlc = await task; + var listOfDlc = await task.ConfigureAwait(false); if (task.IsCompletedSuccessfully) { listOfDlc.Sort((app1, app2) => app1.AppId.CompareTo(app2.AppId)); Dlcs = new ObservableCollection(listOfDlc); - Status = $"Got DLC for AppID {AppId}"; + Status = $"Got DLC for AppID {AppId} (Count: {Dlcs.Count})"; } else { @@ -316,7 +346,7 @@ namespace auto_creamapi.ViewModels else { Status = $"Could not get DLC for AppID {AppId}"; - MyLogger.Log.Error($"GetListOfDlc: Invalid AppID {AppId}"); + _logger.LogError("GetListOfDlc: Invalid AppID {AppId}", AppId); } } @@ -326,14 +356,14 @@ namespace auto_creamapi.ViewModels _config.SetConfigData( AppId, Lang, - Unlockall, - Extraprotection, + UnlockAll, + ExtraProtection, Offline, Dlcs ); _config.SaveFile(); _dll.Save(); - CheckExistence(); + CheckSetupStatus(); Status = "Saving successful."; } @@ -341,8 +371,8 @@ namespace auto_creamapi.ViewModels { AppId = _config.Config.AppId; Lang = _config.Config.Language; - Unlockall = _config.Config.UnlockAll; - Extraprotection = _config.Config.ExtraProtection; + UnlockAll = _config.Config.UnlockAll; + ExtraProtection = _config.Config.ExtraProtection; Offline = _config.Config.ForceOffline; Dlcs = new ObservableCollection(_config.Config.DlcList); Status = "Changes have been reset."; @@ -355,9 +385,7 @@ namespace auto_creamapi.ViewModels { var searchTerm = AppId; //$"{GameName.Replace(" ", "+")}+{appId}"; var destinationUrl = - "https://cs.rin.ru/forum/search.php?keywords=" + - searchTerm + - "&terms=any&fid[]=10&sf=firstpost&sr=topics&submit=Search"; + $"https://cs.rin.ru/forum/search.php?keywords={searchTerm}&terms=any&fid[]=10&sf=firstpost&sr=topics&submit=Search"; var uri = new Uri(destinationUrl); var process = new ProcessStartInfo(uri.AbsoluteUri) { @@ -367,12 +395,34 @@ namespace auto_creamapi.ViewModels } else { - MyLogger.Log.Error($"OpenURL: Invalid AppID {AppId}"); + _logger.LogError("OpenURL: Invalid AppID {AppId}", AppId); Status = $"Could not open URL: Invalid AppID {AppId}"; } } - private void CheckExistence() + private void GoToSteamdb() + { + Status = "Opening URL..."; + if (AppId > 0) + { + var searchTerm = AppId; //$"{GameName.Replace(" ", "+")}+{appId}"; + var destinationUrl = + $"https://steamdb.info/app/{searchTerm}/dlc/"; + var uri = new Uri(destinationUrl); + var process = new ProcessStartInfo(uri.AbsoluteUri) + { + UseShellExecute = true + }; + Process.Start(process); + } + else + { + _logger.LogError("OpenURL: Invalid AppID {AppId}", AppId); + Status = $"Could not open URL: Invalid AppID {AppId}"; + } + } + + private void CheckSetupStatus() { DllApplied = _dll.CreamApiApplied(); ConfigExists = _config.ConfigExists(); @@ -380,8 +430,12 @@ namespace auto_creamapi.ViewModels private void SetNameById() { - var appById = _cache.GetAppById(_appId); - GameName = appById != null ? appById.Name : ""; + if (_appId > 0) + { + var appById = _cache.GetAppById(_appId); + GameName = appById != null ? appById.Name : ""; + } + else GameName = ""; } } } \ No newline at end of file diff --git a/auto-creamapi/ViewModels/SearchResultViewModel.cs b/auto-creamapi/ViewModels/SearchResultViewModel.cs index 8e26c1f..82e401c 100644 --- a/auto-creamapi/ViewModels/SearchResultViewModel.cs +++ b/auto-creamapi/ViewModels/SearchResultViewModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using auto_creamapi.Models; using auto_creamapi.Utils; +using Microsoft.Extensions.Logging; using MvvmCross.Commands; using MvvmCross.Logging; using MvvmCross.Navigation; @@ -13,16 +14,18 @@ namespace auto_creamapi.ViewModels IMvxViewModel, SteamApp> { private readonly IMvxNavigationService _navigationService; + private readonly ILogger _logger; private IEnumerable _steamApps; /*public override async Task Initialize() { await base.Initialize(); }*/ - public SearchResultViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base( - logProvider, navigationService) + public SearchResultViewModel(ILoggerFactory loggerFactory, IMvxNavigationService navigationService) : base( + loggerFactory, navigationService) { _navigationService = navigationService; + _logger = loggerFactory.CreateLogger(); } public IEnumerable Apps @@ -55,9 +58,11 @@ namespace auto_creamapi.ViewModels public override void ViewDestroy(bool viewFinishing = true) { - if (viewFinishing && CloseCompletionSource != null && !CloseCompletionSource.Task.IsCompleted && + if (viewFinishing && CloseCompletionSource?.Task.IsCompleted == false && !CloseCompletionSource.Task.IsFaulted) + { CloseCompletionSource?.TrySetCanceled(); + } base.ViewDestroy(viewFinishing); } @@ -66,8 +71,8 @@ namespace auto_creamapi.ViewModels { if (Selected != null) { - MyLogger.Log.Information($"Successfully got app {Selected}"); - await _navigationService.Close(this, Selected); + _logger.LogInformation("Successfully got app {Selected}", Selected); + await _navigationService.Close(this, Selected).ConfigureAwait(false); } } diff --git a/auto-creamapi/Views/MainView.xaml b/auto-creamapi/Views/MainView.xaml index 17d2c2e..719893e 100644 --- a/auto-creamapi/Views/MainView.xaml +++ b/auto-creamapi/Views/MainView.xaml @@ -1,3 +1,4 @@ + + mc:Ignorable="d" + d:DesignHeight="720" d:DesignWidth="560"> @@ -25,36 +27,54 @@ - - +