NetPreloader.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. using System;
  2. using System.Diagnostics;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using BepInEx.Bootstrap;
  7. using BepInEx.Configuration;
  8. using BepInEx.Logging;
  9. using BepInEx.NetLauncher.RuntimeFixes;
  10. using BepInEx.Preloader.Core;
  11. using BepInEx.Preloader.Core.RuntimeFixes;
  12. using MonoMod.RuntimeDetour;
  13. namespace BepInEx.NetLauncher
  14. {
  15. public static class NetPreloader
  16. {
  17. private static readonly ManualLogSource Log = PreloaderLogger.Log;
  18. public static void Start(string[] args)
  19. {
  20. if (ConfigEntrypointExecutable.Value == null)
  21. {
  22. Log.LogFatal($"Entry executable was not set. Please set this in your config before launching the application");
  23. Program.ReadExit();
  24. return;
  25. }
  26. string executablePath = Path.GetFullPath(ConfigEntrypointExecutable.Value);
  27. if (!File.Exists(executablePath))
  28. {
  29. Log.LogFatal($"Unable to locate executable: {ConfigEntrypointExecutable.Value}");
  30. Program.ReadExit();
  31. return;
  32. }
  33. Paths.SetExecutablePath(executablePath);
  34. Program.ResolveDirectories.Add(Paths.GameRootPath);
  35. TypeLoader.SearchDirectories.Add(Paths.GameRootPath);
  36. bool bridgeInitialized = Utility.TryDo(() =>
  37. {
  38. if (ConfigShimHarmony.Value)
  39. HarmonyDetourBridge.Init();
  40. }, out var harmonyBridgeException);
  41. Logger.Sources.Add(TraceLogSource.CreateSource());
  42. HarmonyFixes.Apply();
  43. string consoleTile = $"BepInEx {typeof(Paths).Assembly.GetName().Version} - {Process.GetCurrentProcess().ProcessName}";
  44. Log.LogMessage(consoleTile);
  45. if (ConsoleManager.ConsoleActive)
  46. ConsoleManager.SetConsoleTitle(consoleTile);
  47. //See BuildInfoAttribute for more information about this section.
  48. object[] attributes = typeof(BuildInfoAttribute).Assembly.GetCustomAttributes(typeof(BuildInfoAttribute), false);
  49. if (attributes.Length > 0)
  50. {
  51. var attribute = (BuildInfoAttribute)attributes[0];
  52. Log.LogMessage(attribute.Info);
  53. }
  54. Log.LogInfo($"CLR runtime version: {Environment.Version}");
  55. if (harmonyBridgeException != null)
  56. Log.LogWarning($"Failed to enable fix for Harmony for .NET Standard API. Error message: {harmonyBridgeException.Message}");
  57. Log.LogMessage("Preloader started");
  58. Assembly entrypointAssembly;
  59. using (var assemblyPatcher = new AssemblyPatcher())
  60. {
  61. assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath);
  62. Log.LogInfo($"{assemblyPatcher.PatcherPlugins.Count} patcher plugin(s) loaded");
  63. assemblyPatcher.LoadAssemblyDirectory(Paths.GameRootPath, "dll", "exe");
  64. Log.LogInfo($"{assemblyPatcher.AssembliesToPatch.Count} assemblies discovered");
  65. assemblyPatcher.PatchAndLoad();
  66. var assemblyName = AssemblyName.GetAssemblyName(executablePath);
  67. entrypointAssembly = assemblyPatcher.LoadedAssemblies.Values.FirstOrDefault(x => x.FullName == assemblyName.FullName);
  68. if (entrypointAssembly != null)
  69. {
  70. Log.LogDebug("Found patched entrypoint assembly! Using it");
  71. }
  72. else
  73. {
  74. Log.LogDebug("Using entrypoint assembly from disk");
  75. entrypointAssembly = Assembly.LoadFrom(executablePath);
  76. }
  77. }
  78. Log.LogMessage("Preloader finished");
  79. var chainloader = new NetChainloader();
  80. chainloader.Initialize();
  81. chainloader.Execute();
  82. AssemblyFix.Execute(entrypointAssembly);
  83. entrypointAssembly.EntryPoint.Invoke(null, new [] { args });
  84. }
  85. #region Config
  86. private static readonly ConfigEntry<string> ConfigEntrypointExecutable = ConfigFile.CoreConfig.Bind<string>(
  87. "Preloader.Entrypoint", "Assembly",
  88. null,
  89. "The local filename of the .NET executable to target.");
  90. private static readonly ConfigEntry<bool> ConfigShimHarmony = ConfigFile.CoreConfig.Bind(
  91. "Preloader", "ShimHarmonySupport",
  92. !Utility.CLRSupportsDynamicAssemblies,
  93. "If enabled, basic Harmony functionality is patched to use MonoMod's RuntimeDetour instead.\nTry using this if Harmony does not work in a game.");
  94. private static readonly ConfigEntry<bool> ConfigPreloaderCOutLogging = ConfigFile.CoreConfig.Bind(
  95. "Logging", "PreloaderConsoleOutRedirection",
  96. true,
  97. "Redirects text from Console.Out during preloader patch loading to the BepInEx logging system.");
  98. #endregion
  99. }
  100. }