mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-19 15:51:45 +00:00
120 lines
4.3 KiB
C#
120 lines
4.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using BepInEx.Preloader.Core;
|
|
using Mono.Cecil;
|
|
using MonoMod.Utils;
|
|
|
|
namespace BepInEx.Unity.Mono.Preloader.Utils;
|
|
|
|
internal static class MonoAssemblyHelper
|
|
{
|
|
static MonoAssemblyHelper()
|
|
{
|
|
// We can't use mono's __Internal because on Windows it will use GetModuleHandleW(NULL) that will
|
|
// in turn return the module to the EXE and not mono.dll (at least on Unity versions < 5).
|
|
typeof(MonoAssemblyHelper).ResolveDynDllImports(new()
|
|
{
|
|
["mono"] = new()
|
|
{
|
|
EnvVars.DOORSTOP_MONO_LIB_PATH
|
|
}
|
|
});
|
|
}
|
|
|
|
private static ReadAssemblyResult ReadAssemblyData(string filePath)
|
|
{
|
|
return ReadAssemblyData(File.ReadAllBytes(filePath));
|
|
}
|
|
|
|
private static ReadAssemblyResult ReadAssemblyData(byte[] assemblyData)
|
|
{
|
|
using var ms = new MemoryStream(assemblyData);
|
|
using var ad = AssemblyDefinition.ReadAssembly(ms);
|
|
|
|
return new ReadAssemblyResult
|
|
{
|
|
AssemblyName = ad.Name.Name,
|
|
AssemblyData = assemblyData
|
|
};
|
|
}
|
|
|
|
private static Assembly GetAssemblyByName(string assemblyName)
|
|
{
|
|
return AppDomain.CurrentDomain.GetAssemblies()
|
|
.FirstOrDefault(a => Utility.TryParseAssemblyName(a.FullName, out var name) &&
|
|
name.Name == assemblyName);
|
|
}
|
|
|
|
public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, out Assembly assembly) =>
|
|
Utility.TryResolveDllAssembly(assemblyName, directory, Load, out assembly);
|
|
|
|
public static Assembly LoadFromMemory(byte[] data, string filePath)
|
|
{
|
|
var fullPath = Path.GetFullPath(filePath);
|
|
return ReadAssemblyData(data).Load(fullPath);
|
|
}
|
|
|
|
public static Assembly Load(string filePath)
|
|
{
|
|
var fullPath = Path.GetFullPath(filePath);
|
|
return ReadAssemblyData(fullPath).Load(fullPath);
|
|
}
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
private delegate nint ImageOpenDelegate(nint data,
|
|
uint dataLength,
|
|
bool needCopy,
|
|
out MonoImageOpenStatus status,
|
|
bool refOnly,
|
|
[MarshalAs(UnmanagedType.LPStr)] string name);
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
private delegate nint AssemblyLoadDelegate(nint image,
|
|
[MarshalAs(UnmanagedType.LPStr)] string fileName,
|
|
out MonoImageOpenStatus status,
|
|
bool refOnly);
|
|
|
|
private enum MonoImageOpenStatus
|
|
{
|
|
MONO_IMAGE_OK,
|
|
MONO_IMAGE_ERROR_ERRNO,
|
|
MONO_IMAGE_MISSING_ASSEMBLYREF,
|
|
MONO_IMAGE_IMAGE_INVALID
|
|
}
|
|
|
|
private class ReadAssemblyResult
|
|
{
|
|
public byte[] AssemblyData;
|
|
public string AssemblyName;
|
|
|
|
public unsafe Assembly Load(string fullPath)
|
|
{
|
|
var assembly = GetAssemblyByName(AssemblyName);
|
|
if (assembly != null)
|
|
return assembly;
|
|
|
|
fixed (byte* data = &AssemblyData[0])
|
|
{
|
|
var image = imageOpen((nint) data, (uint) AssemblyData.Length, true,
|
|
out var status, false, fullPath);
|
|
if (status != MonoImageOpenStatus.MONO_IMAGE_OK)
|
|
throw new BadImageFormatException($"Failed to load image {fullPath}: {status}");
|
|
assemblyLoad(image, fullPath, out status, false);
|
|
if (status != MonoImageOpenStatus.MONO_IMAGE_OK)
|
|
throw new BadImageFormatException($"Failed to load assembly {fullPath}: {status}");
|
|
return GetAssemblyByName(AssemblyName);
|
|
}
|
|
}
|
|
}
|
|
#pragma warning disable CS0649
|
|
[DynDllImport("mono", "mono_image_open_from_data_with_name")]
|
|
private static ImageOpenDelegate imageOpen;
|
|
|
|
[DynDllImport("mono", "mono_assembly_load_from_full")]
|
|
private static AssemblyLoadDelegate assemblyLoad;
|
|
#pragma warning restore CS0649
|
|
}
|