Skip to content

Commit

Permalink
feat: init
Browse files Browse the repository at this point in the history
  • Loading branch information
ChecksumDev committed Aug 27, 2023
0 parents commit 56fe29b
Show file tree
Hide file tree
Showing 10 changed files with 395 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.idea/
.vscode/
/packages/
bin/
obj/
riderModule.iml
/_ReSharper.Caches/
*.user
16 changes: 16 additions & 0 deletions GenericStripper.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenericStripper", "GenericStripper\GenericStripper.csproj", "{512279BF-4F83-447E-B818-CBF94378E222}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{512279BF-4F83-447E-B818-CBF94378E222}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{512279BF-4F83-447E-B818-CBF94378E222}.Debug|Any CPU.Build.0 = Debug|Any CPU
{512279BF-4F83-447E-B818-CBF94378E222}.Release|Any CPU.ActiveCfg = Release|Any CPU
{512279BF-4F83-447E-B818-CBF94378E222}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
19 changes: 19 additions & 0 deletions GenericStripper/GenericStripper.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>


<ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.11.5"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="Spectre.Console" Version="0.47.1-preview.0.11"/>
<PackageReference Include="Spectre.Console.Cli" Version="0.47.1-preview.0.11"/>
</ItemGroup>


</Project>
95 changes: 95 additions & 0 deletions GenericStripper/Modules/BeatSaber/BSAssemblyModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using Mono.Cecil;

namespace GenericStripper.Modules.BeatSaber;

public class BsAssemblyModule
{
private readonly BsLibLoader _bslibLoader;
private readonly FileInfo _file;
private readonly ModuleDefinition _module;

public BsAssemblyModule(string gamePath, string fileName, params string[] resolverDirs)
{
_bslibLoader = new BsLibLoader(gamePath);

_file = new FileInfo(fileName);
if (!_file.Exists) throw new FileNotFoundException("Failed to find assembly to strip!", fileName);

_module = LoadModules(resolverDirs);
}

private ModuleDefinition LoadModules(IEnumerable<string> directories)
{
_bslibLoader.AddSearchDirectory(_file.Directory?.FullName ??
throw new Exception("Failed to get assembly directory!"));
foreach (var directory in directories)
{
if (!Directory.Exists(directory)) continue;
_bslibLoader.AddSearchDirectory(directory);
}

ReaderParameters parameters = new()
{
AssemblyResolver = _bslibLoader,
ReadingMode = ReadingMode.Immediate,
ReadWrite = false,
InMemory = true
};

return ModuleDefinition.ReadModule(_file.FullName, parameters);
}

public void Virtualize()
{
foreach (var type in _module.Types) VirtualizeType(type);
}

private static void VirtualizeType(TypeDefinition type)
{
if (type.IsSealed) type.IsSealed = false;

if (type.IsInterface) return;
if (type.IsAbstract) return;

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
}))
{
m.IsVirtual = true;
m.IsPublic = true;
m.IsPrivate = false;
m.IsNewSlot = true;
m.IsHideBySig = true;
}

foreach (var field in type.Fields.Where(field => field.IsPrivate)) field.IsFamily = true;
}

public void Strip()
{
foreach (var type in _module.Types) StripType(type);
}

private static void StripType(TypeDefinition type)
{
foreach (var m in type.Methods.Where(m => m.Body != null))
{
m.Body.Instructions.Clear();
m.Body.InitLocals = false;
m.Body.Variables.Clear();
}

foreach (var subType in type.NestedTypes) StripType(subType);
}

public void Write(string outFile)
{
_module.Write(outFile);
}
}
28 changes: 28 additions & 0 deletions GenericStripper/Modules/BeatSaber/BSLibLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Mono.Cecil;

namespace GenericStripper.Modules.BeatSaber;

