From 418e4fa86c092b31ee688b8561e5ea3b0db4f110 Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Sat, 9 Jan 2021 20:17:09 +0100 Subject: [PATCH] Downloading and extraction of Goldberg releases is now implemented. Added support for "offline", "disable networking" and "disable overlay" features. "Generate steam_interfaces.txt" Button is only enabled if steam_interfaces.txt doesn't exist. Most options are now disabled until a DLL is selected. Added "About" section. GUI Changes. --- GoldbergGUI.Core/Services/GoldbergService.cs | 157 ++++++++++++++++--- GoldbergGUI.Core/ViewModels/MainViewModel.cs | 56 +++++-- GoldbergGUI.WPF/Views/MainView.xaml | 47 ++++-- 3 files changed, 222 insertions(+), 38 deletions(-) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index a808f3f..2fc4bce 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; +using System.Net.Http; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; using GoldbergGUI.Core.Models; using MvvmCross.Logging; @@ -15,11 +18,13 @@ namespace GoldbergGUI.Core.Services public interface IGoldbergService { public Task<(string accountName, long userSteamId)> Initialize(IMvxLog log); - public Task<(int appId, List dlcList)> Read(string path); - public Task Save(string path, int appId, List dlcList); + public Task<(int appId, List dlcList, bool offline, bool disableNetworking, bool disableOverlay)> + Read(string path); + public Task Save(string path, int appId, List dlcList, + bool offline, bool disableNetworking, bool disableOverlay); public bool GoldbergApplied(string path); - public void Download(); - public void Extract(string archivePath); + public Task Download(); + public Task Extract(string archivePath); public Task GenerateInterfacesFile(string filePath); } @@ -27,7 +32,8 @@ namespace GoldbergGUI.Core.Services public class GoldbergService : IGoldbergService { private IMvxLog _log; - //private const string GoldbergUrl = "https://mr_goldberg.gitlab.io/goldberg_emulator/"; + private const string GoldbergUrl = "https://mr_goldberg.gitlab.io/goldberg_emulator/"; + private readonly string _goldbergZipPath = Path.Combine(Directory.GetCurrentDirectory(), "goldberg.zip"); private readonly string _goldbergPath = Path.Combine(Directory.GetCurrentDirectory(), "goldberg"); private static readonly string GlobalSettingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @@ -62,11 +68,16 @@ namespace GoldbergGUI.Core.Services "STEAMVIDEO_INTERFACE_V" }; + // Call Download // Get global settings public async Task<(string accountName, long userSteamId)> Initialize(IMvxLog log) { _log = log; + + var download = await Download().ConfigureAwait(false); + if (download) await Extract(_goldbergZipPath).ConfigureAwait(false); + var accountName = "Account name..."; long steamId = -1; await Task.Run(() => @@ -82,7 +93,8 @@ namespace GoldbergGUI.Core.Services // If first time, call GenerateInterfaces // else try to read config - public async Task<(int appId, List dlcList)> Read(string path) + public async Task<(int appId, List dlcList, bool offline, bool disableNetworking, bool disableOverlay)> + Read(string path) { var appId = -1; var dlcList = new List(); @@ -105,15 +117,21 @@ namespace GoldbergGUI.Core.Services Name = match.Groups["name"].Value}); } } - return (appId, dlcList); + return (appId, dlcList, + File.Exists(Path.Combine(path, "steam_settings", "offline.txt")), + File.Exists(Path.Combine(path, "steam_settings", "disable_networking.txt")), + File.Exists(Path.Combine(path, "steam_settings", "disable_overlay.txt")) + ); } // If first time, rename original SteamAPI DLL to steam_api(64)_o.dll // If not, rename current SteamAPI DLL to steam_api(64).dll.backup // Copy Goldberg DLL to path // Save configuration files - public async Task Save(string path, int appId, List dlcList) + public async Task Save(string path, int appId, List dlcList, + bool offline, bool disableNetworking, bool disableOverlay) { + // DLL setup const string x86Name = "steam_api"; const string x64Name = "steam_api64"; if (File.Exists(Path.Combine(path, $"{x86Name}.dll"))) @@ -126,15 +144,59 @@ namespace GoldbergGUI.Core.Services CopyDllFiles(path, x64Name); } + // Create steam_settings folder if missing if (!Directory.Exists(Path.Combine(path, "steam_settings"))) { Directory.CreateDirectory(Path.Combine(path, "steam_settings")); } + // create steam_appid.txt await File.WriteAllTextAsync(Path.Combine(path, "steam_appid.txt"), appId.ToString()).ConfigureAwait(false); - var dlcString = ""; - dlcList.ForEach(x => dlcString += $"{x}\n"); - await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "DLC.txt"), dlcString).ConfigureAwait(false); + + // DLC + if (dlcList.Count > 0) + { + var dlcString = ""; + dlcList.ForEach(x => dlcString += $"{x}\n"); + await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "DLC.txt"), dlcString) + .ConfigureAwait(false); + } + else + { + if (File.Exists(Path.Combine(path, "steam_settings", "DLC.txt"))) + File.Delete(Path.Combine(path, "steam_settings", "DLC.txt")); + } + + // Offline + if (offline) + { + await File.Create(Path.Combine(path, "steam_settings", "offline.txt")).DisposeAsync().ConfigureAwait(false); + } + else + { + File.Delete(Path.Combine(path, "steam_settings", "offline.txt")); + } + + // Disable Networking + if (disableNetworking) + { + await File.Create(Path.Combine(path, "steam_settings", "disable_networking.txt")).DisposeAsync().ConfigureAwait(false); + } + else + { + File.Delete(Path.Combine(path, "steam_settings", "disable_networking.txt")); + } + + // Disable Overlay + if (disableOverlay) + { + await File.Create(Path.Combine(path, "steam_settings", "disable_overlay.txt")).DisposeAsync().ConfigureAwait(false); + } + else + { + File.Delete(Path.Combine(path, "steam_settings", "disable_overlay.txt")); + } + } private void CopyDllFiles(string path, string name) @@ -156,29 +218,88 @@ namespace GoldbergGUI.Core.Services public bool GoldbergApplied(string path) { - //throw new NotImplementedException(); - return true; + var steamSettingsDirExists = Directory.Exists(Path.Combine(path, "steam_settings")); + var steamAppIdTxtExists = File.Exists(Path.Combine(path, "steam_appid.txt")); + return steamSettingsDirExists && steamAppIdTxtExists; } // Get webpage - // Get commit, compare with local if exists, save it if false or missing + // Get job id, compare with local if exists, save it if false or missing // Get latest archive if mismatch, call Extract - public void Download() + public async Task Download() { - //throw new NotImplementedException(); + var value = false; + _log.Debug("Download"); + if (!Directory.Exists(_goldbergPath)) Directory.CreateDirectory(_goldbergPath); + var client = new HttpClient(); + var response = await client.GetAsync(GoldbergUrl).ConfigureAwait(false); + var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var regex = new Regex( + @"https:\/\/gitlab\.com\/Mr_Goldberg\/goldberg_emulator\/-\/jobs\/(?.*)\/artifacts\/download"); + var jobIdPath = Path.Combine(_goldbergPath, "job_id"); + var match = regex.Match(body); + var downloadUrl = match.Value; + if (File.Exists(jobIdPath)) + { + 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)) + { + await StartDownload(client, downloadUrl).ConfigureAwait(false); + value = true; + } + } + else + { + await StartDownload(client, downloadUrl).ConfigureAwait(false); + value = true; + } + + return value; + } + + private async Task StartDownload(HttpClient client, string downloadUrl) + { + _log.Debug(downloadUrl); + await using var fileStream = File.OpenWrite(_goldbergZipPath); + //client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead) + var task = GetFileAsync(client, downloadUrl, fileStream).ConfigureAwait(false); + await task; + if (task.GetAwaiter().IsCompleted) + { + _log.Info("Download finished!"); + } + } + + private static async Task GetFileAsync(HttpClient client, string requestUri, Stream destination, + CancellationToken cancelToken = default) + { + var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancelToken) + .ConfigureAwait(false); + await using var download = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + await download.CopyToAsync(destination, cancelToken).ConfigureAwait(false); + if (destination.CanSeek) destination.Position = 0; } // Empty subfolder ./goldberg/ // Extract all from archive to subfolder ./goldberg/ - public void Extract(string archivePath) + public async Task Extract(string archivePath) { - //throw new NotImplementedException(); + _log.Debug("Extract"); + await Task.Run(() => + { + Directory.Delete(_goldbergPath, true); + ZipFile.ExtractToDirectory(archivePath, _goldbergPath); + }).ConfigureAwait(false); + _log.Debug("Extract done!"); } // https://gitlab.com/Mr_Goldberg/goldberg_emulator/-/blob/master/generate_interfaces_file.cpp // (maybe) check DLL date first public async Task GenerateInterfacesFile(string filePath) { + _log.Debug($"GenerateInterfacesFile {filePath}"); //throw new NotImplementedException(); // Get DLL content var result = new HashSet(); diff --git a/GoldbergGUI.Core/ViewModels/MainViewModel.cs b/GoldbergGUI.Core/ViewModels/MainViewModel.cs index 885cc77..6849718 100644 --- a/GoldbergGUI.Core/ViewModels/MainViewModel.cs +++ b/GoldbergGUI.Core/ViewModels/MainViewModel.cs @@ -32,6 +32,7 @@ private readonly IGoldbergService _goldberg; private readonly IMvxLog _log; private bool _mainWindowEnabled; + private bool _goldbergApplied; public MainViewModel(ISteamService steam, IGoldbergService goldberg, IMvxLog log) { @@ -102,6 +103,8 @@ { _dlcs = value; RaisePropertyChanged(() => DLCs); + RaisePropertyChanged(() => DllSelected); + RaisePropertyChanged(() => SteamInterfacesTxtExists); } } @@ -165,8 +168,29 @@ } } - // COMMANDS // + public bool GoldbergApplied + { + get => _goldbergApplied; + set + { + _goldbergApplied = value; + RaisePropertyChanged(() => GoldbergApplied); + } + } + + public bool SteamInterfacesTxtExists + { + get + { + var dllPathDirExists = GetDllPathDir(out var dirPath); + return dllPathDirExists && !File.Exists(Path.Combine(dirPath, "steam_interfaces.txt")); + } + } + + public bool DllSelected => !DllPath.Contains("Path to game's steam_api(64).dll"); + // COMMANDS // + public IMvxCommand OpenFileCommand => new MvxAsyncCommand(OpenFile); private async Task OpenFile() @@ -241,7 +265,7 @@ private async Task SaveConfig() { - if (DllPath.Contains("Path to game's steam_api(64).dll")) + if (!DllSelected) { _log.Error("No DLL selected!"); return; @@ -249,7 +273,13 @@ _log.Info("Saving..."); if (!GetDllPathDir(out var dirPath)) return; MainWindowEnabled = false; - await _goldberg.Save(dirPath, AppId, DLCs.ToList()).ConfigureAwait(false); + await _goldberg.Save(dirPath, + AppId, + DLCs.ToList(), + Offline, + DisableNetworking, + DisableOverlay).ConfigureAwait(false); + GoldbergApplied = _goldberg.GoldbergApplied(dirPath); MainWindowEnabled = true; } @@ -257,7 +287,7 @@ private async Task ResetConfig() { - if (DllPath.Contains("Path to game's steam_api(64).dll")) + if (!DllSelected) { _log.Error("No DLL selected!"); return; @@ -272,15 +302,20 @@ private async Task GenerateSteamInterfaces() { - if (DllPath.Contains("Path to game's steam_api(64).dll")) + if (!DllSelected) { _log.Error("No DLL selected!"); return; } - _log.Info("Generate steam_interfaces.txt..."); MainWindowEnabled = false; - await _goldberg.GenerateInterfacesFile(DllPath).ConfigureAwait(false); + GetDllPathDir(out var dirPath); + if (File.Exists(Path.Combine(dirPath, "steam_api_o.dll"))) + await _goldberg.GenerateInterfacesFile(Path.Combine(dirPath, "steam_api_o.dll")).ConfigureAwait(false); + else if (File.Exists(Path.Combine(dirPath, "steam_api64_o.dll"))) + await _goldberg.GenerateInterfacesFile(Path.Combine(dirPath, "steam_api64_o.dll")).ConfigureAwait(false); + else await _goldberg.GenerateInterfacesFile(DllPath).ConfigureAwait(false); + await RaisePropertyChanged(() => SteamInterfacesTxtExists).ConfigureAwait(false); MainWindowEnabled = true; } @@ -304,14 +339,17 @@ if (!GetDllPathDir(out var dirPath)) return; MainWindowEnabled = false; List dlcList; - (AppId, dlcList) = await _goldberg.Read(dirPath).ConfigureAwait(false); + (AppId, dlcList, Offline, DisableNetworking, DisableOverlay) = + await _goldberg.Read(dirPath).ConfigureAwait(false); DLCs = new ObservableCollection(dlcList); + GoldbergApplied = _goldberg.GoldbergApplied(dirPath); + await RaisePropertyChanged(() => SteamInterfacesTxtExists).ConfigureAwait(false); MainWindowEnabled = true; } private bool GetDllPathDir(out string dirPath) { - if (DllPath.Contains("Path to game's steam_api(64).dll")) + if (!DllSelected) { _log.Error("No DLL selected!"); dirPath = null; diff --git a/GoldbergGUI.WPF/Views/MainView.xaml b/GoldbergGUI.WPF/Views/MainView.xaml index 85fd0aa..f0fe4e4 100644 --- a/GoldbergGUI.WPF/Views/MainView.xaml +++ b/GoldbergGUI.WPF/Views/MainView.xaml @@ -24,7 +24,7 @@