From 0fc99bf5cd9d87dd14715e09787ca7dbb167c15e Mon Sep 17 00:00:00 2001 From: "waffle.lord" Date: Sat, 9 Jul 2022 00:33:55 -0400 Subject: [PATCH 1/3] rework dump --- Aki.Core/Interfaces/ILiveTaskTableEntry.cs | 21 +++ Aki.Core/Interfaces/IProgressableTask.cs | 13 ++ Aki.Core/InternalData.cs | 43 ++++++ Aki.Core/Model/GenericResult.cs | 18 +++ Aki.Core/Model/LiveTableTask.cs | 153 +++++++++++++++++++++ Aki.Core/Model/LiveTableTaskRunner.cs | 78 +++++++++++ Aki.Core/Program.cs | 150 -------------------- Aki.Core/SPTInstaller.cs | 145 +++++++++++++++++++ Aki.Core/Tasks/CopyClientTask.cs | 47 +++++++ Aki.Core/Tasks/DownloadTask.cs | 138 +++++++++++++++++++ Aki.Core/Tasks/IntializationTask.cs | 47 +++++++ Aki.Core/Tasks/ReleaseCheckTask.cs | 84 +++++++++++ Aki.Core/Tasks/SetupClientTask.cs | 29 ++++ Aki.Helper/DownloadHelper.cs | 107 +++----------- Aki.Helper/HttpClientProgressExtensions.cs | 2 +- Aki.Helper/LogHelper.cs | 48 ------- Aki.Helper/PreCheckHelper.cs | 43 ++---- Aki.Helper/ProcessHelper.cs | 116 +++++----------- Aki.Helper/SpectreHelper.cs | 15 -- SPT_AKI_Installer.csproj | 4 +- 20 files changed, 890 insertions(+), 411 deletions(-) create mode 100644 Aki.Core/Interfaces/ILiveTaskTableEntry.cs create mode 100644 Aki.Core/Interfaces/IProgressableTask.cs create mode 100644 Aki.Core/InternalData.cs create mode 100644 Aki.Core/Model/GenericResult.cs create mode 100644 Aki.Core/Model/LiveTableTask.cs create mode 100644 Aki.Core/Model/LiveTableTaskRunner.cs delete mode 100644 Aki.Core/Program.cs create mode 100644 Aki.Core/SPTInstaller.cs create mode 100644 Aki.Core/Tasks/CopyClientTask.cs create mode 100644 Aki.Core/Tasks/DownloadTask.cs create mode 100644 Aki.Core/Tasks/IntializationTask.cs create mode 100644 Aki.Core/Tasks/ReleaseCheckTask.cs create mode 100644 Aki.Core/Tasks/SetupClientTask.cs delete mode 100644 Aki.Helper/LogHelper.cs delete mode 100644 Aki.Helper/SpectreHelper.cs diff --git a/Aki.Core/Interfaces/ILiveTaskTableEntry.cs b/Aki.Core/Interfaces/ILiveTaskTableEntry.cs new file mode 100644 index 0000000..5fcbc04 --- /dev/null +++ b/Aki.Core/Interfaces/ILiveTaskTableEntry.cs @@ -0,0 +1,21 @@ +using Spectre.Console; +using SPT_AKI_Installer.Aki.Core.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SPT_AKI_Installer.Aki.Core.Interfaces +{ + public interface ILiveTaskTableEntry + { + public string TaskName { get; set; } + + public int RowIndex { get; set; } + + public void SetContext(LiveDisplayContext context, Table table); + + public Task RunAsync(); + } +} diff --git a/Aki.Core/Interfaces/IProgressableTask.cs b/Aki.Core/Interfaces/IProgressableTask.cs new file mode 100644 index 0000000..a2f56f3 --- /dev/null +++ b/Aki.Core/Interfaces/IProgressableTask.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SPT_AKI_Installer.Aki.Core.Interfaces +{ + internal interface IProgressableTask + { + public int Progress { get; set; } + } +} diff --git a/Aki.Core/InternalData.cs b/Aki.Core/InternalData.cs new file mode 100644 index 0000000..a585717 --- /dev/null +++ b/Aki.Core/InternalData.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; + +namespace SPT_AKI_Installer.Aki.Core +{ + public class InternalData + { + /// + /// The folder to install SPT into + /// + public string TargetInstallPath { get; set; } + + /// + /// The orginal EFT game path + /// + public string OriginalGamePath { get; set; } + + /// + /// The original EFT game version + /// + public string OriginalGameVersion { get; set; } + + + /// + /// The release download link for SPT-AKI + /// + public string AkiReleaseDownloadLink { get; set; } + + /// + /// The release download link for the patcher mirror list + /// + public string PatcherMirrorsLink { get; set; } + + /// + /// The release download mirrors for the patcher + /// + public List PatcherReleaseMirrors { get; set; } = null; + + /// + /// Whether or not a patch is needed to downgrade the client files + /// + public bool PatchNeeded { get; set; } + } +} diff --git a/Aki.Core/Model/GenericResult.cs b/Aki.Core/Model/GenericResult.cs new file mode 100644 index 0000000..6700813 --- /dev/null +++ b/Aki.Core/Model/GenericResult.cs @@ -0,0 +1,18 @@ +namespace SPT_AKI_Installer.Aki.Core.Model +{ + public class GenericResult + { + public string Message { get; private set; } + + public bool Succeeded { get; private set; } + + protected GenericResult(string message, bool succeeded) + { + Message = message; + Succeeded = succeeded; + } + + public static GenericResult FromSuccess(string message = "") => new GenericResult(message, true); + public static GenericResult FromError(string errorMessage) => new GenericResult(errorMessage, false); + } +} diff --git a/Aki.Core/Model/LiveTableTask.cs b/Aki.Core/Model/LiveTableTask.cs new file mode 100644 index 0000000..d96f080 --- /dev/null +++ b/Aki.Core/Model/LiveTableTask.cs @@ -0,0 +1,153 @@ +using Spectre.Console; +using SPT_AKI_Installer.Aki.Core.Interfaces; +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace SPT_AKI_Installer.Aki.Core.Model +{ + public abstract class LiveTableTask : ILiveTaskTableEntry, IProgressableTask, IDisposable + { + /// + /// The name that will be displayed in th first column of the table + /// + public string TaskName { get; set; } + + /// + /// Wheather the task reports progress or not + /// + public bool IsIndeterminate; + + /// + /// The progress (percent completed) of the task + /// + public int Progress { get; set; } + + /// + /// The row index in the table of the task + /// + public int RowIndex { get; set; } + + private bool _continueRendering = false; + + private int _indeterminateState = 0; + + private string _currentStatus = "running"; + + private Table _table { get; set; } + + private LiveDisplayContext _context { get; set; } + + public LiveTableTask(string name, bool isIndeterminate = true) + { + TaskName = name; + IsIndeterminate = isIndeterminate; + } + + private string GetIndetermminateStatus() + { + string status = $"[blue]{_currentStatus.EscapeMarkup()} "; + + if (_indeterminateState > 3) _indeterminateState = 0; + + status += new string('.', _indeterminateState); + + status += "[/]"; + + _indeterminateState++; + + return status; + } + + /// + /// Start indeterminate progress spinner + /// + /// this doesn't need to be called if you set isIndeterminate in the constructor. You need to set IsIndeterminate to false to stop this background task + public void StartDrawingIndeterminateProgress() + { + _continueRendering = true; + new Task(new Action(() => { RenderIndeterminateProgress(ref _continueRendering); })).Start(); + } + + public void StartDrawingProgress() + { + _continueRendering = true; + new Task(new Action(() => { RenderProgress(ref _continueRendering); })).Start(); + } + + private void ReRenderEntry(string message) + { + _table.RemoveRow(RowIndex); + _table.InsertRow(RowIndex, TaskName, message); + _context.Refresh(); + } + + private void RenderIndeterminateProgress(ref bool continueRendering) + { + while (continueRendering) + { + ReRenderEntry(GetIndetermminateStatus()); + Thread.Sleep(300); + } + } + + private void RenderProgress(ref bool continueRendering) + { + while (continueRendering) + { + string progressBar = new string(' ', 10); + + int progressFill = (int)Math.Floor((double)Progress / 10); + + progressBar = progressBar.Remove(0, progressFill).Insert(0, new string('=', progressFill)); + + progressBar = $"[blue][[{progressBar}]][/] {Progress}% {_currentStatus}"; + + ReRenderEntry(progressBar); + + Thread.Sleep(300); + } + } + + /// + /// Set the context and table for this task + /// + /// + /// + /// This is called by when it is ran. No need to call it yourself + public void SetContext(LiveDisplayContext context, Table table) + { + _context = context; + _table = table; + } + + /// + /// Set the status text for the task + /// + /// The message to show + /// Stop rendering progress updates (progress and indeterminate progress tasks) + /// If you are running an indeterminate task, set render to false. It will render at the next indeterminate update interval + public void SetStatus(string message, bool stopRendering = true) + { + _currentStatus = message; + + if (stopRendering) + { + _continueRendering = false; + ReRenderEntry(message); + } + } + + /// + /// Run the task async + /// + /// Returns the result of the task + public abstract Task RunAsync(); + + public void Dispose() + { + IsIndeterminate = false; + } + } +} diff --git a/Aki.Core/Model/LiveTableTaskRunner.cs b/Aki.Core/Model/LiveTableTaskRunner.cs new file mode 100644 index 0000000..bf1c62b --- /dev/null +++ b/Aki.Core/Model/LiveTableTaskRunner.cs @@ -0,0 +1,78 @@ +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SPT_AKI_Installer.Aki.Core.Model +{ + public class LiveTableTaskRunner + { + private static async Task<(bool, LiveTableTask task)> RunAllTasksAsync(List tasks, LiveDisplayContext context, Table table) + { + foreach (var task in tasks) + { + if (task.IsIndeterminate) + { + task.StartDrawingIndeterminateProgress(); + } + else + { + task.StartDrawingProgress(); + } + + var result = await task.RunAsync(); + + if (!result.Succeeded) + { + task.SetStatus($"[red]{result.Message.EscapeMarkup()}[/]"); + return (false, task); + } + + task.SetStatus($"[green]{(result.Message == "" ? "Complete" : $"{result.Message.EscapeMarkup()}")}[/]"); + } + + return (true, null); + } + public static async Task RunAsync(List tasks) + { + int halfBufferWidth = Console.BufferWidth / 2; + + Table table = new Table().Alignment(Justify.Center).Border(TableBorder.Rounded).BorderColor(Color.Grey).AddColumn("Task", (column) => + { + column.Width(halfBufferWidth); + }) + .AddColumn("Status", (column) => + { + column.Width(halfBufferWidth); + }); + + await AnsiConsole.Live(table).StartAsync(async context => + { + foreach (var task in tasks) + { + table.AddRow(task.TaskName, "[purple]Pending[/]"); + task.RowIndex = table.Rows.Count() - 1; + task.SetContext(context, table); + + await Task.Delay(50); + context.Refresh(); + } + + var result = await RunAllTasksAsync(tasks, context, table); + + // if a task failed and returned early, set any remaining task status to cancelled + if(!result.Item1) + { + var cancelledTasks = tasks.Take(new Range(tasks.IndexOf(result.Item2)+1, tasks.Count)); + + foreach(var task in cancelledTasks) + { + task.SetStatus("[grey]Cancelled[/]"); + } + + } + }); + } + } +} diff --git a/Aki.Core/Program.cs b/Aki.Core/Program.cs deleted file mode 100644 index a20ef64..0000000 --- a/Aki.Core/Program.cs +++ /dev/null @@ -1,150 +0,0 @@ -using Spectre.Console; -using SPT_AKI_Installer.Aki.Helper; -using System; -using System.IO; -using System.Threading; - -namespace SPT_AKI_Installer.Aki.Core -{ - //TODO: - // locales, language selection - - public static class SPTinstaller - { - static void Main() - { - string targetPath = Environment.CurrentDirectory; -#if DEBUG - targetPath = @"D:\install"; -#endif - SpectreHelper.Figlet("SPT-AKI INSTALLER", Color.Yellow); - PreCheckHelper.GameCheck(out string originalGamePath); - PreCheckHelper.DetectOriginalGameVersion(originalGamePath); - - if (originalGamePath == null) - { - CloseApp("Unable to find EFT OG directory \n please make sure EFT is installed \n please also run EFT once"); - } - - if (originalGamePath == targetPath) - { - CloseApp("Installer is located in EFT's original directory \n Please move the installer to a seperate folder as per the guide"); - } - - var checkForExistingFiles = FileHelper.FindFile(targetPath, "EscapeFromTarkov.exe"); - if (checkForExistingFiles != null) - { - CloseApp("Installer is located in a Folder that has existing Game Files \n Please make sure the installer is in a fresh folder as per the guide"); - } - - LogHelper.Info($"Your current game version: { PreCheckHelper.gameVersion }"); - - LogHelper.Info("Checking releases for AKI and the Patcher"); - - var check = DownloadHelper.ReleaseCheck(); - - while (check.Status != System.Threading.Tasks.TaskStatus.RanToCompletion) - { - } - - LogHelper.Info("Checking if Zips already exist in directory"); - - PreCheckHelper.PatcherZipCheck(originalGamePath, targetPath, out string patcherZipPath); - PreCheckHelper.AkiZipCheck(targetPath, out string akiZipPath); - - if (patcherZipPath == null && DownloadHelper.patchNeedCheck) - { - LogHelper.Info("No Patcher zip file present in directory, downloading..."); - var task = DownloadHelper.DownloadFile(targetPath, DownloadHelper.patcherLink, "/PATCHERZIP.zip"); - while(task.Status != System.Threading.Tasks.TaskStatus.RanToCompletion) - { - } - LogHelper.Info("Download Complete!"); - } - - if (akiZipPath == null) - { - LogHelper.Info("No AKI zip file present in directory, downloading..."); - var task = DownloadHelper.DownloadFile(targetPath, DownloadHelper.akiLink, "/AKIZIP.zip"); - while (task.Status != System.Threading.Tasks.TaskStatus.RanToCompletion) - { - } - LogHelper.Info("Download Complete!"); - } - - PreCheckHelper.PatcherZipCheck(originalGamePath, targetPath, out patcherZipPath); - PreCheckHelper.AkiZipCheck(targetPath, out akiZipPath); - - - - GameCopy(originalGamePath, targetPath); - - if (DownloadHelper.patchNeedCheck) - { - PatcherCopy(targetPath, patcherZipPath); - PatcherProcess(targetPath); - } - - AkiInstall(targetPath, akiZipPath); - DeleteZip(patcherZipPath, akiZipPath); - } - - static void GameCopy(string originalGamePath, string targetPath) - { - LogHelper.Info("Copying game files"); - FileHelper.CopyDirectory(originalGamePath, targetPath, true); - } - - static void PatcherCopy(string targetPath, string patcherZipPath) - { - LogHelper.Info("Extracting patcher"); - ZipHelper.ZipDecompress(patcherZipPath, targetPath); - FileHelper.FindFolder(patcherZipPath, targetPath, out DirectoryInfo dir); - FileHelper.CopyDirectory(dir.FullName, targetPath, true); - - if (dir.Exists) - { - dir.Delete(true); - dir.Refresh(); - if (dir.Exists) - { - LogHelper.Error("unable to delete patcher folder"); - LogHelper.Error($"please delete folder called {dir.FullName}"); - } - } - } - - static void PatcherProcess(string targetPath) - { - LogHelper.Info("Starting patcher"); - ProcessHelper patcherProcess = new(); - patcherProcess.StartProcess(Path.Join(targetPath + "/patcher.exe"), targetPath); - - FileHelper.DeleteFiles(Path.Join(targetPath, "/patcher.exe")); - } - - static void AkiInstall(string targetPath, string akiZipPath) - { - ZipHelper.ZipDecompress(akiZipPath, targetPath); - LogHelper.Info("Aki has been extracted"); - } - - static void DeleteZip(string patcherZipPath, string akiZipPath) - { - FileHelper.DeleteFiles(patcherZipPath, false); - FileHelper.DeleteFiles(akiZipPath, false); - - LogHelper.User("Removed Zips, Press enter to close the installer, you can then delete the installer"); - LogHelper.User("ENJOY SPT-AKI!"); - Console.ReadKey(); - Environment.Exit(0); - } - - static void CloseApp(string text) - { - LogHelper.Warning(text); - Console.ReadKey(); - Environment.Exit(0); - } - } -} \ No newline at end of file diff --git a/Aki.Core/SPTInstaller.cs b/Aki.Core/SPTInstaller.cs new file mode 100644 index 0000000..953c452 --- /dev/null +++ b/Aki.Core/SPTInstaller.cs @@ -0,0 +1,145 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Threading.Tasks; +using Spectre.Console; +using System.IO; +using SPT_AKI_Installer.Aki.Core.Model; +using SPT_AKI_Installer.Aki.Core.Tasks; +using System.Collections.Generic; + +namespace SPT_AKI_Installer.Aki.Core +{ + //TODO: + // locales, language selection + + public class SPTinstaller + { + InternalData _data; + static void Main() + { + var host = ConfigureHost(); + + var tasks = host.Services.GetServices(); + + var taskList = new List(tasks); + + AnsiConsole.Write(new FigletText("SPT-AKI INSTALLER").Centered().Color(Color.Yellow)); + + host.Services.GetRequiredService() + .RunTasksAsync(taskList) + .GetAwaiter() + .GetResult(); + + // GameCopy(originalGamePath, targetPath); + + // if (DownloadHelper.patchNeedCheck) + // { + // PatcherCopy(targetPath, patcherZipPath); + // PatcherProcess(targetPath); + // } + + // AkiInstall(targetPath, akiZipPath); + // DeleteZip(patcherZipPath, akiZipPath); + // } + + // static void GameCopy(string originalGamePath, string targetPath) + // { + // LogHelper.Info("Copying game files"); + // FileHelper.CopyDirectory(originalGamePath, targetPath, true); + // } + + // static void PatcherCopy(string targetPath, string patcherZipPath) + // { + // LogHelper.Info("Extracting patcher"); + // ZipHelper.ZipDecompress(patcherZipPath, targetPath); + // FileHelper.FindFolder(patcherZipPath, targetPath, out DirectoryInfo dir); + // FileHelper.CopyDirectory(dir.FullName, targetPath, true); + + // if (dir.Exists) + // { + // dir.Delete(true); + // dir.Refresh(); + // if (dir.Exists) + // { + // LogHelper.Error("unable to delete patcher folder"); + // LogHelper.Error($"please delete folder called {dir.FullName}"); + // } + // } + } + + public SPTinstaller( + InternalData data + ) + { + _data = data; + } + + public async Task RunTasksAsync(List tasks) + { + _data.TargetInstallPath = Environment.CurrentDirectory; + +#if DEBUG + var path = AnsiConsole.Ask("[purple]DEBUG[/] [blue]::[/] Enter path to install folder: ").Replace("\"", ""); + + if (!Directory.Exists(path)) + { + CloseApp($"Path invalid: {path}"); + } + + _data.TargetInstallPath = path; +#endif + + await LiveTableTaskRunner.RunAsync(tasks); + } + + private static IHost ConfigureHost() + { + return Host.CreateDefaultBuilder().ConfigureServices((_, services) => + { + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + }) + .Build(); + } + + //static void PatcherProcess(string targetPath) + //{ + // LogHelper.Info("Starting patcher"); + // ProcessHelper patcherProcess = new(); + // patcherProcess.StartProcess(Path.Join(targetPath + "/patcher.exe"), targetPath); + + // FileHelper.DeleteFiles(Path.Join(targetPath, "/patcher.exe")); + //} + + //static void AkiInstall(string targetPath, string akiZipPath) + //{ + // ZipHelper.ZipDecompress(akiZipPath, targetPath); + // LogHelper.Info("Aki has been extracted"); + //} + + //static void DeleteZip(string patcherZipPath, string akiZipPath) + //{ + // FileHelper.DeleteFiles(patcherZipPath, false); + // FileHelper.DeleteFiles(akiZipPath, false); + + // LogHelper.User("Removed Zips, Press enter to close the installer, you can then delete the installer"); + // LogHelper.User("ENJOY SPT-AKI!"); + // Console.ReadKey(); + // Environment.Exit(0); + //} + + static void CloseApp(string text) + { + AnsiConsole.MarkupLine($"[yellow]{text.EscapeMarkup()}[/]"); + AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine("Press [blue]Enter[/] to close ..."); + Console.ReadKey(); + Environment.Exit(0); + } + } +} \ No newline at end of file diff --git a/Aki.Core/Tasks/CopyClientTask.cs b/Aki.Core/Tasks/CopyClientTask.cs new file mode 100644 index 0000000..978e678 --- /dev/null +++ b/Aki.Core/Tasks/CopyClientTask.cs @@ -0,0 +1,47 @@ +using SPT_AKI_Installer.Aki.Core.Model; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace SPT_AKI_Installer.Aki.Core.Tasks +{ + public class CopyClientTask : LiveTableTask + { + private InternalData _data; + + public CopyClientTask(InternalData data) : base("Copy Client Files", false) + { + _data = data; + } + + public override async Task RunAsync() + { + SetStatus("Copying", false); + + try + { + int totalFiles = Directory.GetFiles(_data.OriginalGamePath, "*.*", SearchOption.AllDirectories).Length; + int processedFiles = 0; + + foreach (string dirPath in Directory.GetDirectories(_data.OriginalGamePath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(dirPath.Replace(_data.OriginalGamePath, _data.TargetInstallPath)); + } + + foreach (string newPath in Directory.GetFiles(_data.OriginalGamePath, "*.*", SearchOption.AllDirectories)) + { + File.Copy(newPath, newPath.Replace(_data.OriginalGamePath, _data.TargetInstallPath), true); + processedFiles++; + + Progress = (int)Math.Floor(((double)processedFiles / totalFiles) * 100); + } + + return GenericResult.FromSuccess(); + } + catch(Exception ex) + { + return GenericResult.FromError(ex.Message); + } + } + } +} diff --git a/Aki.Core/Tasks/DownloadTask.cs b/Aki.Core/Tasks/DownloadTask.cs new file mode 100644 index 0000000..2cad08e --- /dev/null +++ b/Aki.Core/Tasks/DownloadTask.cs @@ -0,0 +1,138 @@ +using Newtonsoft.Json; +using SPT_AKI_Installer.Aki.Core.Model; +using SPT_AKI_Installer.Aki.Helper; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using CG.Web.MegaApiClient; + +namespace SPT_AKI_Installer.Aki.Core.Tasks +{ + public class DownloadTask : LiveTableTask + { + private InternalData _data; + + public DownloadTask(InternalData data) : base("Download Files", false) + { + _data = data; + } + + private async Task BuildMirrorList() + { + var mirrorListInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "mirrors.json")); + + SetStatus("Downloading mirror list", false); + + var progress = new Progress((d) => { Progress = (int)Math.Floor(d); }); + + var downloadResult = await DownloadHelper.DownloadFile(mirrorListInfo, _data.PatcherMirrorsLink, progress); + + if (!downloadResult.Succeeded) + { + return downloadResult; + } + + var blah = JsonConvert.DeserializeObject>(File.ReadAllText(mirrorListInfo.FullName)); + + if (blah is List mirrors) + { + _data.PatcherReleaseMirrors = mirrors; + + return GenericResult.FromSuccess(); + } + + return GenericResult.FromError("Failed to deserialize mirrors list"); + } + + private async Task DownloadPatcherFromMirrors(FileInfo patcherZip, IProgress progress) + { + foreach (string mirror in _data.PatcherReleaseMirrors) + { + SetStatus($"Download Patcher: {mirror}", false); + + // mega is a little weird since they use encryption, but thankfully there is a great library for their api :) + if (mirror.StartsWith("https://mega")) + { + var megaClient = new MegaApiClient(); + await megaClient.LoginAnonymousAsync(); + + // if mega fails to connect, try the next mirror + if (!megaClient.IsLoggedIn) continue; + + try + { + using var megaDownloadStream = await megaClient.DownloadAsync(new Uri(mirror), progress); + using var patcherFileStream = patcherZip.Open(FileMode.Create); + { + await megaDownloadStream.CopyToAsync(patcherFileStream); + } + + return GenericResult.FromSuccess(); + } + catch(Exception) + { + //most likely a 509 (Bandwidth limit exceeded) due to mega's user quotas. + continue; + } + } + + var result = await DownloadHelper.DownloadFile(patcherZip, mirror, progress); + + if(result.Succeeded) + { + return GenericResult.FromSuccess(); + } + } + + return GenericResult.FromError("Failed to download Patcher"); + } + + public override async Task RunAsync() + { + var patcherZipInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "patcher.zip")); + var akiZipInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "sptaki.zip")); + + if (_data.PatchNeeded) + { + if (patcherZipInfo.Exists) patcherZipInfo.Delete(); + + var buildResult = await BuildMirrorList(); + + if (!buildResult.Succeeded) + { + return buildResult; + } + + SetStatus("Downloading Patcher", false); + + Progress = 0; + + var progress = new Progress((d) => { Progress = (int)Math.Floor(d); }); + var patcherDownloadRresult = await DownloadPatcherFromMirrors(patcherZipInfo, progress); + + if (!patcherDownloadRresult.Succeeded) + { + return patcherDownloadRresult; + } + } + + if (akiZipInfo.Exists) akiZipInfo.Delete(); + + SetStatus("Downloading SPT-AKI", false); + + Progress = 0; + + var akiProgress = new Progress((d) => { Progress = (int)Math.Floor(d); }); + + var releaseDownloadResult = await DownloadHelper.DownloadFile(akiZipInfo, _data.AkiReleaseDownloadLink, akiProgress); + + if (!releaseDownloadResult.Succeeded) + { + return releaseDownloadResult; + } + + return GenericResult.FromSuccess(); + } + } +} \ No newline at end of file diff --git a/Aki.Core/Tasks/IntializationTask.cs b/Aki.Core/Tasks/IntializationTask.cs new file mode 100644 index 0000000..caec87f --- /dev/null +++ b/Aki.Core/Tasks/IntializationTask.cs @@ -0,0 +1,47 @@ +using SPT_AKI_Installer.Aki.Core.Model; +using SPT_AKI_Installer.Aki.Helper; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace SPT_AKI_Installer.Aki.Core.Tasks +{ + public class InitializationTask : LiveTableTask + { + private InternalData _data; + + public InitializationTask(InternalData data) : base("Startup") + { + _data = data; + } + + public override async Task RunAsync() + { + _data.OriginalGamePath = PreCheckHelper.DetectOriginalGamePath(); + + if (_data.OriginalGamePath == null) + { + return GenericResult.FromError("EFT IS NOT INSTALLED!"); + } + + _data.OriginalGameVersion = PreCheckHelper.DetectOriginalGameVersion(_data.OriginalGamePath); + + if (_data.OriginalGamePath == null) + { + GenericResult.FromError("Unable to find EFT OG directory \n please make sure EFT is installed \n please also run EFT once"); + } + + if (_data.OriginalGamePath == _data.TargetInstallPath) + { + GenericResult.FromError("Installer is located in EFT's original directory \n Please move the installer to a seperate folder as per the guide"); + } + + if (File.Exists(Path.Join(_data.TargetInstallPath, "EscapeFromTarkov.exe"))) + { + GenericResult.FromError("Installer is located in a Folder that has existing Game Files \n Please make sure the installer is in a fresh folder as per the guide"); + } + + return GenericResult.FromSuccess($"Current Game Version: {_data.OriginalGameVersion}"); + } + } +} diff --git a/Aki.Core/Tasks/ReleaseCheckTask.cs b/Aki.Core/Tasks/ReleaseCheckTask.cs new file mode 100644 index 0000000..69762ce --- /dev/null +++ b/Aki.Core/Tasks/ReleaseCheckTask.cs @@ -0,0 +1,84 @@ +using Gitea.Api; +using Gitea.Client; +using SPT_AKI_Installer.Aki.Core.Model; +using System; +using System.Threading.Tasks; + +namespace SPT_AKI_Installer.Aki.Core.Tasks +{ + public class ReleaseCheckTask : LiveTableTask + { + private InternalData _data; + + public ReleaseCheckTask(InternalData data) : base("Release Checks") + { + _data = data; + } + + public override async Task RunAsync() + { + Configuration.Default.BasePath = "https://dev.sp-tarkov.com/api/v1"; + + var repo = new RepositoryApi(Configuration.Default); + + try + { + SetStatus("Checking SPT Releases", false); + + var akiRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Stable-releases"); + + SetStatus("Checking for Patches", false); + + var patchRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Downgrade-Patches"); + + var latestAkiRelease = akiRepoReleases.FindAll(x => !x.Prerelease)[0]; + var latestAkiVersion = latestAkiRelease.Name.Replace('(', ' ').Replace(')', ' ').Split(' ')[3]; + var comparePatchToAki = patchRepoReleases?.Find(x => x.Name.Contains(_data.OriginalGameVersion) && x.Name.Contains(latestAkiVersion)); + + _data.PatcherMirrorsLink = comparePatchToAki?.Assets[0].BrowserDownloadUrl; + _data.AkiReleaseDownloadLink = latestAkiRelease.Assets[0].BrowserDownloadUrl; + + int IntAkiVersion = int.Parse(latestAkiVersion); + int IntGameVersion = int.Parse(_data.OriginalGameVersion); + bool patchNeedCheck = false; + + if (IntGameVersion > IntAkiVersion) + { + patchNeedCheck = true; + } + + if (IntGameVersion < IntAkiVersion) + { + return GenericResult.FromError("Your client is outdated. Please update EFT"); + + } + + if (IntGameVersion == IntAkiVersion) + { + patchNeedCheck = false; + } + + if (comparePatchToAki == null && patchNeedCheck) + { + return GenericResult.FromError("No patcher available for your version"); + } + + _data.PatchNeeded = patchNeedCheck; + + string status = $"Current Release: {latestAkiVersion}"; + + if(_data.PatchNeeded) + { + status += " - Patch Available"; + } + + return GenericResult.FromSuccess(status); + } + catch (Exception) + { + //request failed + return GenericResult.FromError("Request Failed"); + } + } + } +} diff --git a/Aki.Core/Tasks/SetupClientTask.cs b/Aki.Core/Tasks/SetupClientTask.cs new file mode 100644 index 0000000..4cef8d1 --- /dev/null +++ b/Aki.Core/Tasks/SetupClientTask.cs @@ -0,0 +1,29 @@ +using SPT_AKI_Installer.Aki.Core.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SPT_AKI_Installer.Aki.Core.Tasks +{ + public class SetupClientTask : LiveTableTask + { + private InternalData _data; + + public SetupClientTask(InternalData data) : base("Setup Client", false) + { + _data = data; + } + + public override async Task RunAsync() + { + /* TODO: + * patch if needed + * extract release + */ + + throw new NotImplementedException(); + } + } +} diff --git a/Aki.Helper/DownloadHelper.cs b/Aki.Helper/DownloadHelper.cs index 3f2ca94..a583824 100644 --- a/Aki.Helper/DownloadHelper.cs +++ b/Aki.Helper/DownloadHelper.cs @@ -1,104 +1,41 @@ -using Newtonsoft.Json; -using System; +using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; -using System.Threading; -using Gitea.Api; -using Gitea.Client; -using Gitea.Model; -using Spectre.Console; using HttpClientProgress; +using SPT_AKI_Installer.Aki.Core.Model; namespace SPT_AKI_Installer.Aki.Helper { public static class DownloadHelper { - public static bool patchNeedCheck; - public static string akiLink; - public static string patcherLink; + private static HttpClient _httpClient = new HttpClient() { Timeout = TimeSpan.FromHours(1) }; - public static async Task ReleaseCheck() + public static async Task DownloadFile(FileInfo outputFile, string targetLink, IProgress progress) { - Configuration.Default.BasePath = "https://dev.sp-tarkov.com/api/v1"; - - var repo = new RepositoryApi(Configuration.Default); - try { - var patchRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Downgrade-Patches"); - var akiRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Stable-releases"); + outputFile.Refresh(); - var latestAkiRelease = akiRepoReleases.FindAll(x => !x.Prerelease)[0]; - var latestAkiVersion = latestAkiRelease.Name.Replace('(', ' ').Replace(')', ' ').Split(' ')[3]; - var comparePatchToAki = patchRepoReleases?.Find(x => x.Name.Contains(PreCheckHelper.gameVersion) && x.Name.Contains(latestAkiVersion)); - - patcherLink = comparePatchToAki?.Assets[0].BrowserDownloadUrl; - akiLink = latestAkiRelease.Assets[0].BrowserDownloadUrl; - - int IntAkiVersion = int.Parse(latestAkiVersion); - int IntGameVersion = int.Parse(PreCheckHelper.gameVersion); - - if (IntGameVersion > IntAkiVersion) - { - patchNeedCheck = true; - } - - if (IntGameVersion < IntAkiVersion) - { - LogHelper.Info("Client is older than current Aki Version, Please update!"); - LogHelper.Warning("Press enter to close the app!"); - Console.ReadKey(); - Environment.Exit(0); - } - - if (IntGameVersion == IntAkiVersion) - { - patchNeedCheck = false; - } - - if(comparePatchToAki == null && patchNeedCheck) - { - LogHelper.Warning("There is no current patcher for your client version and its needed"); - LogHelper.Warning("Please try again later once a new patcher is uploaded."); - LogHelper.Warning("Press enter to close the app!"); - Console.ReadKey(); - Environment.Exit(0); - } - } - catch (Exception) - { - //request failed - LogHelper.Info("Request Failed"); - } - } - - public static async Task DownloadFile(string targetFilePath, string targetLink, string newFileName) - { - await AnsiConsole.Progress().Columns( - new PercentageColumn(), - new TaskDescriptionColumn(), - new ProgressBarColumn(), - new ElapsedTimeColumn(), - new SpinnerColumn() - ).StartAsync(async (ProgressContext context) => - { - var task = context.AddTask("Downloading File"); - - var client = new HttpClient(); - var docUrl = targetLink; - var filePath = Path.Join(targetFilePath, newFileName); - - // Setup your progress reporter - var progress = new Progress((float progress) => - { - task.Value = progress; - }); + if (outputFile.Exists) outputFile.Delete(); // Use the provided extension method - using (var file = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) - await client.DownloadDataAsync(docUrl, file, progress); - }); + using (var file = new FileStream(outputFile.FullName, FileMode.Create, FileAccess.Write, FileShare.None)) + await _httpClient.DownloadDataAsync(targetLink, file, progress); + + outputFile.Refresh(); + + if(!outputFile.Exists) + { + return GenericResult.FromError($"Failed to download {outputFile.Name}"); + } + + return GenericResult.FromSuccess(); + } + catch(Exception ex) + { + return GenericResult.FromError(ex.Message); + } } } } diff --git a/Aki.Helper/HttpClientProgressExtensions.cs b/Aki.Helper/HttpClientProgressExtensions.cs index 32c2c41..2c8d58e 100644 --- a/Aki.Helper/HttpClientProgressExtensions.cs +++ b/Aki.Helper/HttpClientProgressExtensions.cs @@ -8,7 +8,7 @@ namespace HttpClientProgress { public static class HttpClientProgressExtensions { - public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)) + public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)) { using (var response = await client.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead)) { diff --git a/Aki.Helper/LogHelper.cs b/Aki.Helper/LogHelper.cs deleted file mode 100644 index 2749774..0000000 --- a/Aki.Helper/LogHelper.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Spectre.Console; - -namespace SPT_AKI_Installer.Aki.Helper -{ - public static class LogHelper - { - private static void Log(string tag, string message, string foreground, string background = "black") - { - AnsiConsole.MarkupLine($"[{foreground} on {background}][[{tag}]]: {message.EscapeMarkup()}[/]"); - } - - /// - /// Outputs a string to console starting with [USER] with - /// Green text - /// - public static void User(string text) - { - Log("USER", text, "green"); - } - - /// - /// Outputs a string to console starting with [WARNING] with - /// Yellow text - /// - public static void Warning(string text) - { - Log("WARNING", text, "yellow"); - } - - /// - /// Outputs a string to console starting with [ERROR] with - /// Red text - /// - public static void Error(string text) - { - Log("ERROR", text, "red"); - } - - /// - /// Outputs a string to console starting with [INFO] with - /// Blue text - /// - public static void Info(string text) - { - Log("INFO", text, "blue"); - } - } -} diff --git a/Aki.Helper/PreCheckHelper.cs b/Aki.Helper/PreCheckHelper.cs index 8e9c6f4..54fdc5b 100644 --- a/Aki.Helper/PreCheckHelper.cs +++ b/Aki.Helper/PreCheckHelper.cs @@ -1,7 +1,6 @@ using Microsoft.Win32; using System.Runtime.InteropServices; using System.IO; -using System; using System.Diagnostics; namespace SPT_AKI_Installer.Aki.Helper @@ -9,10 +8,6 @@ namespace SPT_AKI_Installer.Aki.Helper public static class PreCheckHelper { private const string registryInstall = @"Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\EscapeFromTarkov"; - private static string OGGamePath; - public static string gameVersion; - private static string patchZip; - private static string akiZip; public static string DetectOriginalGamePath() { @@ -23,50 +18,38 @@ namespace SPT_AKI_Installer.Aki.Helper var uninstallStringValue = Registry.LocalMachine.OpenSubKey(registryInstall, false) ?.GetValue("UninstallString"); var info = (uninstallStringValue is string key) ? new FileInfo(key) : null; - OGGamePath = info?.DirectoryName; - - return OGGamePath; + + return info?.DirectoryName; } - public static void GameCheck(out string gamePath) + public static string DetectOriginalGameVersion(string gamePath) { - string Path = DetectOriginalGamePath(); - - if (Path == null) - { - LogHelper.Error("EFT IS NOT INSTALLED!"); - LogHelper.Error("Press enter to close the app"); - Console.ReadKey(); - Environment.Exit(0); - } - gamePath = Path; + return FileVersionInfo.GetVersionInfo(Path.Join(gamePath + "/EscapeFromTarkov.exe")).ProductVersion.Replace('-', '.').Split('.')[^2]; } - public static void DetectOriginalGameVersion(string gamePath) - { - gameVersion = FileVersionInfo.GetVersionInfo(Path.Join(gamePath + "/EscapeFromTarkov.exe")).ProductVersion.Replace('-', '.').Split('.')[^2]; - } - - public static void PatcherZipCheck(string gamePath, string targetPath, out string patcherZipPath) + public static string GetPatcherZipPath(string gameVersion, string targetPath) { // example patch name - Patcher.12.12.15.17861.to.12.12.15.17349.zip - patchZip = FileHelper.FindFile(targetPath, gameVersion, "Patcher"); + var patchZip = FileHelper.FindFile(targetPath, gameVersion, "Patcher"); if (patchZip == null) { patchZip = FileHelper.FindFile(targetPath, "PATCHERZIP"); } - patcherZipPath = patchZip; + + return patchZip; } - public static void AkiZipCheck(string targetPath, out string akiZipPath) + public static string GetAkiZipPath(string targetPath) { // example aki name - RELEASE-SPT-2.3.1-17349.zip - akiZip = FileHelper.FindFile(targetPath, "SPT", "RELEASE"); + var akiZip = FileHelper.FindFile(targetPath, "SPT", "RELEASE"); + if (akiZip == null) { akiZip = FileHelper.FindFile(targetPath, "AKIZIP"); } - akiZipPath = akiZip; + + return akiZip; } } } diff --git a/Aki.Helper/ProcessHelper.cs b/Aki.Helper/ProcessHelper.cs index f9216f4..0fa8794 100644 --- a/Aki.Helper/ProcessHelper.cs +++ b/Aki.Helper/ProcessHelper.cs @@ -1,100 +1,54 @@ using System.Diagnostics; -using System.Threading; -using System; +using SPT_AKI_Installer.Aki.Core.Model; namespace SPT_AKI_Installer.Aki.Helper { + public enum PatcherExitCode + { + ProgramClosed = 0, + Success = 10, + EftExeNotFound = 11, + NoPatchFolder = 12, + MissingFile = 13, + MissingDir = 14, + PatchFailed = 15 + } + public class ProcessHelper { - private Process _process; - private string _exeDir; - private string _workingDir; - private string response; - - public void StartProcess(string exeDir, string workingDir) + public GenericResult PatchClientFiles(string exeDir, string workingDir) { - _exeDir = exeDir; - _workingDir = workingDir; - _process = new Process(); - _process.StartInfo.FileName = exeDir; - _process.StartInfo.WorkingDirectory = workingDir; - _process.EnableRaisingEvents = true; - _process.StartInfo.Arguments = "autoclose"; - _process.Start(); + var process = new Process(); + process.StartInfo.FileName = exeDir; + process.StartInfo.WorkingDirectory = workingDir; + process.EnableRaisingEvents = true; + process.StartInfo.Arguments = "autoclose"; + process.Start(); - _process.WaitForExit(); - ExitCodeCheck(_process.ExitCode); - } + process.WaitForExit(); - public void ExitCodeCheck(int exitCode) - { - /* - public enum PatcherExitCode + switch ((PatcherExitCode)process.ExitCode) { - ProgramClosed = 0, - Success = 10, - EftExeNotFound = 11, - NoPatchFolder = 12, - MissingFile = 13, - MissingDir = 14 - } - */ + case PatcherExitCode.Success: + return GenericResult.FromSuccess("Patcher Finished Successfully, extracting Aki"); - switch (exitCode) - { - case 0: - LogHelper.Warning("Patcher was closed before completing!"); - LogHelper.Warning("If you need to start the patcher again, type retry"); - LogHelper.Warning("If you want to close the installer, close the app."); - response = Console.ReadLine(); + case PatcherExitCode.ProgramClosed: + return GenericResult.FromError("Patcher was closed before completing!"); - while (!string.Equals(response, "retry", StringComparison.OrdinalIgnoreCase)) - { - LogHelper.Warning("Answer needs to be retry"); - LogHelper.Warning("Try Again.."); - response = Console.ReadLine(); - } - if (string.Equals(response, "retry", StringComparison.OrdinalIgnoreCase)) - { - StartProcess(_exeDir, _workingDir); - break; - } - break; + case PatcherExitCode.EftExeNotFound: + return GenericResult.FromError("EscapeFromTarkov.exe is missing from the install Path"); - case 10: - LogHelper.Info("Patcher Finished Successfully, extracting Aki"); - break; + case PatcherExitCode.NoPatchFolder: + return GenericResult.FromError("Patchers Folder called 'Aki_Patches' is missing"); - case 11: - LogHelper.Error("EscapeFromTarkov.exe is missing from the install Path"); - LogHelper.Warning("Check your game files in their original location are complete!"); - LogHelper.Warning("Closing the installer in 20 seconds"); - Thread.Sleep(20000); - Environment.Exit(0); - break; + case PatcherExitCode.MissingFile: + return GenericResult.FromError("EFT files was missing a Vital file to continue"); - case 12: - LogHelper.Error("Patchers Folder called 'Aki_Patches' missing"); - LogHelper.Warning("Closing the installer in 20 seconds"); - Thread.Sleep(20000); - Environment.Exit(0); - break; + case PatcherExitCode.PatchFailed: + return GenericResult.FromError("A patch failed to apply"); - case 13: - LogHelper.Error("EFT files was missing a Vital file to continue"); - LogHelper.Warning("please reinstall EFT through the BSG launcher"); - LogHelper.Warning("Closing the installer in 20 seconds"); - Thread.Sleep(20000); - Environment.Exit(0); - break; - - case 14: - LogHelper.Error("Patcher Reported Missing Folder"); - // check with Waffle what this one is - LogHelper.Warning("Closing the installer in 20 seconds"); - Thread.Sleep(20000); - Environment.Exit(0); - break; + default: + return GenericResult.FromError("an unknown error occurred in the patcher"); } } } diff --git a/Aki.Helper/SpectreHelper.cs b/Aki.Helper/SpectreHelper.cs deleted file mode 100644 index 152cbe9..0000000 --- a/Aki.Helper/SpectreHelper.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Spectre.Console; - -namespace SPT_AKI_Installer.Aki.Helper -{ - public static class SpectreHelper - { - public static void Figlet(string text, Color color) - { - AnsiConsole.Write( - new FigletText(text) - .Centered() - .Color(color)); - } - } -} diff --git a/SPT_AKI_Installer.csproj b/SPT_AKI_Installer.csproj index 58c0766..4ec1a68 100644 --- a/SPT_AKI_Installer.csproj +++ b/SPT_AKI_Installer.csproj @@ -14,8 +14,10 @@ + + - + From b999f3cb4c6d7a055f309f2a0cc11c3f13c96c34 Mon Sep 17 00:00:00 2001 From: "waffle.lord" Date: Sat, 9 Jul 2022 13:08:41 -0400 Subject: [PATCH 2/3] all the things --- Aki.Core/InternalData.cs | 10 +++ Aki.Core/Model/LiveTableTask.cs | 19 +++-- Aki.Core/SPTInstaller.cs | 64 +--------------- Aki.Core/Tasks/CopyClientTask.cs | 27 ++----- Aki.Core/Tasks/DownloadTask.cs | 24 +++--- Aki.Core/Tasks/IntializationTask.cs | 6 +- Aki.Core/Tasks/SetupClientTask.cs | 73 +++++++++++++++++-- Aki.Helper/FileHelper.cs | 36 ++++----- Aki.Helper/PreCheckHelper.cs | 40 +++++----- Aki.Helper/ProcessHelper.cs | 14 +++- Aki.Helper/ZipHelper.cs | 50 +++++++++---- .../PublishProfiles/FolderProfile.pubxml.user | 2 +- SPT_AKI_Installer.csproj.user | 2 +- 13 files changed, 195 insertions(+), 172 deletions(-) diff --git a/Aki.Core/InternalData.cs b/Aki.Core/InternalData.cs index a585717..1c31cbb 100644 --- a/Aki.Core/InternalData.cs +++ b/Aki.Core/InternalData.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; namespace SPT_AKI_Installer.Aki.Core { @@ -18,7 +19,16 @@ namespace SPT_AKI_Installer.Aki.Core /// The original EFT game version /// public string OriginalGameVersion { get; set; } + + /// + /// Patcher zip file info + /// + public FileInfo PatcherZipInfo { get; set; } + /// + /// SPT-AKI zip file info + /// + public FileInfo AkiZipInfo { get; set; } /// /// The release download link for SPT-AKI diff --git a/Aki.Core/Model/LiveTableTask.cs b/Aki.Core/Model/LiveTableTask.cs index d96f080..34f9677 100644 --- a/Aki.Core/Model/LiveTableTask.cs +++ b/Aki.Core/Model/LiveTableTask.cs @@ -29,7 +29,8 @@ namespace SPT_AKI_Installer.Aki.Core.Model /// public int RowIndex { get; set; } - private bool _continueRendering = false; + private bool _continueRenderingProgress = false; + private bool _continueRenderingIndeterminateProgress = false; private int _indeterminateState = 0; @@ -66,14 +67,19 @@ namespace SPT_AKI_Installer.Aki.Core.Model /// this doesn't need to be called if you set isIndeterminate in the constructor. You need to set IsIndeterminate to false to stop this background task public void StartDrawingIndeterminateProgress() { - _continueRendering = true; - new Task(new Action(() => { RenderIndeterminateProgress(ref _continueRendering); })).Start(); + _continueRenderingProgress = false; + _continueRenderingIndeterminateProgress = true; + + new Task(new Action(() => { RenderIndeterminateProgress(ref _continueRenderingIndeterminateProgress); })).Start(); } public void StartDrawingProgress() { - _continueRendering = true; - new Task(new Action(() => { RenderProgress(ref _continueRendering); })).Start(); + Progress = 0; + _continueRenderingIndeterminateProgress = false; + _continueRenderingProgress = true; + + new Task(new Action(() => { RenderProgress(ref _continueRenderingProgress); })).Start(); } private void ReRenderEntry(string message) @@ -134,7 +140,8 @@ namespace SPT_AKI_Installer.Aki.Core.Model if (stopRendering) { - _continueRendering = false; + _continueRenderingProgress = false; + _continueRenderingIndeterminateProgress = false; ReRenderEntry(message); } } diff --git a/Aki.Core/SPTInstaller.cs b/Aki.Core/SPTInstaller.cs index 953c452..b80e362 100644 --- a/Aki.Core/SPTInstaller.cs +++ b/Aki.Core/SPTInstaller.cs @@ -30,42 +30,6 @@ namespace SPT_AKI_Installer.Aki.Core .RunTasksAsync(taskList) .GetAwaiter() .GetResult(); - - // GameCopy(originalGamePath, targetPath); - - // if (DownloadHelper.patchNeedCheck) - // { - // PatcherCopy(targetPath, patcherZipPath); - // PatcherProcess(targetPath); - // } - - // AkiInstall(targetPath, akiZipPath); - // DeleteZip(patcherZipPath, akiZipPath); - // } - - // static void GameCopy(string originalGamePath, string targetPath) - // { - // LogHelper.Info("Copying game files"); - // FileHelper.CopyDirectory(originalGamePath, targetPath, true); - // } - - // static void PatcherCopy(string targetPath, string patcherZipPath) - // { - // LogHelper.Info("Extracting patcher"); - // ZipHelper.ZipDecompress(patcherZipPath, targetPath); - // FileHelper.FindFolder(patcherZipPath, targetPath, out DirectoryInfo dir); - // FileHelper.CopyDirectory(dir.FullName, targetPath, true); - - // if (dir.Exists) - // { - // dir.Delete(true); - // dir.Refresh(); - // if (dir.Exists) - // { - // LogHelper.Error("unable to delete patcher folder"); - // LogHelper.Error($"please delete folder called {dir.FullName}"); - // } - // } } public SPTinstaller( @@ -91,6 +55,7 @@ namespace SPT_AKI_Installer.Aki.Core #endif await LiveTableTaskRunner.RunAsync(tasks); + CloseApp("SPT is Ready to play"); } private static IHost ConfigureHost() @@ -102,37 +67,12 @@ namespace SPT_AKI_Installer.Aki.Core services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); }) .Build(); } - //static void PatcherProcess(string targetPath) - //{ - // LogHelper.Info("Starting patcher"); - // ProcessHelper patcherProcess = new(); - // patcherProcess.StartProcess(Path.Join(targetPath + "/patcher.exe"), targetPath); - - // FileHelper.DeleteFiles(Path.Join(targetPath, "/patcher.exe")); - //} - - //static void AkiInstall(string targetPath, string akiZipPath) - //{ - // ZipHelper.ZipDecompress(akiZipPath, targetPath); - // LogHelper.Info("Aki has been extracted"); - //} - - //static void DeleteZip(string patcherZipPath, string akiZipPath) - //{ - // FileHelper.DeleteFiles(patcherZipPath, false); - // FileHelper.DeleteFiles(akiZipPath, false); - - // LogHelper.User("Removed Zips, Press enter to close the installer, you can then delete the installer"); - // LogHelper.User("ENJOY SPT-AKI!"); - // Console.ReadKey(); - // Environment.Exit(0); - //} - static void CloseApp(string text) { AnsiConsole.MarkupLine($"[yellow]{text.EscapeMarkup()}[/]"); diff --git a/Aki.Core/Tasks/CopyClientTask.cs b/Aki.Core/Tasks/CopyClientTask.cs index 978e678..99e5dc4 100644 --- a/Aki.Core/Tasks/CopyClientTask.cs +++ b/Aki.Core/Tasks/CopyClientTask.cs @@ -1,4 +1,5 @@ using SPT_AKI_Installer.Aki.Core.Model; +using SPT_AKI_Installer.Aki.Helper; using System; using System.IO; using System.Threading.Tasks; @@ -18,30 +19,12 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks { SetStatus("Copying", false); - try - { - int totalFiles = Directory.GetFiles(_data.OriginalGamePath, "*.*", SearchOption.AllDirectories).Length; - int processedFiles = 0; + var originalGameDirInfo = new DirectoryInfo(_data.OriginalGamePath); + var targetInstallDirInfo = new DirectoryInfo(_data.TargetInstallPath); - foreach (string dirPath in Directory.GetDirectories(_data.OriginalGamePath, "*", SearchOption.AllDirectories)) - { - Directory.CreateDirectory(dirPath.Replace(_data.OriginalGamePath, _data.TargetInstallPath)); - } + var progress = new Progress((d) => { Progress = (int)Math.Floor(d); }); - foreach (string newPath in Directory.GetFiles(_data.OriginalGamePath, "*.*", SearchOption.AllDirectories)) - { - File.Copy(newPath, newPath.Replace(_data.OriginalGamePath, _data.TargetInstallPath), true); - processedFiles++; - - Progress = (int)Math.Floor(((double)processedFiles / totalFiles) * 100); - } - - return GenericResult.FromSuccess(); - } - catch(Exception ex) - { - return GenericResult.FromError(ex.Message); - } + return FileHelper.CopyDirectoryWithProgress(originalGameDirInfo, targetInstallDirInfo, progress); } } } diff --git a/Aki.Core/Tasks/DownloadTask.cs b/Aki.Core/Tasks/DownloadTask.cs index 2cad08e..e14f5f2 100644 --- a/Aki.Core/Tasks/DownloadTask.cs +++ b/Aki.Core/Tasks/DownloadTask.cs @@ -22,7 +22,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks { var mirrorListInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "mirrors.json")); - SetStatus("Downloading mirror list", false); + SetStatus("Downloading Mirror List", false); var progress = new Progress((d) => { Progress = (int)Math.Floor(d); }); @@ -45,11 +45,11 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks return GenericResult.FromError("Failed to deserialize mirrors list"); } - private async Task DownloadPatcherFromMirrors(FileInfo patcherZip, IProgress progress) + private async Task DownloadPatcherFromMirrors(IProgress progress) { foreach (string mirror in _data.PatcherReleaseMirrors) { - SetStatus($"Download Patcher: {mirror}", false); + SetStatus($"Downloading Patcher: {mirror}", false); // mega is a little weird since they use encryption, but thankfully there is a great library for their api :) if (mirror.StartsWith("https://mega")) @@ -63,7 +63,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks try { using var megaDownloadStream = await megaClient.DownloadAsync(new Uri(mirror), progress); - using var patcherFileStream = patcherZip.Open(FileMode.Create); + using var patcherFileStream = _data.PatcherZipInfo.Open(FileMode.Create); { await megaDownloadStream.CopyToAsync(patcherFileStream); } @@ -77,7 +77,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks } } - var result = await DownloadHelper.DownloadFile(patcherZip, mirror, progress); + var result = await DownloadHelper.DownloadFile(_data.PatcherZipInfo, mirror, progress); if(result.Succeeded) { @@ -90,12 +90,12 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks public override async Task RunAsync() { - var patcherZipInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "patcher.zip")); - var akiZipInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "sptaki.zip")); + _data.PatcherZipInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "patcher.zip")); + _data.AkiZipInfo= new FileInfo(Path.Join(_data.TargetInstallPath, "sptaki.zip")); if (_data.PatchNeeded) { - if (patcherZipInfo.Exists) patcherZipInfo.Delete(); + if (_data.PatcherZipInfo.Exists) _data.PatcherZipInfo.Delete(); var buildResult = await BuildMirrorList(); @@ -103,13 +103,11 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks { return buildResult; } - - SetStatus("Downloading Patcher", false); Progress = 0; var progress = new Progress((d) => { Progress = (int)Math.Floor(d); }); - var patcherDownloadRresult = await DownloadPatcherFromMirrors(patcherZipInfo, progress); + var patcherDownloadRresult = await DownloadPatcherFromMirrors(progress); if (!patcherDownloadRresult.Succeeded) { @@ -117,7 +115,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks } } - if (akiZipInfo.Exists) akiZipInfo.Delete(); + if (_data.AkiZipInfo.Exists) _data.AkiZipInfo.Delete(); SetStatus("Downloading SPT-AKI", false); @@ -125,7 +123,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks var akiProgress = new Progress((d) => { Progress = (int)Math.Floor(d); }); - var releaseDownloadResult = await DownloadHelper.DownloadFile(akiZipInfo, _data.AkiReleaseDownloadLink, akiProgress); + var releaseDownloadResult = await DownloadHelper.DownloadFile(_data.AkiZipInfo, _data.AkiReleaseDownloadLink, akiProgress); if (!releaseDownloadResult.Succeeded) { diff --git a/Aki.Core/Tasks/IntializationTask.cs b/Aki.Core/Tasks/IntializationTask.cs index caec87f..0123545 100644 --- a/Aki.Core/Tasks/IntializationTask.cs +++ b/Aki.Core/Tasks/IntializationTask.cs @@ -28,17 +28,17 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks if (_data.OriginalGamePath == null) { - GenericResult.FromError("Unable to find EFT OG directory \n please make sure EFT is installed \n please also run EFT once"); + return GenericResult.FromError("Unable to find EFT OG directory, please make sure EFT is installed. Please also run EFT once"); } if (_data.OriginalGamePath == _data.TargetInstallPath) { - GenericResult.FromError("Installer is located in EFT's original directory \n Please move the installer to a seperate folder as per the guide"); + return GenericResult.FromError("Installer is located in EFT's original directory. Please move the installer to a seperate folder as per the guide"); } if (File.Exists(Path.Join(_data.TargetInstallPath, "EscapeFromTarkov.exe"))) { - GenericResult.FromError("Installer is located in a Folder that has existing Game Files \n Please make sure the installer is in a fresh folder as per the guide"); + return GenericResult.FromError("Installer is located in a Folder that has existing Game Files. Please make sure the installer is in a fresh folder as per the guide"); } return GenericResult.FromSuccess($"Current Game Version: {_data.OriginalGameVersion}"); diff --git a/Aki.Core/Tasks/SetupClientTask.cs b/Aki.Core/Tasks/SetupClientTask.cs index 4cef8d1..6d76b42 100644 --- a/Aki.Core/Tasks/SetupClientTask.cs +++ b/Aki.Core/Tasks/SetupClientTask.cs @@ -1,8 +1,8 @@ using SPT_AKI_Installer.Aki.Core.Model; +using SPT_AKI_Installer.Aki.Helper; using System; -using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; namespace SPT_AKI_Installer.Aki.Core.Tasks @@ -18,12 +18,71 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks public override async Task RunAsync() { - /* TODO: - * patch if needed - * extract release - */ + // extract patcher files + SetStatus("Extrating Patcher", false); - throw new NotImplementedException(); + var extractPatcherProgress = new Progress((d) => { Progress = (int)Math.Floor(d); }); + + var patcherOutputDir = new DirectoryInfo(Path.Join(_data.TargetInstallPath, "patcher")); + + var extractPatcherResult = ZipHelper.Decompress(_data.PatcherZipInfo, patcherOutputDir, extractPatcherProgress); + + if(!extractPatcherResult.Succeeded) + { + return extractPatcherResult; + } + + // copy patcher files to install directory + SetStatus("Copying Patcher", false); + + var patcherDirInfo = patcherOutputDir.GetDirectories("Patcher*", SearchOption.TopDirectoryOnly).First(); + var targetInstallDirInfo = new DirectoryInfo(_data.TargetInstallPath); + + var copyPatcherProgress = new Progress((d) => { Progress = (int)Math.Floor(d); }); + + var copyPatcherResult = FileHelper.CopyDirectoryWithProgress(patcherDirInfo, targetInstallDirInfo, copyPatcherProgress); + + if(!copyPatcherResult.Succeeded) + { + return copyPatcherResult; + } + + // run patcher + SetStatus("Running Patcher"); + StartDrawingIndeterminateProgress(); + + var patcherEXE = new FileInfo(Path.Join(_data.TargetInstallPath, "patcher.exe")); + + var patchingResult = ProcessHelper.PatchClientFiles(patcherEXE, targetInstallDirInfo); + + if(!patchingResult.Succeeded) + { + return patchingResult; + } + + // extract release files + SetStatus("Extracting Release"); + StartDrawingProgress(); + + var extractReleaseProgress = new Progress((d) => { Progress = (int)Math.Floor(d); }); + + var extractReleaseResult = ZipHelper.Decompress(_data.AkiZipInfo, targetInstallDirInfo, extractReleaseProgress); + + if(!extractReleaseResult.Succeeded) + { + return extractPatcherResult; + } + + // cleanup temp files + SetStatus("Cleanup"); + StartDrawingIndeterminateProgress(); + + patcherOutputDir.Delete(true); + _data.PatcherZipInfo.Delete(); + _data.AkiZipInfo.Delete(); + patcherEXE.Delete(); + + return GenericResult.FromSuccess("SPT is Setup. Happy Playing!"); } } } diff --git a/Aki.Helper/FileHelper.cs b/Aki.Helper/FileHelper.cs index 15f11c7..423decc 100644 --- a/Aki.Helper/FileHelper.cs +++ b/Aki.Helper/FileHelper.cs @@ -1,36 +1,38 @@ using System; using System.IO; using Spectre.Console; +using SPT_AKI_Installer.Aki.Core.Model; namespace SPT_AKI_Installer.Aki.Helper { public static class FileHelper { - public static void CopyDirectory(string oldDir, string newDir, bool overwrite) + public static GenericResult CopyDirectoryWithProgress(DirectoryInfo sourceDir, DirectoryInfo targetDir, IProgress progress) { - int totalFiles = Directory.GetFiles(oldDir, "*.*", SearchOption.AllDirectories).Length; - - AnsiConsole.Progress().Columns( - new PercentageColumn(), - new TaskDescriptionColumn(), - new ProgressBarColumn(), - new ElapsedTimeColumn(), - new SpinnerColumn() - ).Start((ProgressContext context) => + try { - var task = context.AddTask("Copying Files", true, totalFiles); + int totalFiles = sourceDir.GetFiles("*.*", SearchOption.AllDirectories).Length; + int processedFiles = 0; - foreach (string dirPath in Directory.GetDirectories(oldDir, "*", SearchOption.AllDirectories)) + foreach (var dir in sourceDir.GetDirectories("*", SearchOption.AllDirectories)) { - Directory.CreateDirectory(dirPath.Replace(oldDir, newDir)); + Directory.CreateDirectory(dir.FullName.Replace(sourceDir.FullName, targetDir.FullName)); } - foreach (string newPath in Directory.GetFiles(oldDir, "*.*", SearchOption.AllDirectories)) + foreach (var file in sourceDir.GetFiles("*.*", SearchOption.AllDirectories)) { - File.Copy(newPath, newPath.Replace(oldDir, newDir), overwrite); - task.Increment(1); + File.Copy(file.FullName, file.FullName.Replace(sourceDir.FullName, targetDir.FullName), true); + processedFiles++; + + progress.Report((int)Math.Floor(((double)processedFiles / totalFiles) * 100)); } - }); + + return GenericResult.FromSuccess(); + } + catch(Exception ex) + { + return GenericResult.FromError(ex.Message); + } } public static void DeleteFiles(string filePath, bool allFolders = false) diff --git a/Aki.Helper/PreCheckHelper.cs b/Aki.Helper/PreCheckHelper.cs index 54fdc5b..a12da89 100644 --- a/Aki.Helper/PreCheckHelper.cs +++ b/Aki.Helper/PreCheckHelper.cs @@ -27,29 +27,29 @@ namespace SPT_AKI_Installer.Aki.Helper return FileVersionInfo.GetVersionInfo(Path.Join(gamePath + "/EscapeFromTarkov.exe")).ProductVersion.Replace('-', '.').Split('.')[^2]; } - public static string GetPatcherZipPath(string gameVersion, string targetPath) - { - // example patch name - Patcher.12.12.15.17861.to.12.12.15.17349.zip - var patchZip = FileHelper.FindFile(targetPath, gameVersion, "Patcher"); - if (patchZip == null) - { - patchZip = FileHelper.FindFile(targetPath, "PATCHERZIP"); - } + //public static string GetPatcherZipPath(string gameVersion, string targetPath) + //{ + // // example patch name - Patcher.12.12.15.17861.to.12.12.15.17349.zip + // var patchZip = FileHelper.FindFile(targetPath, gameVersion, "Patcher"); + // if (patchZip == null) + // { + // patchZip = FileHelper.FindFile(targetPath, "PATCHERZIP"); + // } - return patchZip; - } + // return patchZip; + //} - public static string GetAkiZipPath(string targetPath) - { - // example aki name - RELEASE-SPT-2.3.1-17349.zip - var akiZip = FileHelper.FindFile(targetPath, "SPT", "RELEASE"); + //public static string GetAkiZipPath(string targetPath) + //{ + // // example aki name - RELEASE-SPT-2.3.1-17349.zip + // var akiZip = FileHelper.FindFile(targetPath, "SPT", "RELEASE"); - if (akiZip == null) - { - akiZip = FileHelper.FindFile(targetPath, "AKIZIP"); - } + // if (akiZip == null) + // { + // akiZip = FileHelper.FindFile(targetPath, "AKIZIP"); + // } - return akiZip; - } + // return akiZip; + //} } } diff --git a/Aki.Helper/ProcessHelper.cs b/Aki.Helper/ProcessHelper.cs index 0fa8794..e859e42 100644 --- a/Aki.Helper/ProcessHelper.cs +++ b/Aki.Helper/ProcessHelper.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.IO; using SPT_AKI_Installer.Aki.Core.Model; namespace SPT_AKI_Installer.Aki.Helper @@ -14,13 +15,18 @@ namespace SPT_AKI_Installer.Aki.Helper PatchFailed = 15 } - public class ProcessHelper + public static class ProcessHelper { - public GenericResult PatchClientFiles(string exeDir, string workingDir) + public static GenericResult PatchClientFiles(FileInfo executable, DirectoryInfo workingDir) { + if(!executable.Exists || !workingDir.Exists) + { + return GenericResult.FromError($"Could not find executable ({executable.Name}) or working directory ({workingDir.Name})"); + } + var process = new Process(); - process.StartInfo.FileName = exeDir; - process.StartInfo.WorkingDirectory = workingDir; + process.StartInfo.FileName = executable.FullName; + process.StartInfo.WorkingDirectory = workingDir.FullName; process.EnableRaisingEvents = true; process.StartInfo.Arguments = "autoclose"; process.Start(); diff --git a/Aki.Helper/ZipHelper.cs b/Aki.Helper/ZipHelper.cs index e047a71..84a0490 100644 --- a/Aki.Helper/ZipHelper.cs +++ b/Aki.Helper/ZipHelper.cs @@ -1,38 +1,56 @@ using SharpCompress.Archives; using SharpCompress.Archives.Zip; using SharpCompress.Common; -using Spectre.Console; +using SPT_AKI_Installer.Aki.Core.Model; +using System; +using System.IO; using System.Linq; namespace SPT_AKI_Installer.Aki.Helper { public static class ZipHelper { - public static void ZipDecompress(string ArchivePath, string OutputFolderPath) + public static GenericResult Decompress(FileInfo ArchivePath, DirectoryInfo OutputFolderPath, IProgress progress = null) { - AnsiConsole.Progress().Columns( - new PercentageColumn(), - new TaskDescriptionColumn(), - new ProgressBarColumn(), - new ElapsedTimeColumn(), - new SpinnerColumn() - ).Start((ProgressContext context) => + try { - using var archive = ZipArchive.Open(ArchivePath); - var entries = archive.Entries.Where(entry => !entry.IsDirectory); - var task = context.AddTask("Extracting Files", true, entries.Count()); + OutputFolderPath.Refresh(); - foreach (var entry in entries) + if (!OutputFolderPath.Exists) OutputFolderPath.Create(); + + using var archive = ZipArchive.Open(ArchivePath); + var totalEntries = archive.Entries.Where(entry => !entry.IsDirectory); + int processedEntries = 0; + + foreach (var entry in totalEntries) { - entry.WriteToDirectory($"{OutputFolderPath}", new ExtractionOptions() + entry.WriteToDirectory(OutputFolderPath.FullName, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true }); - task.Increment(1); + processedEntries++; + + if (progress != null) + { + progress.Report(Math.Floor(((double)processedEntries / totalEntries.Count()) * 100)); + } } - }); + + OutputFolderPath.Refresh(); + + if(!OutputFolderPath.Exists) + { + return GenericResult.FromError($"Failed to extract files: {ArchivePath.Name}"); + } + + return GenericResult.FromSuccess(); + } + catch(Exception ex) + { + return GenericResult.FromError(ex.Message); + } } } } diff --git a/Properties/PublishProfiles/FolderProfile.pubxml.user b/Properties/PublishProfiles/FolderProfile.pubxml.user index fe4b3b5..b3fad3b 100644 --- a/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -4,6 +4,6 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - True|2022-06-21T18:47:38.7532473Z;True|2022-06-08T18:26:47.7977621+01:00;True|2022-06-06T15:07:18.8067168+01:00;True|2022-06-05T22:55:20.5192697+01:00;True|2022-05-30T13:11:30.6942032+01:00;True|2022-05-30T13:08:08.4269393+01:00;True|2022-05-17T01:06:33.6758525+01:00;True|2022-05-14T01:56:09.8410037+01:00;True|2022-05-14T00:54:24.0683990+01:00;True|2022-05-14T00:53:04.7105427+01:00;True|2022-05-14T00:51:00.6280767+01:00;True|2022-05-14T00:49:19.4630888+01:00;True|2022-05-14T00:47:59.2166156+01:00; + True|2022-07-09T17:06:26.5751622Z;True|2022-07-09T12:56:17.1018408-04:00;True|2022-07-09T12:38:17.0878078-04:00;True|2022-07-09T12:18:23.6469737-04:00;True|2022-06-21T14:47:38.7532473-04:00;True|2022-06-08T13:26:47.7977621-04:00;True|2022-06-06T10:07:18.8067168-04:00;True|2022-06-05T17:55:20.5192697-04:00;True|2022-05-30T08:11:30.6942032-04:00;True|2022-05-30T08:08:08.4269393-04:00;True|2022-05-16T20:06:33.6758525-04:00;True|2022-05-13T20:56:09.8410037-04:00;True|2022-05-13T19:54:24.0683990-04:00;True|2022-05-13T19:53:04.7105427-04:00;True|2022-05-13T19:51:00.6280767-04:00;True|2022-05-13T19:49:19.4630888-04:00;True|2022-05-13T19:47:59.2166156-04:00; \ No newline at end of file diff --git a/SPT_AKI_Installer.csproj.user b/SPT_AKI_Installer.csproj.user index f965575..a3d4ff2 100644 --- a/SPT_AKI_Installer.csproj.user +++ b/SPT_AKI_Installer.csproj.user @@ -1,6 +1,6 @@  - <_LastSelectedProfileId>C:\Users\craig\source\repos\CWXDEV\CWX-SPTinstaller\Properties\PublishProfiles\FolderProfile.pubxml + <_LastSelectedProfileId>Z:\dev_stuff\SPT-AKI-Installer\Properties\PublishProfiles\FolderProfile.pubxml \ No newline at end of file From 96b69641654a09c88622f472b2523defc80b9135 Mon Sep 17 00:00:00 2001 From: "waffle.lord" Date: Sat, 9 Jul 2022 13:14:03 -0400 Subject: [PATCH 3/3] ran code cleanup --- Aki.Core/Interfaces/ILiveTaskTableEntry.cs | 4 - Aki.Core/Interfaces/IProgressableTask.cs | 8 +- Aki.Core/InternalData.cs | 2 +- Aki.Core/Model/LiveTableTask.cs | 1 - Aki.Core/Model/LiveTableTaskRunner.cs | 6 +- Aki.Core/SPTInstaller.cs | 6 +- Aki.Core/Tasks/DownloadTask.cs | 14 ++-- Aki.Core/Tasks/IntializationTask.cs | 1 - Aki.Core/Tasks/ReleaseCheckTask.cs | 2 +- Aki.Core/Tasks/SetupClientTask.cs | 8 +- Aki.Helper/DownloadHelper.cs | 10 +-- Aki.Helper/FileHelper.cs | 70 +---------------- Aki.Helper/HttpClientProgressExtensions.cs | 90 +++++++++++----------- Aki.Helper/PreCheckHelper.cs | 31 +------- Aki.Helper/ProcessHelper.cs | 6 +- Aki.Helper/ZipHelper.cs | 6 +- 16 files changed, 82 insertions(+), 183 deletions(-) diff --git a/Aki.Core/Interfaces/ILiveTaskTableEntry.cs b/Aki.Core/Interfaces/ILiveTaskTableEntry.cs index 5fcbc04..b334661 100644 --- a/Aki.Core/Interfaces/ILiveTaskTableEntry.cs +++ b/Aki.Core/Interfaces/ILiveTaskTableEntry.cs @@ -1,9 +1,5 @@ using Spectre.Console; using SPT_AKI_Installer.Aki.Core.Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace SPT_AKI_Installer.Aki.Core.Interfaces diff --git a/Aki.Core/Interfaces/IProgressableTask.cs b/Aki.Core/Interfaces/IProgressableTask.cs index a2f56f3..7237c7a 100644 --- a/Aki.Core/Interfaces/IProgressableTask.cs +++ b/Aki.Core/Interfaces/IProgressableTask.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SPT_AKI_Installer.Aki.Core.Interfaces +namespace SPT_AKI_Installer.Aki.Core.Interfaces { internal interface IProgressableTask { diff --git a/Aki.Core/InternalData.cs b/Aki.Core/InternalData.cs index 1c31cbb..690d20a 100644 --- a/Aki.Core/InternalData.cs +++ b/Aki.Core/InternalData.cs @@ -19,7 +19,7 @@ namespace SPT_AKI_Installer.Aki.Core /// The original EFT game version /// public string OriginalGameVersion { get; set; } - + /// /// Patcher zip file info /// diff --git a/Aki.Core/Model/LiveTableTask.cs b/Aki.Core/Model/LiveTableTask.cs index 34f9677..df90b56 100644 --- a/Aki.Core/Model/LiveTableTask.cs +++ b/Aki.Core/Model/LiveTableTask.cs @@ -1,7 +1,6 @@ using Spectre.Console; using SPT_AKI_Installer.Aki.Core.Interfaces; using System; -using System.Text; using System.Threading; using System.Threading.Tasks; diff --git a/Aki.Core/Model/LiveTableTaskRunner.cs b/Aki.Core/Model/LiveTableTaskRunner.cs index bf1c62b..be8ee98 100644 --- a/Aki.Core/Model/LiveTableTaskRunner.cs +++ b/Aki.Core/Model/LiveTableTaskRunner.cs @@ -62,11 +62,11 @@ namespace SPT_AKI_Installer.Aki.Core.Model var result = await RunAllTasksAsync(tasks, context, table); // if a task failed and returned early, set any remaining task status to cancelled - if(!result.Item1) + if (!result.Item1) { - var cancelledTasks = tasks.Take(new Range(tasks.IndexOf(result.Item2)+1, tasks.Count)); + var cancelledTasks = tasks.Take(new Range(tasks.IndexOf(result.Item2) + 1, tasks.Count)); - foreach(var task in cancelledTasks) + foreach (var task in cancelledTasks) { task.SetStatus("[grey]Cancelled[/]"); } diff --git a/Aki.Core/SPTInstaller.cs b/Aki.Core/SPTInstaller.cs index b80e362..6dfa6d8 100644 --- a/Aki.Core/SPTInstaller.cs +++ b/Aki.Core/SPTInstaller.cs @@ -1,12 +1,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using System; -using System.Threading.Tasks; using Spectre.Console; -using System.IO; using SPT_AKI_Installer.Aki.Core.Model; using SPT_AKI_Installer.Aki.Core.Tasks; +using System; using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; namespace SPT_AKI_Installer.Aki.Core { diff --git a/Aki.Core/Tasks/DownloadTask.cs b/Aki.Core/Tasks/DownloadTask.cs index e14f5f2..7b9e52d 100644 --- a/Aki.Core/Tasks/DownloadTask.cs +++ b/Aki.Core/Tasks/DownloadTask.cs @@ -1,11 +1,11 @@ -using Newtonsoft.Json; +using CG.Web.MegaApiClient; +using Newtonsoft.Json; using SPT_AKI_Installer.Aki.Core.Model; using SPT_AKI_Installer.Aki.Helper; using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using CG.Web.MegaApiClient; namespace SPT_AKI_Installer.Aki.Core.Tasks { @@ -70,7 +70,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks return GenericResult.FromSuccess(); } - catch(Exception) + catch (Exception) { //most likely a 509 (Bandwidth limit exceeded) due to mega's user quotas. continue; @@ -79,7 +79,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks var result = await DownloadHelper.DownloadFile(_data.PatcherZipInfo, mirror, progress); - if(result.Succeeded) + if (result.Succeeded) { return GenericResult.FromSuccess(); } @@ -91,7 +91,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks public override async Task RunAsync() { _data.PatcherZipInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "patcher.zip")); - _data.AkiZipInfo= new FileInfo(Path.Join(_data.TargetInstallPath, "sptaki.zip")); + _data.AkiZipInfo = new FileInfo(Path.Join(_data.TargetInstallPath, "sptaki.zip")); if (_data.PatchNeeded) { @@ -103,7 +103,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks { return buildResult; } - + Progress = 0; var progress = new Progress((d) => { Progress = (int)Math.Floor(d); }); @@ -120,7 +120,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks SetStatus("Downloading SPT-AKI", false); Progress = 0; - + var akiProgress = new Progress((d) => { Progress = (int)Math.Floor(d); }); var releaseDownloadResult = await DownloadHelper.DownloadFile(_data.AkiZipInfo, _data.AkiReleaseDownloadLink, akiProgress); diff --git a/Aki.Core/Tasks/IntializationTask.cs b/Aki.Core/Tasks/IntializationTask.cs index 0123545..7ba3a52 100644 --- a/Aki.Core/Tasks/IntializationTask.cs +++ b/Aki.Core/Tasks/IntializationTask.cs @@ -1,6 +1,5 @@ using SPT_AKI_Installer.Aki.Core.Model; using SPT_AKI_Installer.Aki.Helper; -using System; using System.IO; using System.Threading.Tasks; diff --git a/Aki.Core/Tasks/ReleaseCheckTask.cs b/Aki.Core/Tasks/ReleaseCheckTask.cs index 69762ce..865fd7d 100644 --- a/Aki.Core/Tasks/ReleaseCheckTask.cs +++ b/Aki.Core/Tasks/ReleaseCheckTask.cs @@ -67,7 +67,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks string status = $"Current Release: {latestAkiVersion}"; - if(_data.PatchNeeded) + if (_data.PatchNeeded) { status += " - Patch Available"; } diff --git a/Aki.Core/Tasks/SetupClientTask.cs b/Aki.Core/Tasks/SetupClientTask.cs index 6d76b42..15aedc4 100644 --- a/Aki.Core/Tasks/SetupClientTask.cs +++ b/Aki.Core/Tasks/SetupClientTask.cs @@ -27,7 +27,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks var extractPatcherResult = ZipHelper.Decompress(_data.PatcherZipInfo, patcherOutputDir, extractPatcherProgress); - if(!extractPatcherResult.Succeeded) + if (!extractPatcherResult.Succeeded) { return extractPatcherResult; } @@ -42,7 +42,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks var copyPatcherResult = FileHelper.CopyDirectoryWithProgress(patcherDirInfo, targetInstallDirInfo, copyPatcherProgress); - if(!copyPatcherResult.Succeeded) + if (!copyPatcherResult.Succeeded) { return copyPatcherResult; } @@ -55,7 +55,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks var patchingResult = ProcessHelper.PatchClientFiles(patcherEXE, targetInstallDirInfo); - if(!patchingResult.Succeeded) + if (!patchingResult.Succeeded) { return patchingResult; } @@ -68,7 +68,7 @@ namespace SPT_AKI_Installer.Aki.Core.Tasks var extractReleaseResult = ZipHelper.Decompress(_data.AkiZipInfo, targetInstallDirInfo, extractReleaseProgress); - if(!extractReleaseResult.Succeeded) + if (!extractReleaseResult.Succeeded) { return extractPatcherResult; } diff --git a/Aki.Helper/DownloadHelper.cs b/Aki.Helper/DownloadHelper.cs index a583824..ff199dd 100644 --- a/Aki.Helper/DownloadHelper.cs +++ b/Aki.Helper/DownloadHelper.cs @@ -1,9 +1,9 @@ -using System; +using HttpClientProgress; +using SPT_AKI_Installer.Aki.Core.Model; +using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; -using HttpClientProgress; -using SPT_AKI_Installer.Aki.Core.Model; namespace SPT_AKI_Installer.Aki.Helper { @@ -25,14 +25,14 @@ namespace SPT_AKI_Installer.Aki.Helper outputFile.Refresh(); - if(!outputFile.Exists) + if (!outputFile.Exists) { return GenericResult.FromError($"Failed to download {outputFile.Name}"); } return GenericResult.FromSuccess(); } - catch(Exception ex) + catch (Exception ex) { return GenericResult.FromError(ex.Message); } diff --git a/Aki.Helper/FileHelper.cs b/Aki.Helper/FileHelper.cs index 423decc..9bcdb44 100644 --- a/Aki.Helper/FileHelper.cs +++ b/Aki.Helper/FileHelper.cs @@ -1,7 +1,6 @@ -using System; +using SPT_AKI_Installer.Aki.Core.Model; +using System; using System.IO; -using Spectre.Console; -using SPT_AKI_Installer.Aki.Core.Model; namespace SPT_AKI_Installer.Aki.Helper { @@ -29,73 +28,10 @@ namespace SPT_AKI_Installer.Aki.Helper return GenericResult.FromSuccess(); } - catch(Exception ex) + catch (Exception ex) { return GenericResult.FromError(ex.Message); } } - - public static void DeleteFiles(string filePath, bool allFolders = false) - { - if (File.Exists(filePath) || Directory.Exists(filePath)) - { - if (filePath.Contains('.')) - { - File.Delete(filePath); - } - else - { - Directory.Delete(filePath, allFolders); - } - } - } - - public static string FindFile(string path, string name) - { - string[] filePaths = Directory.GetFiles(path); - foreach (string file in filePaths) - { - if (file.Contains(name, StringComparison.OrdinalIgnoreCase)) - { - return file; - } - } - return null; - } - - public static string FindFile(string path, string name, string altName) - { - string[] filePaths = Directory.GetFiles(path); - foreach (string file in filePaths) - { - if (file.Contains(name, StringComparison.OrdinalIgnoreCase) && - file.Contains(altName, StringComparison.OrdinalIgnoreCase)) - { - return file; - } - } - return null; - } - - public static bool FindFolder(string patchRef, string targetPath, out DirectoryInfo dir) - { - var dirInfo = new DirectoryInfo(targetPath).GetDirectories(); - string patchInner = null; - foreach (var file in dirInfo) - { - if (file.FullName.Contains("patcher", StringComparison.OrdinalIgnoreCase)) - { - patchInner = file.FullName; - } - } - var path = new DirectoryInfo(patchInner); - if (path.Exists) - { - dir = path; - return true; - } - dir = null; - return false; - } } } diff --git a/Aki.Helper/HttpClientProgressExtensions.cs b/Aki.Helper/HttpClientProgressExtensions.cs index 2c8d58e..3568717 100644 --- a/Aki.Helper/HttpClientProgressExtensions.cs +++ b/Aki.Helper/HttpClientProgressExtensions.cs @@ -6,52 +6,52 @@ using System.Threading.Tasks; namespace HttpClientProgress { - public static class HttpClientProgressExtensions - { - public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var response = await client.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead)) - { - var contentLength = response.Content.Headers.ContentLength; - using (var download = await response.Content.ReadAsStreamAsync()) - { - // no progress... no contentLength... very sad - if (progress is null || !contentLength.HasValue) - { - await download.CopyToAsync(destination); - return; - } - // Such progress and contentLength much reporting Wow! - var progressWrapper = new Progress(totalBytes => progress.Report(GetProgressPercentage(totalBytes, contentLength.Value))); - await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken); - } - } + public static class HttpClientProgressExtensions + { + public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var response = await client.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead)) + { + var contentLength = response.Content.Headers.ContentLength; + using (var download = await response.Content.ReadAsStreamAsync()) + { + // no progress... no contentLength... very sad + if (progress is null || !contentLength.HasValue) + { + await download.CopyToAsync(destination); + return; + } + // Such progress and contentLength much reporting Wow! + var progressWrapper = new Progress(totalBytes => progress.Report(GetProgressPercentage(totalBytes, contentLength.Value))); + await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken); + } + } - float GetProgressPercentage(float totalBytes, float currentBytes) => (totalBytes / currentBytes) * 100f; - } + float GetProgressPercentage(float totalBytes, float currentBytes) => (totalBytes / currentBytes) * 100f; + } - static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (bufferSize < 0) - throw new ArgumentOutOfRangeException(nameof(bufferSize)); - if (source is null) - throw new ArgumentNullException(nameof(source)); - if (!source.CanRead) - throw new InvalidOperationException($"'{nameof(source)}' is not readable."); - if (destination == null) - throw new ArgumentNullException(nameof(destination)); - if (!destination.CanWrite) - throw new InvalidOperationException($"'{nameof(destination)}' is not writable."); + static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (bufferSize < 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize)); + if (source is null) + throw new ArgumentNullException(nameof(source)); + if (!source.CanRead) + throw new InvalidOperationException($"'{nameof(source)}' is not readable."); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + if (!destination.CanWrite) + throw new InvalidOperationException($"'{nameof(destination)}' is not writable."); - var buffer = new byte[bufferSize]; - long totalBytesRead = 0; - int bytesRead; - while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) - { - await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); - totalBytesRead += bytesRead; - progress?.Report(totalBytesRead); - } - } - } + var buffer = new byte[bufferSize]; + long totalBytesRead = 0; + int bytesRead; + while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + totalBytesRead += bytesRead; + progress?.Report(totalBytesRead); + } + } + } } \ No newline at end of file diff --git a/Aki.Helper/PreCheckHelper.cs b/Aki.Helper/PreCheckHelper.cs index a12da89..6919988 100644 --- a/Aki.Helper/PreCheckHelper.cs +++ b/Aki.Helper/PreCheckHelper.cs @@ -1,7 +1,7 @@ using Microsoft.Win32; -using System.Runtime.InteropServices; -using System.IO; using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; namespace SPT_AKI_Installer.Aki.Helper { @@ -18,7 +18,7 @@ namespace SPT_AKI_Installer.Aki.Helper var uninstallStringValue = Registry.LocalMachine.OpenSubKey(registryInstall, false) ?.GetValue("UninstallString"); var info = (uninstallStringValue is string key) ? new FileInfo(key) : null; - + return info?.DirectoryName; } @@ -26,30 +26,5 @@ namespace SPT_AKI_Installer.Aki.Helper { return FileVersionInfo.GetVersionInfo(Path.Join(gamePath + "/EscapeFromTarkov.exe")).ProductVersion.Replace('-', '.').Split('.')[^2]; } - - //public static string GetPatcherZipPath(string gameVersion, string targetPath) - //{ - // // example patch name - Patcher.12.12.15.17861.to.12.12.15.17349.zip - // var patchZip = FileHelper.FindFile(targetPath, gameVersion, "Patcher"); - // if (patchZip == null) - // { - // patchZip = FileHelper.FindFile(targetPath, "PATCHERZIP"); - // } - - // return patchZip; - //} - - //public static string GetAkiZipPath(string targetPath) - //{ - // // example aki name - RELEASE-SPT-2.3.1-17349.zip - // var akiZip = FileHelper.FindFile(targetPath, "SPT", "RELEASE"); - - // if (akiZip == null) - // { - // akiZip = FileHelper.FindFile(targetPath, "AKIZIP"); - // } - - // return akiZip; - //} } } diff --git a/Aki.Helper/ProcessHelper.cs b/Aki.Helper/ProcessHelper.cs index e859e42..8dd4feb 100644 --- a/Aki.Helper/ProcessHelper.cs +++ b/Aki.Helper/ProcessHelper.cs @@ -1,6 +1,6 @@ -using System.Diagnostics; +using SPT_AKI_Installer.Aki.Core.Model; +using System.Diagnostics; using System.IO; -using SPT_AKI_Installer.Aki.Core.Model; namespace SPT_AKI_Installer.Aki.Helper { @@ -19,7 +19,7 @@ namespace SPT_AKI_Installer.Aki.Helper { public static GenericResult PatchClientFiles(FileInfo executable, DirectoryInfo workingDir) { - if(!executable.Exists || !workingDir.Exists) + if (!executable.Exists || !workingDir.Exists) { return GenericResult.FromError($"Could not find executable ({executable.Name}) or working directory ({workingDir.Name})"); } diff --git a/Aki.Helper/ZipHelper.cs b/Aki.Helper/ZipHelper.cs index 84a0490..aabcf52 100644 --- a/Aki.Helper/ZipHelper.cs +++ b/Aki.Helper/ZipHelper.cs @@ -10,7 +10,7 @@ namespace SPT_AKI_Installer.Aki.Helper { public static class ZipHelper { - public static GenericResult Decompress(FileInfo ArchivePath, DirectoryInfo OutputFolderPath, IProgress progress = null) + public static GenericResult Decompress(FileInfo ArchivePath, DirectoryInfo OutputFolderPath, IProgress progress = null) { try { @@ -40,14 +40,14 @@ namespace SPT_AKI_Installer.Aki.Helper OutputFolderPath.Refresh(); - if(!OutputFolderPath.Exists) + if (!OutputFolderPath.Exists) { return GenericResult.FromError($"Failed to extract files: {ArchivePath.Name}"); } return GenericResult.FromSuccess(); } - catch(Exception ex) + catch (Exception ex) { return GenericResult.FromError(ex.Message); }