public class BsLibLoader : BaseAssemblyResolver
{
public BsLibLoader(string gamePath)
{
_ = new LibLoader(gamePath);
}

public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
LibLoader.SetupAssemblyFilenames();
if (LibLoader.FilenameLocations == null) throw new Exception("Failed to setup assembly filenames!");

if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.{name.Version}.dll", out var path))
{
if (File.Exists(path)) return AssemblyDefinition.ReadAssembly(path, parameters);
}
else if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.dll", out path))
{
if (File.Exists(path)) return AssemblyDefinition.ReadAssembly(path, parameters);
}

return base.Resolve(name, parameters);
}
}
81 changes: 81 additions & 0 deletions GenericStripper/Modules/BeatSaber/BeatSaber.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Diagnostics;
using System.IO.Compression;
using System.Net;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace GenericStripper.Modules.BeatSaber;

public class BeatSaber : IModule
{
private readonly HttpClient _client;

public BeatSaber(string gamePath)
{
GamePath = gamePath;
_client = new HttpClient();
_client.DefaultRequestHeaders.Add("User-Agent", "GenericStripper");
}

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();

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()
{
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<Dictionary<string, dynamic>>(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();
}
}
10 changes: 10 additions & 0 deletions GenericStripper/Modules/IModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace GenericStripper.Modules;

public interface IModule
{
public string GameName { get; }

public string GamePath { get; }

public void StripDll(string file, string outDir, params string[] resolveDirs);
}
69 changes: 69 additions & 0 deletions GenericStripper/Modules/LibLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

namespace GenericStripper.Modules;

[SuppressMessage("Interoperability",
"SYSLIB1054:Use \'LibraryImportAttribute\' instead of \'DllImportAttribute\' to generate P/Invoke marshalling code at compile time")]
public class LibLoader
{
public LibLoader(string gamePath)
{
GamePath = gamePath;
}

private static string GamePath { get; set; } = string.Empty;
private static string LibPath => Path.Combine(GamePath, "Libs");
private static string NativePath => Path.Combine(LibPath, "Native");
internal static Dictionary<string, string>? FilenameLocations { get; private set; }

public static void SetupAssemblyFilenames()
{
if (FilenameLocations != null) return;

FilenameLocations = new Dictionary<string, string>();
var files = Directory.GetFiles(LibPath, "*.dll", SearchOption.AllDirectories);
files = files.Where(f => !f.StartsWith(NativePath)).ToArray();

foreach (var file in files)
{
var filename = Path.GetFileName(file);
FilenameLocations[filename] = file;
}

if (Directory.Exists(NativePath))
{
var ptr = AddDllDirectory(NativePath);
if (ptr == IntPtr.Zero) throw new Exception("Failed to add Native directory to DLL search path!");

var nativeDirectories = Directory.GetDirectories(NativePath, "*", SearchOption.AllDirectories);
foreach (var nativeDirectory in nativeDirectories)
{
ptr = AddDllDirectory(nativeDirectory);
if (ptr == IntPtr.Zero)
throw new Exception($"Failed to add Native directory to DLL search path: {nativeDirectory}");
}
}

var unityData = Directory.EnumerateDirectories(GamePath, "*_Data", SearchOption.TopDirectoryOnly).First();
var unityPlugins = Path.Combine(unityData, "Plugins");
if (Directory.Exists(unityPlugins))
{
var ptr = AddDllDirectory(unityPlugins);
if (ptr == IntPtr.Zero)
throw new Exception($"Failed to add Unity Plugins directory to DLL search path: {unityPlugins}");
}

foreach (var dir in Environment.GetEnvironmentVariable("path")?.Split(Path.PathSeparator) ??
Array.Empty<string>())
{
if (!Directory.Exists(dir)) continue;

var ptr = AddDllDirectory(dir);
if (ptr == IntPtr.Zero) throw new Exception($"Failed to add directory to DLL search path: {dir}");
}
}

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr AddDllDirectory(string lpPathName);
}
Loading

0 comments on commit 56fe29b

Please sign in to comment.