diff --git a/GenericStripper/Modules/BeatSaber/BeatSaber.cs b/GenericStripper/Modules/BeatSaber/BeatSaber.cs index a43de1d..8e2aff8 100644 --- a/GenericStripper/Modules/BeatSaber/BeatSaber.cs +++ b/GenericStripper/Modules/BeatSaber/BeatSaber.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Spectre.Console; namespace GenericStripper.Modules.BeatSaber; @@ -21,6 +22,61 @@ public BeatSaber(string gamePath) public string GameName => "Beat Saber"; public string GamePath { get; } + + private async Task InstallBsipa() + { + if (File.Exists(Path.Combine(GamePath, "IPA.exe"))) + { + AnsiConsole.MarkupLine("[green]BSIPA already installed, skipping...[/]"); + return; + } + + await AnsiConsole.Status() + .StartAsync("Installing BSIPA...", async ctx => + { + var res = await _client.GetAsync( + "https://api.github.com/repos/nike4613/BeatSaber-IPA-Reloaded/releases/latest"); + if (res.StatusCode != HttpStatusCode.OK) throw new Exception("Failed to get latest BSIPA release!"); + + var latestRelease = + JsonConvert.DeserializeObject>(await res.Content.ReadAsStringAsync()); + if (latestRelease == null) throw new Exception("Failed to parse BSIPA release!"); + + var assets = latestRelease["assets"] as JArray; + var arch = RuntimeInformation.OSArchitecture.ToString().ToLower(); + var asset = assets?.FirstOrDefault(x => x["name"]?.ToString().Contains(arch) ?? false); + + if (asset == null) throw new Exception("Failed to find a BSIPA asset for this system!"); + + var assetRes = await _client.GetAsync(asset["browser_download_url"]?.ToString()); + if (assetRes.StatusCode != HttpStatusCode.OK) throw new Exception("Failed to download BSIPA asset!"); + + await using var assetStream = await assetRes.Content.ReadAsStreamAsync(); + using var archive = new ZipArchive(assetStream); + archive.ExtractToDirectory(GamePath); + + if (!File.Exists(Path.Combine(GamePath, "IPA.exe"))) + throw new Exception("Failed to extract BSIPA asset!"); + + // todo: prevent BSIPA from outputting to stdout + var bsipa = new Process + { + StartInfo = + { + FileName = Path.Combine(GamePath, "IPA.exe"), + WorkingDirectory = GamePath, + Arguments = "--nowait", + } + }; + + bsipa.Start(); + await bsipa.WaitForExitAsync(); + + if (bsipa.ExitCode != 0) throw new Exception("Failed to install BSIPA!"); + ctx.Status("[green]BSIPA installed![/]"); + }); + } + public void StripDll(string file, string outDir, params string[] resolveDirs) { if (!File.Exists(file)) throw new FileNotFoundException("Failed to find assembly to strip!", file); @@ -30,52 +86,39 @@ public void StripDll(string file, string outDir, params string[] resolveDirs) bsAssemblyModule.Virtualize(); bsAssemblyModule.Strip(); - Console.WriteLine($"Stripped {fileInf.Name}!"); - Directory.CreateDirectory(Path.Combine(outDir, Path.GetRelativePath(GamePath, fileInf.Directory!.ToString()))); var outFile = Path.Combine(outDir, Path.GetRelativePath(GamePath, fileInf.FullName)); bsAssemblyModule.Write(outFile); } - public async Task InstallBsipa() + public async Task StripAllDlls(string outDir) { - if (File.Exists(Path.Combine(GamePath, "IPA.exe"))) return; - Console.WriteLine("Installing BSIPA..."); - - var res = await _client.GetAsync( - "https://api.github.com/repos/nike4613/BeatSaber-IPA-Reloaded/releases/latest"); - if (res.StatusCode != HttpStatusCode.OK) throw new Exception("Failed to get latest BSIPA release!"); - - var latestRelease = - JsonConvert.DeserializeObject>(await res.Content.ReadAsStringAsync()); - if (latestRelease == null) throw new Exception("Failed to parse BSIPA release!"); - - var assets = latestRelease["assets"] as JArray; - var arch = RuntimeInformation.OSArchitecture.ToString().ToLower(); - var asset = assets?.FirstOrDefault(x => x["name"]?.ToString().Contains(arch) ?? false); - - if (asset == null) throw new Exception("Failed to find a BSIPA asset for this system!"); - - var assetRes = await _client.GetAsync(asset["browser_download_url"]?.ToString()); - if (assetRes.StatusCode != HttpStatusCode.OK) throw new Exception("Failed to download BSIPA asset!"); - - await using var assetStream = await assetRes.Content.ReadAsStreamAsync(); - using var archive = new ZipArchive(assetStream); - archive.ExtractToDirectory(GamePath); - - if (!File.Exists(Path.Combine(GamePath, "IPA.exe"))) throw new Exception("Failed to extract BSIPA asset!"); - - var bsipa = new Process - { - StartInfo = - { - FileName = Path.Combine(GamePath, "IPA.exe"), - WorkingDirectory = GamePath, - Arguments = "--nowait" - } - }; - - bsipa.Start(); - await bsipa.WaitForExitAsync(); + await InstallBsipa(); + + var bsLibsDir = Path.Combine(GamePath, "Libs"); + var bsManagedDir = Path.Combine(GamePath, "Beat Saber_Data", "Managed"); + + var outputDir = Path.Combine(GamePath, outDir); + if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir); + + var libAssemblies = Directory.GetFiles(bsLibsDir, "*.dll", SearchOption.AllDirectories); + var managedAssemblies = Directory.GetFiles(bsManagedDir, "*.dll", SearchOption.AllDirectories); + + AnsiConsole.Progress() + .Start(ctx => + { + var task = ctx.AddTask("[salmon1]Stripping assemblies...[/]", new ProgressTaskSettings + { + MaxValue = libAssemblies.Length + managedAssemblies.Length + }); + + foreach (var assembly in libAssemblies.Concat(managedAssemblies)) + { + StripDll(assembly, outputDir, bsLibsDir, bsManagedDir); + task.Increment(1); + AnsiConsole.MarkupLine($"[teal]Stripped {assembly}[/]"); + } + } + ); } } \ No newline at end of file diff --git a/GenericStripper/Modules/IModule.cs b/GenericStripper/Modules/IModule.cs index ac455f9..8ceb5bb 100644 --- a/GenericStripper/Modules/IModule.cs +++ b/GenericStripper/Modules/IModule.cs @@ -6,5 +6,7 @@ public interface IModule public string GamePath { get; } - public void StripDll(string file, string outDir, params string[] resolveDirs); + protected void StripDll(string file, string outDir, params string[] resolveDirs); + + public Task StripAllDlls(string outDir); } \ No newline at end of file diff --git a/GenericStripper/Program.cs b/GenericStripper/Program.cs index 4b0481c..d7d00fe 100644 --- a/GenericStripper/Program.cs +++ b/GenericStripper/Program.cs @@ -1,9 +1,12 @@ -using GenericStripper.Modules.BeatSaber; +using System.Diagnostics.CodeAnalysis; +using GenericStripper.Modules; +using GenericStripper.Modules.BeatSaber; using Spectre.Console; using Spectre.Console.Cli; namespace GenericStripper; +[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] internal abstract class Program { public static int Main(string[] args) @@ -12,9 +15,9 @@ public static int Main(string[] args) return app.Run(args); } - internal sealed class MainCommand : Command + internal sealed class MainCommand : AsyncCommand { - public override int Execute(CommandContext context, Settings settings) + public override async Task ExecuteAsync(CommandContext context, Settings settings) { if (settings.Module == null) { @@ -34,24 +37,23 @@ public override int Execute(CommandContext context, Settings settings) return 1; } - - var bs = new BeatSaber(settings.Path); - bs.InstallBsipa().Wait(); - - var bsLibsDir = Path.Combine(bs.GamePath, "Libs"); - var bsManagedDir = Path.Combine(bs.GamePath, "Beat Saber_Data", "Managed"); - - var outputDir = Path.Combine(bs.GamePath, settings.Out); - if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir); - - var libAssemblies = Directory.GetFiles(bsLibsDir, "*.dll", SearchOption.AllDirectories); - var managedAssemblies = Directory.GetFiles(bsManagedDir, "*.dll", SearchOption.AllDirectories); - - foreach (var assembly in libAssemblies.Concat(managedAssemblies)) - bs.StripDll(assembly, outputDir, bsLibsDir, bsManagedDir); - - Console.WriteLine("Done!"); - + var module = settings.Module.ToLower(); + var path = settings.Path; + var outDir = settings.Out; + + IModule? mod = module switch + { + "beatsaber" => new BeatSaber(path), + _ => null + }; + + if (mod == null) + { + AnsiConsole.MarkupLine("[red]Invalid module specified![/]"); + return 1; + } + + await mod.StripAllDlls(outDir); return 0; }