using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using BepInEx.Bootstrap; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.NET.Common; using BepInEx.NET.CoreCLR; using BepInEx.NET.Shared; using BepInEx.Preloader.Core; using BepInEx.Preloader.Core.Logging; using BepInEx.Preloader.Core.Patching; using Mono.Cecil; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyCompany("BepInEx")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2022 BepInEx Team")] [assembly: AssemblyDescription("BepInEx support library for CoreCLR games")] [assembly: AssemblyFileVersion("6.0.0.0")] [assembly: AssemblyInformationalVersion("6.0.0+802b84c947c2b8ab870be453776f7ab895d8cf09")] [assembly: AssemblyProduct("BepInEx.NET.CoreCLR")] [assembly: AssemblyTitle("BepInEx.NET.CoreCLR")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ibox233/BepinEx-6-CoreCLR-For-Romestead")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("6.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] internal class StartupHook { public static List ResolveDirectories = new List(); public static string DoesNotExistPath = "_doesnotexist_.exe"; private static int initialized; private static Mutex initializationMutex; private const string AppDomainInitializedKey = "Romestead.BepInEx.NET.CoreCLR.StartupHook.Initialized"; public static void Initialize() { if (!TryBeginInitialize()) { return; } string text = $"bepinex_preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"; try { string fileName = Process.GetCurrentProcess().MainModule.FileName; string text2 = TryDetermineAssemblyNameFromDotnet(fileName) ?? TryDetermineAssemblyNameFromStubExecutable(fileName) ?? TryDetermineAssemblyNameFromCurrentAssembly(fileName); string text3 = null; if (text2 != null) { text3 = Path.GetDirectoryName(text2); } string text4 = TryDetermineBepInExRootFromDoorstopTarget() ?? TryDetermineBepInExRootFromCurrentAssembly() ?? TryDetermineBepInExRootFromGameDirectory(text3); string text5 = null; if (text4 != null) { text5 = Path.Combine(text4, "core"); } if (text2 == null || text3 == null || !Directory.Exists(text5)) { throw new Exception("Could not determine game location, or BepInEx install location"); } text = Path.Combine(text3, text); ResolveDirectories.Add(text5); AppDomain.CurrentDomain.AssemblyResolve += SharedEntrypoint.RemoteResolve(ResolveDirectories); NetCorePreloaderRunner.OuterMain(text2, text4); } catch (Exception value) { string text6 = null; string text7 = null; try { text6 = Process.GetCurrentProcess().MainModule?.FileName; text7 = string.Join(' ', Environment.GetCommandLineArgs()); } catch { } string contents = $"Unhandled fatal exception\r\nExecutable location: {text6 ?? ""}\r\nArguments: {text7 ?? ""}\r\n{value}"; File.WriteAllText(text, contents); Console.WriteLine("Unhandled exception"); Console.WriteLine("Executable location: " + (text6 ?? "")); Console.WriteLine("Arguments: " + (text7 ?? "")); Console.WriteLine(value); } } private static bool TryBeginInitialize() { if (Interlocked.Exchange(ref initialized, 1) == 1) { return false; } if (AppDomain.CurrentDomain.GetData("Romestead.BepInEx.NET.CoreCLR.StartupHook.Initialized") != null) { return false; } try { string name = $"Romestead.BepInEx.NET.CoreCLR.StartupHook.{Environment.ProcessId}"; initializationMutex = new Mutex(initiallyOwned: true, name, out var createdNew); if (!createdNew) { return false; } } catch { } AppDomain.CurrentDomain.SetData("Romestead.BepInEx.NET.CoreCLR.StartupHook.Initialized", true); return true; } private static string TryDetermineAssemblyNameFromDotnet(string executableFilename) { if (Path.GetFileNameWithoutExtension(executableFilename) == "dotnet") { string[] commandLineArgs = Environment.GetCommandLineArgs(); foreach (string text in commandLineArgs) { if ((text.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || text.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) && File.Exists(text)) { return Path.GetFullPath(text); } } } return null; } private static string TryDetermineAssemblyNameFromStubExecutable(string executableFilename) { string text = Path.ChangeExtension(executableFilename, ".dll"); if (File.Exists(text)) { return text; } return null; } private static string TryDetermineAssemblyNameFromCurrentAssembly(string executableFilename) { string directoryName = Path.GetDirectoryName(typeof(StartupHook).Assembly.Location.Replace('/', Path.DirectorySeparatorChar)); if (directoryName == null) { return null; } string directoryName2 = Path.GetDirectoryName(Path.GetDirectoryName(directoryName)); if (directoryName2 == null) { return null; } return Path.Combine(directoryName2, DoesNotExistPath); } private static string TryDetermineBepInExRootFromDoorstopTarget() { string text = GetCommandLineArgValue("--doorstop-target") ?? GetCommandLineArgValue("--doorstop-target-assembly") ?? GetCommandLineArgValue("--doorstop_target_assembly"); if (string.IsNullOrWhiteSpace(text)) { return null; } return TryDetermineBepInExRootFromTargetAssembly(text); } private static string TryDetermineBepInExRootFromTargetAssembly(string targetAssembly) { string fullPath; try { fullPath = Path.GetFullPath(targetAssembly); } catch { return null; } if (!File.Exists(fullPath)) { return null; } if (!string.Equals(Path.GetFileName(fullPath), "BepInEx.NET.CoreCLR.dll", StringComparison.OrdinalIgnoreCase)) { return null; } string directoryName = Path.GetDirectoryName(fullPath); if (directoryName == null) { return null; } if (string.Equals(Path.GetFileName(directoryName), "core", StringComparison.OrdinalIgnoreCase)) { string text = ParentDirectory(fullPath, 2); if (HasBepInExCoreDirectory(text)) { return text; } } return TryDetermineBepInExRootFromGameDirectory(directoryName); } private static string TryDetermineBepInExRootFromCurrentAssembly() { return TryDetermineBepInExRootFromTargetAssembly(typeof(StartupHook).Assembly.Location.Replace('/', Path.DirectorySeparatorChar)); } private static string TryDetermineBepInExRootFromGameDirectory(string gameDirectory) { if (string.IsNullOrWhiteSpace(gameDirectory)) { return null; } string text = Path.Combine(gameDirectory, "BepInEx"); if (!HasBepInExCoreDirectory(text)) { return null; } return text; } private static bool HasBepInExCoreDirectory(string bepinexRoot) { if (!string.IsNullOrWhiteSpace(bepinexRoot)) { return Directory.Exists(Path.Combine(bepinexRoot, "core")); } return false; } private static string GetCommandLineArgValue(string name) { string[] commandLineArgs = Environment.GetCommandLineArgs(); for (int i = 1; i < commandLineArgs.Length - 1; i++) { if (string.Equals(commandLineArgs[i], name, StringComparison.OrdinalIgnoreCase)) { return commandLineArgs[i + 1]; } } return null; } private static string ParentDirectory(string path, int levels) { for (int i = 0; i < levels; i++) { path = Path.GetDirectoryName(path); } return path; } } namespace BepInEx.NET.Shared { internal static class SharedEntrypoint { public static ResolveEventHandler RemoteResolve(List resolveDirectories) { return (object? sender, ResolveEventArgs args) => RemoteResolveInternal(sender, args, resolveDirectories); } private static Assembly RemoteResolveInternal(object sender, ResolveEventArgs reference, List resolveDirectories) { AssemblyName assemblyName = new AssemblyName(reference.Name); foreach (string resolveDirectory in resolveDirectories) { if (!Directory.Exists(resolveDirectory)) { continue; } List list = new List { resolveDirectory }; list.AddRange(Directory.GetDirectories(resolveDirectory, "*", SearchOption.AllDirectories)); foreach (string item in list.Select((string x) => Path.Combine(x, assemblyName.Name + ".dll")).Concat(list.Select((string x) => Path.Combine(x, assemblyName.Name + ".exe")))) { if (File.Exists(item)) { Assembly assembly; try { assembly = Assembly.LoadFrom(item); } catch (Exception) { continue; } if (assembly.GetName().Name == assemblyName.Name) { return assembly; } } } } return null; } public static Assembly LocalResolve(object sender, ResolveEventArgs args) { AssemblyName assemblyName = new AssemblyName(args.Name); Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly x) => x.GetName().Name == assemblyName.Name); if (assembly != null) { return assembly; } if (LocalUtility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, out assembly) || LocalUtility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, out assembly) || LocalUtility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out assembly)) { return assembly; } return null; } } internal static class LocalUtility { private static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, Func loader, out T assembly) where T : class { assembly = null; if (!Directory.Exists(directory)) { return false; } List list = new List(); list.Add(directory); list.AddRange(Directory.GetDirectories(directory, "*", SearchOption.AllDirectories)); foreach (string item in list) { string text = Path.Combine(item, assemblyName.Name + ".dll"); if (File.Exists(text)) { try { assembly = loader(text); } catch (Exception) { continue; } return true; } } return false; } public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, out Assembly assembly) { return TryResolveDllAssembly(assemblyName, directory, Assembly.LoadFrom, out assembly); } } } namespace BepInEx.NET.CoreCLR { internal static class NetCorePreloaderRunner { internal static void PreloaderMain() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown ConsoleManager.Initialize(false, true); if (ConsoleManager.ConsoleEnabled) { ConsoleManager.CreateConsole(); Logger.Listeners.Add((ILogListener)new ConsoleLogListener()); } try { NetCorePreloader.Start(); } catch (Exception ex) { PreloaderLogger.Log.Log((LogLevel)1, (object)"Unhandled exception"); PreloaderLogger.Log.Log((LogLevel)1, (object)ex); } } internal static void OuterMain(string filename, string bepinexRootPath) { PlatformUtils.SetPlatform(); Paths.SetDotNetGamePath(filename, bepinexRootPath); AppDomain.CurrentDomain.AssemblyResolve += SharedEntrypoint.LocalResolve; PreloaderMain(); } } public static class NativeEntrypoint { public static int Initialize(nint args, int sizeBytes) { StartupHook.Initialize(); return 0; } } internal class NetCorePreloader { private static readonly ManualLogSource Log = PreloaderLogger.Log; public static void Start() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Expected O, but got Unknown //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Expected O, but got Unknown //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Expected O, but got Unknown //IL_02ac: Expected O, but got Unknown PreloaderConsoleListener item = new PreloaderConsoleListener(); Logger.Listeners.Add((ILogListener)(object)item); string text = ((!Paths.ExecutablePath.EndsWith(StartupHook.DoesNotExistPath)) ? Paths.ExecutablePath : null); TypeLoader.SearchDirectories.Add(Paths.GameRootPath); Logger.Sources.Add(TraceLogSource.CreateSource()); ChainloaderLogHelper.PrintLogInfo(Log); bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(21, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CLR runtime version: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Environment.Version); } Log.LogInfo(val); val = new BepInExInfoLogInterpolatedStringHandler(20, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Current executable: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Process.GetCurrentProcess().MainModule.FileName); } Log.LogInfo(val); val = new BepInExInfoLogInterpolatedStringHandler(21, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Entrypoint assembly: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text ?? ""); } Log.LogInfo(val); val = new BepInExInfoLogInterpolatedStringHandler(18, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Launch arguments: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(string.Join(' ', Environment.GetCommandLineArgs())); } Log.LogInfo(val); if (text != null) { AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(text); AssemblyBuildInfo val3; try { val3 = AssemblyBuildInfo.DetermineInfo(val2); } finally { ((IDisposable)val2)?.Dispose(); } val = new BepInExInfoLogInterpolatedStringHandler(36, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Game executable build architecture: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(val3); } Log.LogInfo(val); } else { Log.LogWarning((object)"Game assembly is unknown, can't determine build architecture"); } Log.LogMessage((object)"Preloader started"); AssemblyPatcher val4 = new AssemblyPatcher((Func)((byte[] data, string _) => Assembly.Load(data))); try { val4.AddPatchersFromDirectory(Paths.PatcherPluginPath); val = new BepInExInfoLogInterpolatedStringHandler(29, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(val4.PatcherContext.PatchDefinitions.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" patcher definition(s) loaded"); } Log.LogInfo(val); val4.LoadAssemblyDirectories((IEnumerable)new string[1] { Paths.GameRootPath }, (IEnumerable)new string[2] { "dll", "exe" }); val = new BepInExInfoLogInterpolatedStringHandler(22, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(val4.PatcherContext.AvailableAssemblies.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" assemblies discovered"); } Log.LogInfo(val); val4.PatchAndLoad(); } finally { ((IDisposable)val4)?.Dispose(); } Log.LogMessage((object)"Preloader finished"); Logger.Listeners.Remove((ILogListener)(object)item); NetChainloader val5 = new NetChainloader(); ((BaseChainloader)val5).Initialize(); ((BaseChainloader)val5).Execute(); } } }