diff --git a/GenericStripper/Modules/BeatSaber/BSAssemblyModule.cs b/GenericStripper/Modules/BeatSaber/BSAssemblyModule.cs index 343ce19..4a8676f 100644 --- a/GenericStripper/Modules/BeatSaber/BSAssemblyModule.cs +++ b/GenericStripper/Modules/BeatSaber/BSAssemblyModule.cs @@ -1,4 +1,6 @@ +using System.Runtime.InteropServices; using Mono.Cecil; +using Mono.Cecil.Rocks; namespace GenericStripper.Modules.BeatSaber; @@ -8,6 +10,8 @@ public class BsAssemblyModule private readonly FileInfo _file; private readonly ModuleDefinition _module; + private TypeReference? _inasmref; + public BsAssemblyModule(string gamePath, string fileName, params string[] resolverDirs) { _bslibLoader = new BsLibLoader(gamePath); @@ -44,7 +48,7 @@ public void Virtualize() foreach (var type in _module.Types) VirtualizeType(type); } - private static void VirtualizeType(TypeDefinition type) + private void VirtualizeType(TypeDefinition type) { if (type.IsSealed) type.IsSealed = false; @@ -53,14 +57,32 @@ private static void VirtualizeType(TypeDefinition type) foreach (var subType in type.NestedTypes) VirtualizeType(subType); - foreach (var m in type.Methods.Where(m => m.IsManaged - && m is - { - IsIL: true, IsStatic: false, IsVirtual: false, IsAbstract: false, - IsAddOn: false, IsConstructor: false, IsSpecialName: false, - IsGenericInstance: false, HasOverrides: false - })) + foreach (var m in type.Methods.Where(m => m.IsManaged && m is + { + IsIL: true, IsStatic: false, IsVirtual: false, IsAbstract: false, IsAddOn: false, + IsConstructor: false, IsSpecialName: false, IsGenericInstance: false, HasOverrides: false + })) { + foreach (var p in m.Parameters.Where(p => p.IsIn)) + { + _inasmref ??= _module.ImportReference(typeof(InAttribute)); + List opt = new(); + List req = new(); + while (_inasmref is IModifierType modType) + { + if (_inasmref.IsOptionalModifier) opt.Add(modType.ModifierType); + else req.Add(modType.ModifierType); + _inasmref = modType.ElementType; + } + + if (!req.Contains(_inasmref)) req.Add(_inasmref); + foreach (var typeReference in req) _inasmref = _inasmref.MakeRequiredModifierType(typeReference); + + foreach (var typeReference in opt) _inasmref = _inasmref.MakeOptionalModifierType(typeReference); + + p.ParameterType = _inasmref; + } + m.IsVirtual = true; m.IsPublic = true; m.IsPrivate = false; diff --git a/GenericStripper/Modules/BeatSaber/BeatSaber.cs b/GenericStripper/Modules/BeatSaber/BeatSaber.cs index 8e2aff8..7b83a34 100644 --- a/GenericStripper/Modules/BeatSaber/BeatSaber.cs +++ b/GenericStripper/Modules/BeatSaber/BeatSaber.cs @@ -22,6 +22,51 @@ public BeatSaber(string gamePath) public string GameName => "Beat Saber"; public string GamePath { get; } + public void StripDll(string file, string outDir, params string[] resolveDirs) + { + if (!File.Exists(file)) throw new FileNotFoundException("Failed to find assembly to strip!", file); + var fileInf = new FileInfo(file); + + var bsAssemblyModule = new BsAssemblyModule(GamePath, file, resolveDirs); + bsAssemblyModule.Virtualize(); + bsAssemblyModule.Strip(); + + 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 StripAllDlls(string outDir) + { + 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}[/]"); + } + } + ); + } + private async Task InstallBsipa() { @@ -65,60 +110,15 @@ await AnsiConsole.Status() { FileName = Path.Combine(GamePath, "IPA.exe"), WorkingDirectory = GamePath, - Arguments = "--nowait", + 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); - var fileInf = new FileInfo(file); - - var bsAssemblyModule = new BsAssemblyModule(GamePath, file, resolveDirs); - bsAssemblyModule.Virtualize(); - bsAssemblyModule.Strip(); - - 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 StripAllDlls(string outDir) - { - 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 8ceb5bb..46d2a10 100644 --- a/GenericStripper/Modules/IModule.cs +++ b/GenericStripper/Modules/IModule.cs @@ -7,6 +7,6 @@ public interface IModule public string GamePath { get; } 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 d7d00fe..513cdc8 100644 --- a/GenericStripper/Program.cs +++ b/GenericStripper/Program.cs @@ -40,19 +40,19 @@ public override async Task ExecuteAsync(CommandContext context, Settings se 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; }