diff --git a/BepInEx.IL2CPP/BepInEx.IL2CPP.csproj b/BepInEx.IL2CPP/BepInEx.IL2CPP.csproj
index f1c047b..46aebe2 100644
--- a/BepInEx.IL2CPP/BepInEx.IL2CPP.csproj
+++ b/BepInEx.IL2CPP/BepInEx.IL2CPP.csproj
@@ -26,9 +26,11 @@
-
+
-
+
+
+
diff --git a/BepInEx.IL2CPP/Il2CppInteropManager.cs b/BepInEx.IL2CPP/Il2CppInteropManager.cs
index 04ad888..2947a17 100644
--- a/BepInEx.IL2CPP/Il2CppInteropManager.cs
+++ b/BepInEx.IL2CPP/Il2CppInteropManager.cs
@@ -8,14 +8,19 @@ using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using BepInEx.Configuration;
+using BepInEx.IL2CPP.Logging;
using BepInEx.Logging;
using Cpp2IL.Core;
using HarmonyLib;
using Il2CppInterop.Common;
using Il2CppInterop.Generator;
-using Il2CppInterop.Runtime.XrefScans;
+using Il2CppInterop.Generator.Runners;
+using Il2CppInterop.Runtime.Startup;
using LibCpp2IL;
+using Microsoft.Extensions.Logging;
using Mono.Cecil;
+using LogLevel = Microsoft.Extensions.Logging.LogLevel;
+using MSLoggerFactory = Microsoft.Extensions.Logging.LoggerFactory;
namespace BepInEx.IL2CPP;
@@ -46,6 +51,8 @@ internal static class Il2CppInteropManager
private static readonly ManualLogSource Logger = BepInEx.Logging.Logger.CreateLogSource("InteropManager");
+ private static bool initialized;
+
public static string GameAssemblyPath => Path.Combine(Paths.GameRootPath, "GameAssembly.dll");
private static string HashPath => Path.Combine(IL2CPPInteropAssemblyPath, "assembly-hash.txt");
@@ -54,6 +61,12 @@ internal static class Il2CppInteropManager
internal static string IL2CPPInteropAssemblyPath => Path.Combine(Paths.BepInExRootPath, "interop");
+ private static ILoggerFactory LoggerFactory { get; } = MSLoggerFactory.Create(b =>
+ {
+ b.AddProvider(new BepInExLoggerProvider())
+ .SetMinimumLevel(LogLevel.Trace); // Each BepInEx log listener has its own filtering
+ });
+
private static string ComputeHash()
{
using var md5 = MD5.Create();
@@ -130,21 +143,20 @@ internal static class Il2CppInteropManager
: null;
}
- public static void Initialize()
+ public static void Initialize(Version unityVersion)
{
- GeneratedDatabasesUtil.DatabasesLocationOverride = IL2CPPInteropAssemblyPath;
-
- var interopLogger = BepInEx.Logging.Logger.CreateLogSource("Il2CppInterop");
- Il2CppInterop.Runtime.Logger.InfoHandler += s => interopLogger.LogInfo(s.Trim());
- Il2CppInterop.Runtime.Logger.WarningHandler += s => interopLogger.LogWarning(s.Trim());
- Il2CppInterop.Runtime.Logger.TraceHandler += s => interopLogger.LogDebug(s.Trim());
- Il2CppInterop.Runtime.Logger.ErrorHandler += s => interopLogger.LogError(s.Trim());
+ if (initialized)
+ throw new InvalidOperationException("Already initialized");
+ initialized = true;
+ Environment.SetEnvironmentVariable("IL2CPP_INTEROP_DATABASES_LOCATION", IL2CPPInteropAssemblyPath);
AppDomain.CurrentDomain.AssemblyResolve += ResolveInteropAssemblies;
GenerateInteropAssemblies();
-
- XrefRuntimeScanner.Initialize();
+ var interopLogger = LoggerFactory.CreateLogger("Il2CppInterop");
+ Il2CppInteropRuntime.Create(new() { UnityVersion = unityVersion })
+ .AddLogger(interopLogger)
+ .Start();
}
private static void GenerateInteropAssemblies()
@@ -205,7 +217,6 @@ internal static class Il2CppInteropManager
{
Logger.LogMessage("Running Cpp2IL to generate dummy assemblies");
-
var metadataPath = Path.Combine(Paths.GameRootPath,
$"{Paths.ProcessName}_Data",
"il2cpp_data",
@@ -253,7 +264,6 @@ internal static class Il2CppInteropManager
Source = sourceAssemblies,
OutputDir = IL2CPPInteropAssemblyPath,
UnityBaseLibsDir = Directory.Exists(UnityBaseLibsDirectory) ? UnityBaseLibsDirectory : null,
- NoCopyRuntimeLibs = true
};
var renameMapLocation = Path.Combine(Paths.BepInExRootPath, "DeobfuscationMap.csv.gz");
@@ -265,7 +275,12 @@ internal static class Il2CppInteropManager
Logger.LogInfo("Generating interop assemblies");
- InteropAssemblyGenerator.GenerateInteropAssemblies(opts);
+ var logger = LoggerFactory.CreateLogger("Il2CppInteropGen");
+
+ Il2CppInteropGenerator.Create(opts)
+ .AddLogger(logger)
+ .AddInteropAssemblyGenerator()
+ .Run();
sourceAssemblies.Do(x => x.Dispose());
}
diff --git a/BepInEx.IL2CPP/Logging/BepInExLoggerProvider.cs b/BepInEx.IL2CPP/Logging/BepInExLoggerProvider.cs
new file mode 100644
index 0000000..dca3e1c
--- /dev/null
+++ b/BepInEx.IL2CPP/Logging/BepInExLoggerProvider.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using BepInEx.Logging;
+using Microsoft.Extensions.Logging;
+using BepInExLogLevel = BepInEx.Logging.LogLevel;
+using LogLevel = Microsoft.Extensions.Logging.LogLevel;
+
+namespace BepInEx.IL2CPP.Logging;
+
+internal class BepInExLoggerProvider : ILoggerProvider
+{
+ private readonly List loggers = new();
+
+ public void Dispose()
+ {
+ foreach (var bepInExLogger in loggers)
+ bepInExLogger.Dispose();
+ loggers.Clear();
+ }
+
+ public ILogger CreateLogger(string categoryName)
+ {
+ var logger = new BepInExLogger { SourceName = categoryName };
+ Logger.Sources.Add(logger);
+ loggers.Add(logger);
+ return logger;
+ }
+
+ private class EmptyScope : IDisposable
+ {
+ public void Dispose() { }
+ }
+
+ private class BepInExLogger : ILogSource, ILogger
+ {
+ public void Log(LogLevel logLevel,
+ EventId eventId,
+ TState state,
+ Exception exception,
+ Func formatter) =>
+ LogEvent?.Invoke(this,
+ new LogEventArgs(formatter(state, exception), MSLogLevelToBepInExLogLevel(logLevel),
+ this));
+
+ public bool IsEnabled(LogLevel logLevel) =>
+ (MSLogLevelToBepInExLogLevel(logLevel) & Logger.ListenedLogLevels) != BepInExLogLevel.None;
+
+ public IDisposable BeginScope(TState state) => new EmptyScope();
+
+ public void Dispose() => Logger.Sources.Remove(this);
+
+ public string SourceName { get; init; }
+
+ public event EventHandler LogEvent;
+
+ private static BepInExLogLevel MSLogLevelToBepInExLogLevel(LogLevel logLevel) => logLevel switch
+ {
+ LogLevel.Trace => BepInExLogLevel.Debug,
+ LogLevel.Debug => BepInExLogLevel.Debug,
+ LogLevel.Information => BepInExLogLevel.Info,
+ LogLevel.Warning => BepInExLogLevel.Warning,
+ LogLevel.Error => BepInExLogLevel.Error,
+ LogLevel.Critical => BepInExLogLevel.Fatal,
+ LogLevel.None => BepInExLogLevel.None,
+ var _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null)
+ };
+ }
+}
diff --git a/BepInEx.IL2CPP/Preloader.cs b/BepInEx.IL2CPP/Preloader.cs
index 2f32818..3903bdd 100644
--- a/BepInEx.IL2CPP/Preloader.cs
+++ b/BepInEx.IL2CPP/Preloader.cs
@@ -8,7 +8,6 @@ using BepInEx.Preloader.Core;
using BepInEx.Preloader.Core.Logging;
using BepInEx.Preloader.Core.Patching;
using BepInEx.Preloader.RuntimeFixes;
-using Il2CppInterop.Runtime.Runtime;
namespace BepInEx.IL2CPP;
@@ -58,8 +57,7 @@ public static class Preloader
Log.Log(LogLevel.Debug, $"BepInEx root path: {Paths.BepInExRootPath}");
InitializeUnityVersion();
- Il2CppInteropManager.Initialize();
- UnityVersionHandler.Initialize(UnityVersion.Major, UnityVersion.Minor, UnityVersion.Build);
+ Il2CppInteropManager.Initialize(UnityVersion);
using (var assemblyPatcher = new AssemblyPatcher())
{