Decompiled source of StartupAccelerator v1.0.3

patchers/StartupAccelerator.dll

Decompiled 2 years ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Preloader;
using HarmonyLib;
using HarmonyLib.Public.Patching;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("StartupAccelerator")]
[assembly: AssemblyDescription("https://valheim.thunderstore.io/package/Smoothbrain/StartupAccelerator")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("StartupAccelerator")]
[assembly: AssemblyCopyright("Copyright ©  2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("962424CE-1773-46A0-A340-36A173D2DF0C")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace StartupAccelerator
{
	public static class StartupAccelerator
	{
		private enum Toggle
		{
			On,
			Off
		}

		[HarmonyPatch]
		private static class Patch_Preloader_PatchEntrypoint
		{
			private static readonly MethodInfo ChainloaderFinishedCallInstructionAdder = AccessTools.DeclaredMethod(typeof(Patch_Preloader_PatchEntrypoint), "AddChainloaderFinishedCall", (Type[])null, (Type[])null);

			private static readonly MethodInfo ChainloaderStart = AccessTools.DeclaredMethod(typeof(Patch_Preloader_PatchEntrypoint), "PreChainloader", (Type[])null, (Type[])null);

			private static readonly MethodInfo ILInstructionInserter = AccessTools.DeclaredMethod(typeof(ILProcessor), "InsertBefore", (Type[])null, (Type[])null);

			private static bool patched = false;

			private static IEnumerable<MethodInfo> TargetMethods()
			{
				return new MethodInfo[1] { AccessTools.DeclaredMethod(typeof(EnvVars).Assembly.GetType("BepInEx.Preloader.Preloader"), "PatchEntrypoint", (Type[])null, (Type[])null) };
			}

			private static void AddChainloaderFinishedCall(ILProcessor ilProcessor, Instruction instruction, AssemblyDefinition assembly)
			{
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				ilProcessor.InsertBefore(instruction, ilProcessor.Create(OpCodes.Call, assembly.MainModule.ImportReference((MethodBase)ChainloaderStart)));
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				bool foundInitMethod = false;
				foreach (CodeInstruction instruction in instructions)
				{
					yield return instruction;
					if (instruction.opcode == OpCodes.Ldloc_S && instruction.operand is LocalBuilder localBuilder && localBuilder.LocalIndex == 6)
					{
						foundInitMethod = true;
					}
					else if (foundInitMethod && CodeInstructionExtensions.Calls(instruction, ILInstructionInserter))
					{
						yield return new CodeInstruction(OpCodes.Ldloc_S, (object)11);
						yield return new CodeInstruction(OpCodes.Ldloc_S, (object)12);
						yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
						yield return new CodeInstruction(OpCodes.Ldind_Ref, (object)null);
						yield return new CodeInstruction(OpCodes.Call, (object)ChainloaderFinishedCallInstructionAdder);
					}
				}
			}

			private static void PreChainloader()
			{
				if (!patched)
				{
					patched = true;
					if (delayedPatcher.Value == Toggle.On)
					{
						delayedPatcherHarmony.PatchAll(typeof(InterceptChainloader));
					}
					if (unifiedLocalization.Value == Toggle.On)
					{
						harmony.PatchAll(typeof(InterceptLocalization));
						harmony.PatchAll(typeof(InterceptLanguageLoad));
					}
					if (optimizeConfigSave.Value == Toggle.On)
					{
						harmony.PatchAll(typeof(DelayConfigSave));
						harmony.PatchAll(typeof(ChangeConfigSaveBack));
						harmony.PatchAll(typeof(SkipManuallyChangedSaveOnConfigSet));
					}
				}
			}
		}

		[HarmonyPatch]
		private static class InterceptLocalization
		{
			private static readonly Dictionary<string, Dictionary<string, string>> localizationCache = new Dictionary<string, Dictionary<string, string>>();

			private static readonly HashSet<MethodBase> alreadyAppliedEnglishLoadCSV = new HashSet<MethodBase>();

			private static MethodInfo TargetMethod()
			{
				return AccessTools.DeclaredMethod(Type.GetType("Localization, assembly_guiutils"), "SetupLanguage", (Type[])null, (Type[])null);
			}

			[HarmonyPriority(0)]
			private static bool Prefix(object __instance, string language, ref Dictionary<string, string> ___m_translations, ref bool __result)
			{
				if (localizationCache.TryGetValue(language, out Dictionary<string, string> value))
				{
					___m_translations = value;
					if (language == "English" && (string)AccessTools.DeclaredMethod(Type.GetType("UnityEngine.PlayerPrefs, UnityEngine.CoreModule"), "GetString", new Type[2]
					{
						typeof(string),
						typeof(string)
					}, (Type[])null).Invoke(null, new object[2] { "language", "English" }) != "English")
					{
						Patch[] postfixes = PatchManager.ToPatchInfo((MethodBase)AccessTools.DeclaredMethod(Type.GetType("Localization, assembly_guiutils"), "LoadCSV", (Type[])null, (Type[])null)).postfixes;
						foreach (Patch val in postfixes)
						{
							if (alreadyAppliedEnglishLoadCSV.Contains(val.PatchMethod))
							{
								continue;
							}
							ParameterInfo[] parameters = val.PatchMethod.GetParameters();
							object[] array = new object[parameters.Length];
							int num = 0;
							ParameterInfo[] array2 = parameters;
							foreach (ParameterInfo parameterInfo in array2)
							{
								if (parameterInfo.Name == "__instance")
								{
									array[num] = __instance;
								}
								else if (parameterInfo.Name == "language")
								{
									array[num] = language;
								}
								else if (parameterInfo.Name == "__result")
								{
									array[num] = true;
								}
								else if (parameterInfo.Name.StartsWith("___"))
								{
									array[num] = AccessTools.Field(__instance.GetType(), parameterInfo.Name.Substring(3)).GetValue(__instance);
								}
								num++;
							}
							val.PatchMethod.Invoke(null, array);
							alreadyAppliedEnglishLoadCSV.Add(val.PatchMethod);
						}
					}
					__result = true;
					return false;
				}
				if (language != "English")
				{
					___m_translations = new Dictionary<string, string>(___m_translations);
				}
				return true;
			}

			[HarmonyPriority(0)]
			private static void Postfix(string language, Dictionary<string, string> ___m_translations, bool __result)
			{
				if (__result)
				{
					localizationCache[language] = ___m_translations;
				}
			}
		}

		[HarmonyPatch]
		private static class InterceptLanguageLoad
		{
			private static MethodInfo TargetMethod()
			{
				return AccessTools.DeclaredMethod(Type.GetType("Localization, assembly_guiutils"), "LoadLanguages", (Type[])null, (Type[])null);
			}

			[HarmonyPriority(0)]
			private static bool Prefix(ref List<string> __result)
			{
				Type type = Type.GetType("Localization, assembly_guiutils");
				object value = AccessTools.DeclaredField(type, "m_instance").GetValue(null);
				if (value != null)
				{
					__result = (List<string>)AccessTools.DeclaredMethod(type, "GetLanguages", (Type[])null, (Type[])null).Invoke(value, Array.Empty<object>());
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch]
		private static class InterceptChainloader
		{
			private static bool postChainloader = false;

			private static bool doNotSkipUpdate = false;

			private static readonly HashSet<MethodBase> methods = new HashSet<MethodBase>();

			private static readonly MethodInfo patchSkip = AccessTools.DeclaredMethod(typeof(InterceptChainloader), "SkipUpdates", (Type[])null, (Type[])null);

			private static readonly MethodInfo addReplacementOriginal = AccessTools.DeclaredMethod(typeof(PatchManager), "AddReplacementOriginal", (Type[])null, (Type[])null);

			private static MethodInfo TargetMethod()
			{
				return postChainloader ? AccessTools.DeclaredMethod(Type.GetType("FejdStartup, assembly_valheim"), "Awake", (Type[])null, (Type[])null) : AccessTools.DeclaredMethod(typeof(Chainloader), "Start", (Type[])null, (Type[])null);
			}

			private static bool SkipUpdates(MethodBase original, ref MethodInfo? __result)
			{
				if (!doNotSkipUpdate)
				{
					Type declaringType = original.DeclaringType;
					if ((object)declaringType == null || !passthroughClasses.Contains(declaringType.FullName))
					{
						methods.Add(original);
						__result = null;
						return false;
					}
				}
				return true;
			}

			[HarmonyPriority(800)]
			public static void Prefix()
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Expected O, but got Unknown
				doNotSkipUpdate = false;
				delayedPatcherHarmony.Patch((MethodBase)harmonyPatcher, new HarmonyMethod(patchSkip), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}

			[HarmonyPriority(0)]
			public static void Postfix()
			{
				doNotSkipUpdate = true;
				string arg;
				if (postChainloader)
				{
					delayedPatcherHarmony.UnpatchSelf();
					arg = "after FejdStartup.Awake";
				}
				else
				{
					delayedPatcherHarmony.Unpatch((MethodBase)TargetMethod(), (HarmonyPatchType)0, delayedPatcherHarmony.Id);
					postChainloader = true;
					delayedPatcherHarmony.PatchAll(typeof(InterceptChainloader));
					arg = "after Chainloader end";
				}
				Stopwatch stopwatch = new Stopwatch();
				stopwatch.Start();
				lock (AccessTools.DeclaredField(typeof(PatchProcessor), "locker").GetValue(null))
				{
					foreach (MethodBase method in methods)
					{
						object obj = harmonyPatcher.Invoke(null, new object[2]
						{
							method,
							PatchManager.ToPatchInfo(method)
						});
						addReplacementOriginal.Invoke(null, new object[2] { method, obj });
					}
				}
				logger.LogInfo((object)$"Batch-patched {methods.Count} methods {arg} in {stopwatch.ElapsedMilliseconds} ms");
				methods.Clear();
			}
		}

		[HarmonyPatch]
		private static class DelayConfigSave
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.DeclaredConstructor(typeof(ConfigFile), new Type[3]
				{
					typeof(string),
					typeof(bool),
					typeof(BepInPlugin)
				}, false);
			}

			private static void Postfix(ConfigFile __instance)
			{
				__instance.SaveOnConfigSet = false;
				changedConfigFiles.Add(__instance);
			}
		}

		[HarmonyPatch]
		private static class ChangeConfigSaveBack
		{
			private static MethodInfo TargetMethod()
			{
				return AccessTools.DeclaredMethod(Type.GetType("FejdStartup, assembly_valheim"), "Awake", (Type[])null, (Type[])null);
			}

			private static void Prefix()
			{
				List<ConfigFile> changedConfigFiles = StartupAccelerator.changedConfigFiles;
				StartupAccelerator.changedConfigFiles = new List<ConfigFile>();
				foreach (ConfigFile item in changedConfigFiles)
				{
					item.Save();
					item.SaveOnConfigSet = true;
				}
			}
		}

		[HarmonyPatch]
		private static class SkipManuallyChangedSaveOnConfigSet
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.DeclaredPropertySetter(typeof(ConfigFile), "SaveOnConfigSet");
			}

			private static void Prefix(ConfigFile __instance)
			{
				if (changedConfigFiles.Remove(__instance))
				{
					__instance.Save();
				}
			}
		}

		private const string CONFIG_FILE_NAME = "StartupAccelerator.cfg";

		private static readonly ConfigFile Config = new ConfigFile(Path.Combine(Paths.ConfigPath, "StartupAccelerator.cfg"), true);

		private static readonly ManualLogSource logger = Logger.CreateLogSource("StartupAccelerator");

		private static readonly Harmony delayedPatcherHarmony = new Harmony("org.bepinex.patchers.startupaccelerator.delayed_patcher");

		private static readonly Harmony harmony = new Harmony("org.bepinex.patchers.startupaccelerator");

		private static readonly MethodInfo harmonyPatcher = AccessTools.DeclaredMethod(typeof(Harmony).Assembly.GetType("HarmonyLib.PatchFunctions"), "UpdateWrapper", (Type[])null, (Type[])null);

		private static readonly string[] hardcodedPassthrough = new string[4]
		{
			typeof(Assembly).FullName,
			"BepInEx.Preloader.RuntimeFixes.HarmonyInteropFix",
			"BepInEx.PluginInfo",
			typeof(Enum).FullName
		};

		private static HashSet<string> passthroughClasses = new HashSet<string>();

		private static readonly ConfigEntry<Toggle> delayedPatcher = Config.Bind<Toggle>("General", "Delay Patching", Toggle.On, new ConfigDescription("Delay Harmony patching until after Chainloader and after FejdStartup.Awake respectively.", (AcceptableValueBase)null, Array.Empty<object>()));

		private static readonly ConfigEntry<Toggle> unifiedLocalization = Config.Bind<Toggle>("General", "Merge Localization Data", Toggle.On, new ConfigDescription("Merge localization data to avoid re-reading it over and over.", (AcceptableValueBase)null, Array.Empty<object>()));

		private static readonly ConfigEntry<Toggle> optimizeConfigSave = Config.Bind<Toggle>("General", "Delay Config Save", Toggle.On, new ConfigDescription("Delay config save so that it saves the config once after start up and not over and over again.", (AcceptableValueBase)null, Array.Empty<object>()));

		private static List<ConfigFile> changedConfigFiles = new List<ConfigFile>();

		public static IEnumerable<string> TargetDLLs { get; } = Array.Empty<string>();


		public static void Patch(AssemblyDefinition assembly)
		{
		}

		public static void Initialize()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			ConfigEntry<string> passthrough = Config.Bind<string>("General", "Passthrough Patched Classes", "", new ConfigDescription("Comma-separated list of classes to unconditionally patch immediately.", (AcceptableValueBase)null, Array.Empty<object>()));
			passthrough.SettingChanged += delegate
			{
				calcPassthrough();
			};
			calcPassthrough();
			harmony.PatchAll(typeof(Patch_Preloader_PatchEntrypoint));
			void calcPassthrough()
			{
				HashSet<string> hashSet = new HashSet<string>();
				foreach (string item in hardcodedPassthrough.Concat((unifiedLocalization.Value != 0) ? ((IEnumerable<string>)Array.Empty<string>()) : ((IEnumerable<string>)new string[1] { "Localization" })).Concat(passthrough.Value.Split(new char[1] { ',' })))
				{
					hashSet.Add(item);
				}
				passthroughClasses = hashSet;
			}
		}
	}
}