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.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Damntry.Utils.Events; using Damntry.Utils.ExtensionMethods; using Damntry.Utils.Logging; using Damntry.Utils.Reflection; using Damntry.UtilsBepInEx.Configuration; using Damntry.UtilsBepInEx.Configuration.ConfigurationManager; using Damntry.UtilsBepInEx.Configuration.ConfigurationManager.Patch; using Damntry.UtilsBepInEx.Configuration.ConfigurationManager.SettingAttributes; using Damntry.UtilsBepInEx.HarmonyPatching.Attributes; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Attributes; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.BaseClasses; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Interfaces; using Damntry.UtilsBepInEx.HarmonyPatching.Exceptions; using Damntry.UtilsUnity.Components.InputManagement; using Damntry.UtilsUnity.Components.InputManagement.Model; using HarmonyLib; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; using MonoMod.Utils; using Newtonsoft.Json; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Damntry BepInEx Globals")] [assembly: AssemblyDescription("Utils usable in any type of BepInEx project")] [assembly: AssemblyCompany("Damntry")] [assembly: AssemblyProduct("Damntry BepInEx Globals")] [assembly: AssemblyCopyright("Copyright © Damntry 2026")] [assembly: AssemblyFileVersion("0.6.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyVersion("0.6.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] internal sealed class ParamCollectionAttribute : Attribute { } [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 Damntry.UtilsBepInEx.ModHelpers { public abstract class ExternalModHelper { protected enum ModLoadStatus { NotLoaded, DifferentVersion, LoadedOk } protected ModLoadStatus ModStatus { get; private set; } protected PluginInfo ModPluginInfo { get; private set; } public bool IsModLoadedAndEnabled => ModStatus != ModLoadStatus.NotLoaded; public ModInfoData ModInfo { get; private set; } private ExternalModHelper() { } public ExternalModHelper(string GUID, string modName, Version supportedVersion) { checkArgsForErrors(GUID, modName, supportedVersion); Init(GUID, modName, supportedVersion); } public ExternalModHelper(string GUID, string modName, string supportedVersion) { checkArgsForErrors(GUID, modName, supportedVersion, out var parsedSupportedVersion); Init(GUID, modName, parsedSupportedVersion); } private void checkArgsForErrors(string GUID, string modName, string supportedVersion, out Version parsedSupportedVersion) { if (supportedVersion == null) { throw new ArgumentNullException("supportedVersion"); } if (!Version.TryParse(supportedVersion, out parsedSupportedVersion)) { throw new ArgumentException("The arg supportedVersion doesnt have a valid Version format."); } checkArgsForErrors(GUID, modName, parsedSupportedVersion); } private void checkArgsForErrors(string GUID, string modName, Version supportedVersion) { if (GUID == null) { throw new ArgumentNullException("GUID"); } if (modName == null) { throw new ArgumentNullException("modName"); } if (supportedVersion == null) { throw new ArgumentNullException("supportedVersion"); } } private void Init(string GUID, string modName, Version supportedVersion) { ModInfo = new ModInfoData(GUID, modName, supportedVersion); ModStatus = IsModLoaded(); } protected virtual ModLoadStatus IsModLoaded() { PluginInfo value; bool num = Chainloader.PluginInfos.TryGetValue(ModInfo.GUID, out value); ModPluginInfo = value; if (num) { ModInfo.LoadedVersion = ModPluginInfo.Metadata.Version; if (ModInfo.LoadedVersion != ModInfo.SupportedVersion) { return ModLoadStatus.DifferentVersion; } return ModLoadStatus.LoadedOk; } return ModLoadStatus.NotLoaded; } protected bool GetConfigValue(string configKey, out T value) { return ConfigMethods.GetExternalConfigValue(ModInfo.GUID, configKey, out value); } } public class ModInfoData { public string GUID { get; internal set; } public string Name { get; internal set; } public Version SupportedVersion { get; internal set; } public Version LoadedVersion { get; internal set; } internal ModInfoData(string GUID, string modName, Version SupportedVersion) { this.GUID = GUID; Name = modName; this.SupportedVersion = SupportedVersion; } public ModInfoData(ModInfoData modInfo) { GUID = modInfo.GUID; Name = modInfo.Name; SupportedVersion = modInfo.SupportedVersion; LoadedVersion = modInfo.LoadedVersion; } } } namespace Damntry.UtilsBepInEx.Logging { public sealed class BepInExTimeLogger : TimeLogger { private static ManualLogSource bepinexLogger; protected override void InitializeLogger(params object[] args) { if (args == null) { throw new ArgumentNullException("args"); } if (args.Length == 0 || !(args[0] is string)) { throw new ArgumentException("The argument is empty or its first index is not a string"); } bepinexLogger = Logger.CreateLogSource((string)args[0]); } protected override void LogMessage(string message, LogTier logLevel) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) bepinexLogger.Log(convertLogLevel(logLevel), (object)message); } private LogLevel convertLogLevel(LogTier logLevel) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0007: Unknown result type (might be due to invalid IL or missing references) if ((int)logLevel == -1) { return (LogLevel)63; } return (LogLevel)logLevel; } } } namespace Damntry.UtilsBepInEx.IL { public static class ILExtensionMethods { public static int LocalIndex(this CodeInstruction code) { if (code.opcode == OpCodes.Ldloc_0 || code.opcode == OpCodes.Stloc_0) { return 0; } if (code.opcode == OpCodes.Ldloc_1 || code.opcode == OpCodes.Stloc_1) { return 1; } if (code.opcode == OpCodes.Ldloc_2 || code.opcode == OpCodes.Stloc_2) { return 2; } if (code.opcode == OpCodes.Ldloc_3 || code.opcode == OpCodes.Stloc_3) { return 3; } if (code.opcode == OpCodes.Ldloc_S || code.opcode == OpCodes.Ldloc) { return GetLocalOperandIndex(code.operand); } if (code.opcode == OpCodes.Stloc_S || code.opcode == OpCodes.Stloc) { return GetLocalOperandIndex(code.operand); } if (code.opcode == OpCodes.Ldloca_S || code.opcode == OpCodes.Ldloca) { return GetLocalOperandIndex(code.operand); } throw new ArgumentException("Instruction is not a load or store", "code"); } private static int GetLocalOperandIndex(object operand) { if (operand.GetType() == typeof(LocalBuilder)) { return ((LocalBuilder)operand).LocalIndex; } return Convert.ToInt32(operand); } public static int ArgumentIndex(this CodeInstruction code) { if (code.opcode == OpCodes.Ldarg_0) { return 0; } if (code.opcode == OpCodes.Ldarg_1) { return 1; } if (code.opcode == OpCodes.Ldarg_2) { return 2; } if (code.opcode == OpCodes.Ldarg_3) { return 3; } if (code.opcode == OpCodes.Ldarg_S || code.opcode == OpCodes.Ldarg) { return Convert.ToInt32(code.operand); } if (code.opcode == OpCodes.Starg_S || code.opcode == OpCodes.Starg) { return Convert.ToInt32(code.operand); } if (code.opcode == OpCodes.Ldarga_S || code.opcode == OpCodes.Ldarga) { return Convert.ToInt32(code.operand); } throw new ArgumentException("Instruction is not a load or store", "code"); } public static string GetFormattedIL(this IEnumerable instrList) { return instrList.Aggregate("", (string combinedText, CodeInstruction instr) => combinedText + "\n" + GetFormattedILSingleLine(instr)); } private static string GetFormattedILSingleLine(CodeInstruction instruction) { string text = FormatArgument(instruction.operand); string text2 = ((text.Length > 0) ? " " : ""); string text3 = instruction.opcode.ToString(); if (instruction.opcode.FlowControl == FlowControl.Branch || instruction.opcode.FlowControl == FlowControl.Cond_Branch) { text3 += " =>"; } text3 = text3.PadRight(10); return $"{CodePos(instruction)}{text3}{text2}{text}"; } private static string CodePos(CodeInstruction instruction) { return ""; } private static string FormatArgument(object argument, string extra = null) { if (argument == null) { return ""; } Type type = argument.GetType(); if (argument is MethodBase methodBase) { return GeneralExtensions.FullDescription(methodBase) + ((extra != null) ? (" " + extra) : ""); } if (argument is FieldInfo fieldInfo) { return GeneralExtensions.FullDescription(fieldInfo.FieldType) + " " + GeneralExtensions.FullDescription(fieldInfo.DeclaringType) + "::" + fieldInfo.Name; } if (type == typeof(Label)) { return $"Label{((Label)argument).GetHashCode()}"; } if (type == typeof(Label[])) { return "Labels" + string.Join(",", ((Label[])argument).Select((Label l) => l.GetHashCode().ToString()).ToArray()); } if (type == typeof(LocalBuilder)) { return $"{((LocalBuilder)argument).LocalIndex} ({((LocalBuilder)argument).LocalType})"; } if (type == typeof(string)) { return GeneralExtensions.ToLiteral(argument.ToString(), "\""); } return argument.ToString().Trim(); } } public static class CodeInstructionNew { public static CodeInstruction LoadLocal(int index, bool useAddress = false) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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 if (useAddress) { if (index >= 256) { return new CodeInstruction(OpCodes.Ldloca, (object)index); } return new CodeInstruction(OpCodes.Ldloca_S, (object)Convert.ToByte(index)); } if (index != 0) { if (index != 1) { if (index != 2) { if (index != 3) { if (index >= 256) { return new CodeInstruction(OpCodes.Ldloc, (object)index); } return new CodeInstruction(OpCodes.Ldloc_S, (object)Convert.ToByte(index)); } return new CodeInstruction(OpCodes.Ldloc_3, (object)null); } return new CodeInstruction(OpCodes.Ldloc_2, (object)null); } return new CodeInstruction(OpCodes.Ldloc_1, (object)null); } return new CodeInstruction(OpCodes.Ldloc_0, (object)null); } public static CodeInstruction StoreLocal(int index) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown if (index != 0) { if (index != 1) { if (index != 2) { if (index != 3) { if (index >= 256) { return new CodeInstruction(OpCodes.Stloc, (object)index); } return new CodeInstruction(OpCodes.Stloc_S, (object)Convert.ToByte(index)); } return new CodeInstruction(OpCodes.Stloc_3, (object)null); } return new CodeInstruction(OpCodes.Stloc_2, (object)null); } return new CodeInstruction(OpCodes.Stloc_1, (object)null); } return new CodeInstruction(OpCodes.Stloc_0, (object)null); } public static CodeInstruction LoadArgument(int index, bool useAddress = false) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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 if (useAddress) { if (index >= 256) { return new CodeInstruction(OpCodes.Ldarga, (object)index); } return new CodeInstruction(OpCodes.Ldarga_S, (object)Convert.ToByte(index)); } if (index != 0) { if (index != 1) { if (index != 2) { if (index != 3) { if (index >= 256) { return new CodeInstruction(OpCodes.Ldarg, (object)index); } return new CodeInstruction(OpCodes.Ldarg_S, (object)Convert.ToByte(index)); } return new CodeInstruction(OpCodes.Ldarg_3, (object)null); } return new CodeInstruction(OpCodes.Ldarg_2, (object)null); } return new CodeInstruction(OpCodes.Ldarg_1, (object)null); } return new CodeInstruction(OpCodes.Ldarg_0, (object)null); } public static CodeInstruction StoreArgument(int index) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (index >= 256) { return new CodeInstruction(OpCodes.Starg, (object)index); } return new CodeInstruction(OpCodes.Starg_S, (object)Convert.ToByte(index)); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching { public class HarmonyInstancePatcher { private enum PatchRecursiveAction { StopAll, SkipAndContinueNested, PatchAndContinueNested } private readonly Type harmonyInstanceType; private readonly Lazy harmonyPatch; public HarmonyInstancePatcher(Type harmonyInstanceType) { this.harmonyInstanceType = harmonyInstanceType; harmonyPatch = new Lazy((Func)(() => new Harmony(GetHarmonyInstanceId()))); } internal string GetHarmonyInstanceId() { return harmonyInstanceType.FullName; } public List PatchInstance() { return StartRecursivePatching(harmonyPatch.Value); } public void UnpatchInstance() { harmonyPatch.Value.UnpatchSelf(); } public int GetPatchedCount() { return harmonyPatch.Value.GetPatchedMethods().Count(); } private List StartRecursivePatching(Harmony harmonyPatch) { List list = new List(); if (PatchClass(harmonyInstanceType, harmonyPatch, list)) { PatchNestedClassesRecursive(new List(), harmonyInstanceType, harmonyPatch, list); } return list; } private void PatchNestedClassesRecursive(List classList, Type classType, Harmony harmony, List listPatchedMethods) { Type[] nestedTypes = classType.GetNestedTypes(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (Type type in nestedTypes) { if (type.IsClass && PatchClass(type, harmony, listPatchedMethods)) { PatchNestedClassesRecursive(classList, type, harmony, listPatchedMethods); } } } private bool PatchClass(Type classType, Harmony harmonyPatch, List listPatchedMethods) { switch (CheckAttributesForAllowedAction(classType)) { case PatchRecursiveAction.StopAll: return false; case PatchRecursiveAction.PatchAndContinueNested: { PatchClassProcessor val = harmonyPatch.CreateClassProcessor(classType, true); listPatchedMethods.AddRange(val.Patch()); break; } } return true; } private PatchRecursiveAction CheckAttributesForAllowedAction(Type classType) { if (ReflectionExtension.HasCustomAttribute((MemberInfo)classType)) { return PatchRecursiveAction.StopAll; } if (ReflectionExtension.HasCustomAttribute((MemberInfo)classType)) { return PatchRecursiveAction.SkipAndContinueNested; } return PatchRecursiveAction.PatchAndContinueNested; } public List PatchClassByType(Type classType) { ThrowIfNotOwnInstanceNestedClass(classType); return harmonyPatch.Value.CreateClassProcessor(classType).Patch(); } public void UnpatchMethod(Type originalClassType, string originalMethodName) { if (originalMethodName == null) { throw new ArgumentNullException("originalMethodName"); } MethodInfo methodInfo = AccessTools.Method(originalClassType, originalMethodName, (Type[])null, (Type[])null); if (methodInfo == null) { throw new InvalidOperationException("The method \"" + originalMethodName + "\" couldnt be found in the type " + originalClassType.FullName + "."); } harmonyPatch.Value.Unpatch((MethodBase)methodInfo, (HarmonyPatchType)0, harmonyPatch.Value.Id); } private void ThrowIfNotOwnInstanceNestedClass(Type classType) { if (classType == null) { throw new ArgumentNullException("classType"); } if (classType == harmonyInstanceType) { throw new InvalidOperationException("Use the method PatchInstance/UnpatchInstance() instead."); } if (GetTopClassOfNested(classType) != harmonyInstanceType) { throw new InvalidOperationException("Class must be a nested class of " + harmonyInstanceType.FullName + ". If you want to do this action on another Instance, use that instance methods instead."); } } private Type GetTopClassOfNested(Type classType) { while (classType.IsNested) { classType = classType.DeclaringType; } return classType; } } public class HarmonyMonoMethods { public static MethodDefinition GetMethodDefinition(MethodInfo methodInfo) { MethodDefinition val = MethodBaseToMethodDefinition(methodInfo); if (val == null) { AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(AssemblyUtils.GetAssemblyDllFilePath(methodInfo.DeclaringType)); try { val = Extensions.FindMethod(val2.MainModule.GetType(methodInfo.DeclaringType.FullName), Extensions.GetID((MethodBase)methodInfo, (string)null, (string)null, true, false, false), false); } catch (Exception ex) { TimeLogger.Logger.LogDebug(TimeLogger.FormatException(ex, "Error while trying to convert MethodInfo to MethodDefinition. You can safely ignore this error if you are not the dev."), (LogCategories)4194304); } } return val; } public static MethodDefinition MethodBaseToMethodDefinition(MethodBase method) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown return (MethodDefinition)((MemberReference)(TypeDefinition)ModuleDefinition.ReadModule((Stream)new MemoryStream(File.ReadAllBytes(method.DeclaringType.Module.FullyQualifiedName))).LookupToken(method.DeclaringType.MetadataToken)).Module.LookupToken(method.MetadataToken); } public static IEnumerable<(MethodInfo methodInfo, HarmonyMethod harmonyMethod)> GetAllPatchMethodTargets(Type assemblyType, bool skipNonExecutablePatches) { HarmonyMethod harmonyMethod; return (from type in Assembly.GetAssembly(assemblyType).GetTypes() where !skipNonExecutablePatches || IsPatchExecutable(type) select type).SelectMany((Type type) => from mInfo in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where IsHarmonyAttribute(mInfo) select (GetTargetMethodFromHarmonyPatchMethod(type, mInfo, out harmonyMethod), harmonyMethod)); } private static bool IsPatchExecutable(Type methodClassType) { MethodInfo methodInfo = (from m in methodClassType.GetMethods(AccessTools.all) where m.ReturnType == typeof(bool) select m).FirstOrDefault((MethodInfo m) => m.Name == "Prepare" || m.GetCustomAttributes(inherit: true).Any((object attr) => attr.GetType().FullName == typeof(HarmonyPrepare).FullName)); if (methodInfo != null) { object[] array = AccessTools.ActualParameters((MethodBase)methodInfo, Array.Empty()); return ReflectionHelper.CallMethod((object)null, methodInfo, array); } return true; } public static bool IsHarmonyAttribute(MethodInfo methodInfo) { IEnumerable enumerable = null; try { enumerable = methodInfo.GetCustomAttributes(); } catch (TypeNotFoundInAssemblyException) { return false; } return enumerable.Any((Attribute attr) => attr is HarmonyAttribute); } public static MethodInfo GetTargetMethodFromHarmonyPatchMethod(Type methodClassType, MethodInfo patchMethodInfo, out HarmonyMethod harmonyMethod) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) IEnumerable source = from attr in patchMethodInfo.GetCustomAttributes(inherit: true) where attr is HarmonyAttribute select ((HarmonyAttribute)attr).info; harmonyMethod = HarmonyMethod.Merge(source.ToList()); if (harmonyMethod.method == null && harmonyMethod.declaringType == null) { HarmonyMethod val = HarmonyMethod.Merge(HarmonyMethodExtensions.GetFromType(methodClassType)); harmonyMethod = HarmonyMethodExtensions.Merge(val, harmonyMethod); } HarmonyMethod val2 = harmonyMethod; MethodType valueOrDefault = val2.methodType.GetValueOrDefault(); if (!val2.methodType.HasValue) { valueOrDefault = (MethodType)0; val2.methodType = valueOrDefault; } MethodInfo methodInfo = AccessTools.Method("HarmonyLib.PatchTools:GetOriginalMethod", new Type[1] { typeof(HarmonyMethod) }, (Type[])null); if (methodInfo != null) { return (MethodInfo)methodInfo.Invoke(null, new object[1] { harmonyMethod }); } TimeLogger.Logger.LogWarning("Reflection access to \"HarmonyLib.PatchTools:GetOriginalMethod\" returned null. Using backup method.", (LogCategories)4194304); return AccessTools.Method(harmonyMethod.declaringType, harmonyMethod.methodName, harmonyMethod.argumentTypes, (Type[])null); } } public class MethodSignatureChecker { internal class MethodSignature { public string Arguments { get; set; } public string ReturnType { get; set; } public int IL_BodyHashcode { get; set; } public void Set_IL_BodyHashcode(Collection instructions) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (instructions == null) { throw new ArgumentNullException("instructions"); } int num = 17; Enumerator enumerator = instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; num = num * 71 + ((object)current).ToString().GetHashCode(); } } finally { ((IDisposable)enumerator).Dispose(); } IL_BodyHashcode = num; } internal bool Equals(MethodSignature other, out string errorDetail) { errorDetail = null; if (other == null) { errorDetail = "\tWrong method call or no longer exists."; } else if (Arguments != other.Arguments) { errorDetail = "\tArguments are different"; } else if (ReturnType != other.ReturnType) { errorDetail = "\tReturn types are different"; } else if (IL_BodyHashcode != other.IL_BodyHashcode) { errorDetail = "\tIL method body hashcode is different"; } if (errorDetail != null) { errorDetail = errorDetail.PadRight(38); } return errorDetail == null; } } private readonly struct MethodInfoData { private readonly struct FullMethodName { private readonly string method; private readonly string declaringType; private readonly string[] parameters; public FullMethodName(string declaredTypeName, string methodName, string[] typeParameterNames = null) { method = methodName; declaringType = ReflectionHelper.ConvertFullTypeToNormal(declaredTypeName); parameters = ReflectionHelper.ConvertFullTypesToNormal(typeParameterNames); } public FullMethodName(string declaredTypeName, string methodName, Type[] typeParameter = null) { method = methodName; declaringType = ReflectionHelper.ConvertFullTypeToNormal(declaredTypeName); string[] array = null; if (typeParameter != null && typeParameter.Length != 0) { array = (from t in typeParameter where t != null select t.Name).ToArray(); } parameters = array; } public string GetText() { string text = ""; if (parameters != null && parameters.Length != 0) { text = string.Join(", ", parameters); } return declaringType + "." + method + "(" + text + ")"; } } private readonly bool isMethodInfo; private readonly MethodInfo methodInfo; private readonly Type declaringType; private readonly string methodName; private readonly Type[] parameters; private readonly Type[] generics; private readonly string fullTypeName; private readonly string[] fullTypeParameters; private readonly FullMethodName fullMethodName; public MethodInfoData(MethodInfo methodInfo, string declaredTypeName, string methodName, string[] typeParameterNames = null) { declaringType = null; this.methodName = null; parameters = null; generics = null; fullTypeName = null; fullTypeParameters = null; if (string.IsNullOrEmpty(declaredTypeName)) { throw new ArgumentNullException("declaredTypeName cannot be empty."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentNullException("methodName cannot be empty."); } this.methodInfo = methodInfo; fullMethodName = new FullMethodName(declaredTypeName, methodName, typeParameterNames); isMethodInfo = true; } public MethodInfoData(MethodInfo methodInfo, Type declaringType, string methodName, Type[] parameters = null, Type[] generics = null) { this.declaringType = null; this.methodName = null; this.parameters = null; this.generics = null; fullTypeName = null; fullTypeParameters = null; if (declaringType == null) { throw new ArgumentNullException("declaringType cannot be null."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentException("methodName cannot be null or empty."); } this.methodInfo = methodInfo; fullMethodName = new FullMethodName(declaringType.Name, methodName, parameters); isMethodInfo = true; } public MethodInfoData(Type declaringType, string methodName, Type[] parameters = null, Type[] generics = null) { isMethodInfo = false; methodInfo = null; fullTypeName = null; fullTypeParameters = null; if (declaringType == null) { throw new ArgumentNullException("declaringType cannot be null."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentException("methodName cannot be null or empty."); } this.declaringType = declaringType; this.methodName = methodName; this.parameters = parameters; this.generics = generics; fullMethodName = new FullMethodName(declaringType.Name, methodName, parameters); } public MethodInfoData(Type declaringType, string methodName, string[] fullTypeParameters = null, Type[] generics = null) { isMethodInfo = false; methodInfo = null; parameters = null; fullTypeName = null; if (declaringType == null) { throw new ArgumentNullException("declaringType cannot be null."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentException("methodName cannot be null or empty."); } this.declaringType = declaringType; this.methodName = methodName; this.fullTypeParameters = fullTypeParameters; this.generics = generics; fullMethodName = new FullMethodName(declaringType.Name, methodName, fullTypeParameters); } public MethodInfoData(string fullTypeName, string methodName, string[] fullTypeParameters = null, Type[] generics = null) { isMethodInfo = false; methodInfo = null; declaringType = null; parameters = null; if (string.IsNullOrEmpty(fullTypeName)) { throw new ArgumentNullException("fullTypeName cannot be null or empty."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentException("methodName cannot be null or empty."); } this.fullTypeName = fullTypeName; this.methodName = methodName; this.fullTypeParameters = fullTypeParameters; this.generics = generics; fullMethodName = new FullMethodName(fullTypeName, methodName, fullTypeParameters); } public MethodInfo GetMethodInfo() { if (isMethodInfo) { return methodInfo; } Type obj = ((declaringType != null) ? declaringType : AssemblyUtils.GetTypeFromLoadedAssemblies(fullTypeName, true)); Type[] array = ((parameters != null) ? parameters : AssemblyUtils.GetTypesFromLoadedAssemblies(true, fullTypeParameters)); return AccessTools.Method(obj, methodName, array, generics); } public string GetFullMethodName() { return fullMethodName.GetText(); } } private List listMethodInfoData; private Dictionary previousMethodSignatures; private readonly string pathFileMethodSignatures; private readonly Type pluginType; public CheckResult LastCheckResult { get; private set; } public MethodSignatureChecker(Type pluginType) { this.pluginType = pluginType; LastCheckResult = new CheckResult(); string combinedPathFromAssemblyFolder = AssemblyUtils.GetCombinedPathFromAssemblyFolder(pluginType, "MethodSignatureChecker"); if (!Directory.Exists(combinedPathFromAssemblyFolder)) { Directory.CreateDirectory(combinedPathFromAssemblyFolder); } char directorySeparatorChar = Path.DirectorySeparatorChar; pathFileMethodSignatures = combinedPathFromAssemblyFolder + directorySeparatorChar + "methodsignatures.json"; listMethodInfoData = new List(); } public static MethodSignatureChecker StartSignatureCheck(Type pluginType) { MethodSignatureChecker methodSignatureChecker = new MethodSignatureChecker(pluginType); methodSignatureChecker.PopulateMethodSignaturesFromHarmonyPatches(); methodSignatureChecker.StartSignatureCheck(); return methodSignatureChecker; } public void PopulateMethodSignaturesFromHarmonyPatches() { foreach (var (methodInfo, val) in HarmonyMonoMethods.GetAllPatchMethodTargets(pluginType, skipNonExecutablePatches: true)) { AddMethod(methodInfo, val.declaringType, val.methodName, val.argumentTypes); } } public void AddMethod(MethodInfo methodInfo, string declaredTypeName, string methodName, string[] typeParameterNames = null) { listMethodInfoData.Add(new MethodInfoData(methodInfo, declaredTypeName, methodName, typeParameterNames)); } public void AddMethod(MethodInfo methodInfo, Type declaringType, string methodName, Type[] parameters = null) { listMethodInfoData.Add(new MethodInfoData(methodInfo, declaringType, methodName, parameters)); } public void AddMethod(string fullTypeName, string methodName, string[] fullTypeParameters = null, Type[] generics = null) { listMethodInfoData.Add(new MethodInfoData(fullTypeName, methodName, fullTypeParameters, generics)); } public void AddMethod(Type declaringType, string methodName, string[] fullTypeParameters = null, Type[] generics = null) { listMethodInfoData.Add(new MethodInfoData(declaringType, methodName, fullTypeParameters, generics)); } public void AddMethod(Type declaringType, string methodName) { listMethodInfoData.Add(new MethodInfoData(declaringType, methodName, (Type[])null, (Type[])null)); } public void AddMethod(Type declaringType, string methodName, Type[] parameters = null, Type[] generics = null) { listMethodInfoData.Add(new MethodInfoData(declaringType, methodName, parameters, generics)); } private MethodSignature CreateMethodSignature(MethodInfo methodInfo, MethodDefinition methodDef) { MethodSignature methodSignature = new MethodSignature(); object[] parameters = methodInfo.GetParameters(); methodSignature.Arguments = string.Join("", parameters); methodSignature.ReturnType = methodInfo.ReturnType.FullName; methodSignature.Set_IL_BodyHashcode(methodDef.Body.Instructions); return methodSignature; } public CheckResult StartSignatureCheck() { CheckResult checkResult = new CheckResult(CheckResult.SignatureCheckResult.Started, ""); Dictionary dictionary = new Dictionary(); foreach (MethodInfoData listMethodInfoDatum in listMethodInfoData) { if (GetMethodSignature(listMethodInfoDatum, out var methodSigEntry) && !dictionary.ContainsKey(methodSigEntry.Key)) { dictionary.Add(methodSigEntry.Key, methodSigEntry.Value); } } if (dictionary == null || !dictionary.Any()) { return checkResult.SetValues(CheckResult.SignatureCheckResult.NoSignaturesAdded, "No method signature has been added. Signature comparison will be skipped."); } if (TryLoadSignaturesFromFile(checkResult)) { CompareOldSignaturesAgainstNew(checkResult, dictionary); } if (checkResult.ResultType != CheckResult.SignatureCheckResult.SignaturesDifferent) { SaveSignaturesToFile(dictionary); } if (checkResult.ResultType == CheckResult.SignatureCheckResult.Started) { throw new InvalidOperationException("Something went wrong. Method check finished with result \"Started\"."); } return LastCheckResult = checkResult; } private bool GetMethodSignature(MethodInfoData methodInfoData, out KeyValuePair methodSigEntry) { string text = ""; MethodSignature value = null; MethodInfo methodInfo = methodInfoData.GetMethodInfo(); if (methodInfo != null) { MethodDefinition methodDefinition = HarmonyMonoMethods.GetMethodDefinition(methodInfo); if (methodDefinition == null) { methodSigEntry = default(KeyValuePair); return false; } value = CreateMethodSignature(methodInfo, methodDefinition); } text = methodInfoData.GetFullMethodName(); methodSigEntry = new KeyValuePair(text, value); return true; } private bool TryLoadSignaturesFromFile(CheckResult checkResult) { if (!File.Exists(pathFileMethodSignatures)) { checkResult.SetValues(CheckResult.SignatureCheckResult.NoPreviousSignatures, "No method signature file. Skipped check."); return false; } try { string text = File.ReadAllText(pathFileMethodSignatures, Encoding.Unicode); previousMethodSignatures = JsonConvert.DeserializeObject>(text); } catch (Exception ex) { checkResult.SetValues(CheckResult.SignatureCheckResult.FileError, TimeLogger.FormatException(ex, "Error while reading and deserializing from file \"" + pathFileMethodSignatures + "\". It might be corrupted. Trying to delete file and skipping loading signatures.")); try { File.Delete(pathFileMethodSignatures); } catch { } return false; } return true; } private void SaveSignaturesToFile(Dictionary currentMethodSignatures) { string contents = JsonConvert.SerializeObject((object)currentMethodSignatures, (Formatting)1); File.WriteAllText(pathFileMethodSignatures, contents, Encoding.Unicode); } private void CompareOldSignaturesAgainstNew(CheckResult checkResult, Dictionary currentMethodSignatures) { Dictionary dictionary = previousMethodSignatures; if (dictionary == null || !dictionary.Any() || currentMethodSignatures == null || !currentMethodSignatures.Any()) { return; } foreach (KeyValuePair currentMethodSignature in currentMethodSignatures) { if (previousMethodSignatures.TryGetValue(currentMethodSignature.Key, out var value)) { MethodSignature value2 = currentMethodSignature.Value; if (value != null && !value.Equals(value2, out var errorDetail)) { checkResult.ResultType = CheckResult.SignatureCheckResult.SignaturesDifferent; checkResult.AddErrorMessage(errorDetail + " | " + currentMethodSignature.Key); } } } if (checkResult.ResultType != CheckResult.SignatureCheckResult.SignaturesDifferent) { checkResult.SetValues(CheckResult.SignatureCheckResult.SignaturesOk, "Method signature check ok."); } } } public class CheckResult { public enum SignatureCheckResult { Unchecked, Started, FileError, NoSignaturesAdded, NoPreviousSignatures, SignaturesDifferent, SignaturesOk } public static CheckResult UnknownError = new CheckResult(SignatureCheckResult.Unchecked, "Unknown Error due to exception"); public SignatureCheckResult ResultType { get; internal set; } public string ResultMessage { get; private set; } = ""; public string DetailMessage { get; private set; } = ""; internal CheckResult() { ResultType = SignatureCheckResult.Unchecked; } internal CheckResult(SignatureCheckResult result, string resultMessage) { ResultMessage = resultMessage; ResultType = result; } internal CheckResult SetValues(SignatureCheckResult result, string resultMessage) { ResultMessage = resultMessage; ResultType = result; return this; } internal void AddErrorMessage(string resultMessage) { if (string.IsNullOrEmpty(ResultMessage)) { ResultMessage = "Method signatures have changed since last successful check"; } DetailMessage = DetailMessage + "\n" + resultMessage; } public void LogResultMessage(LogTier logLevel, bool onlyWhenNotOk, bool showInGame) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (ShouldLogMessage(onlyWhenNotOk)) { string text = ResultMessage; if (ResultType == SignatureCheckResult.SignaturesDifferent) { text = text + ":" + DetailMessage; } TimeLogger.Logger.Log(logLevel, text, (LogCategories)4194304); TimeLogger.Logger.SendMessageNotificationError(ResultMessage, false); } } private bool ShouldLogMessage(bool onlyWhenNotOk) { if (string.IsNullOrEmpty(ResultMessage)) { if (ResultType == SignatureCheckResult.Unchecked) { return false; } ResultMessage = "Result message was empty for a result in which this is not allowed."; return true; } if (onlyWhenNotOk && (ResultType == SignatureCheckResult.SignaturesOk || ResultType == SignatureCheckResult.NoPreviousSignatures)) { return false; } return true; } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.Helpers { public class ArgumentHelper { public T Value { get; set; } public CodeInstruction LoadFieldArgHelper_IL { get; private set; } public CodeInstruction GetterValue_IL { get; private set; } public CodeInstruction SetterValue_IL { get; private set; } public ArgumentHelper(Type declaringClassType, string instanceName, T argumentValue) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown Value = argumentValue; FieldInfo fieldInfo = AccessTools.Field(declaringClassType, instanceName); if (fieldInfo == null) { throw new InvalidOperationException($"No field was found in the type {declaringClassType} with name {instanceName}."); } LoadFieldArgHelper_IL = new CodeInstruction(OpCodes.Ldsfld, (object)fieldInfo); if (!fieldInfo.IsStatic) { LoadFieldArgHelper_IL.opcode = OpCodes.Ldfld; } PropertyInfo propertyInfo = AccessTools.Property(typeof(ArgumentHelper), "Value"); GetterValue_IL = new CodeInstruction(OpCodes.Callvirt, (object)propertyInfo.GetGetMethod()); SetterValue_IL = new CodeInstruction(OpCodes.Callvirt, (object)propertyInfo.GetSetMethod()); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.Exceptions { public class TranspilerDefaultMsgException : Exception { private const string defaultErrorText = "Transpiler couldnt perform its changes due to unexpected source code changes."; public TranspilerDefaultMsgException() : base(composeErrorText()) { } public TranspilerDefaultMsgException(string errorDetail) : base(composeErrorText(errorDetail)) { } public TranspilerDefaultMsgException(string errorDetail, Exception inner) : base(composeErrorText(errorDetail), inner) { } private static string composeErrorText(string errorDetail = "") { return "Transpiler couldnt perform its changes due to unexpected source code changes.\n" + errorDetail; } } public class TypeNotFoundInAssemblyException : Exception { public TypeNotFoundInAssemblyException() { } public TypeNotFoundInAssemblyException(string errorDetail) : base(errorDetail) { } public TypeNotFoundInAssemblyException(string errorDetail, Exception inner) : base(errorDetail, inner) { } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching { public static class Container where T : AutoPatchedInstanceBase { public static T Instance => AutoPatchContainer.GetInstance(); } internal static class AutoPatchContainer { private static Dictionary registeredInstances = new Dictionary(); internal static T GetInstance() where T : AutoPatchedInstanceBase { ThrowIfTypeInvalidOrNotRegistered(); return (T)registeredInstances[typeof(T)]; } internal static AutoPatchedInstanceBase GetAbstractInstance(Type patchType) { ThrowIfTypeInvalidOrNotRegistered(patchType); return registeredInstances[patchType]; } internal static IReadOnlyDictionary GetRegisteredAutoPatches() { return registeredInstances; } internal static void RegisterPatchClass(Type autoPatchType) { ThrowIfTypeInvalidOrAlreadyRegistered(autoPatchType); object obj = Activator.CreateInstance(autoPatchType); registeredInstances.Add(autoPatchType, (AutoPatchedInstanceBase)obj); } internal static void UnregisterPatchClass(Type autoPatchClass) { ThrowIfTypeInvalidOrNotRegistered(autoPatchClass); registeredInstances.Remove(autoPatchClass); } private static void ThrowIfTypeInvalidOrAlreadyRegistered() { ThrowIfTypeInvalidOrAlreadyRegistered(typeof(T)); } private static void ThrowIfTypeInvalidOrAlreadyRegistered(Type patchClassType) { if (patchClassType.IsAbstract || !patchClassType.IsSubclassOf(typeof(AutoPatchedInstanceBase))) { throw new InvalidOperationException("The type " + patchClassType.FullName + " must be a non abstract subclass of AutoPatchedInstanceBase."); } if (registeredInstances.ContainsKey(patchClassType)) { throw new InvalidOperationException("The type " + patchClassType.FullName + " has already been registered."); } } private static void ThrowIfTypeInvalidOrNotRegistered() { ThrowIfTypeInvalidOrNotRegistered(typeof(T)); } private static void ThrowIfTypeInvalidOrNotRegistered(Type patchClassType) { if (patchClassType.IsAbstract || !patchClassType.IsSubclassOf(typeof(AutoPatchedInstanceBase))) { throw new InvalidOperationException("The type " + patchClassType.FullName + " must be a non abstract subclass of AutoPatchedInstanceBase."); } if (!registeredInstances.ContainsKey(patchClassType)) { throw new InvalidOperationException("The type " + patchClassType.FullName + " is not registered."); } } } public static class AutoPatcher { public enum AutoPatchResult { none, error, disabled, success } public static bool RegisterAllAutoPatchContainers() { List list = GetAutoPatchClasses(Assembly.GetCallingAssembly()).ToList(); if (list.Count == 0) { return false; } foreach (Type item in list) { AutoPatchContainer.RegisterPatchClass(item); } return true; } public static bool StartAutoPatcher() { int num = 0; int num2 = 0; int num3 = 0; foreach (KeyValuePair registeredAutoPatch in AutoPatchContainer.GetRegisteredAutoPatches()) { AutoPatchResult autoPatchResult = Patch(registeredAutoPatch.Key, registeredAutoPatch.Value); if (autoPatchResult == AutoPatchResult.disabled) { num2++; continue; } num3++; if (autoPatchResult == AutoPatchResult.error) { num++; } } if (num == 0) { TimeLogger.Logger.LogInfo($"All {num3} auto-patches applied successfully.", (LogCategories)8); return true; } TimeLogger.Logger.LogFatal($"Oh oh, {num} out of {num3} auto-patches failed. " + "Check above for errors.", (LogCategories)8); return false; } private static AutoPatchResult Patch(Type autoPatchType, AutoPatchedInstanceBase autoPatchInstance) { AutoPatchResult autoPatchResult = AutoPatchResult.none; try { if (autoPatchInstance == null) { throw new InvalidOperationException("Auto patch received a null instance from the registered type " + autoPatchType.FullName + "."); } if (autoPatchInstance.IsAutoPatchEnabled) { autoPatchInstance.PatchInstance(); autoPatchResult = AutoPatchResult.success; } else { autoPatchResult = AutoPatchResult.disabled; } } catch (Exception ex) { autoPatchResult = AutoPatchResult.error; TimeLogger.Logger.LogExceptionWithMessage("Error auto patching class " + autoPatchType.FullName + ".", ex, (LogCategories)8); if (autoPatchInstance != null) { TimeLogger.Logger.LogError(autoPatchInstance.ErrorMessageOnAutoPatchFail, (LogCategories)2097152); if (autoPatchInstance.IsRollbackOnAutoPatchFail && autoPatchInstance.GetPatchedCount() > 0) { autoPatchInstance.UnpatchInstance(); } } } finally { try { autoPatchInstance?.RaiseEventOnAutoPatchFinish(autoPatchResult); } catch (Exception ex2) { TimeLogger.Logger.LogExceptionWithMessage("Error calling RaiseEventOnAutoPatchFinish:", ex2, (LogCategories)2097152); } } return autoPatchResult; } public static bool IsPatchActiveFromResult(AutoPatchResult autoPatchResult) { switch (autoPatchResult) { case AutoPatchResult.success: return true; case AutoPatchResult.error: case AutoPatchResult.disabled: return false; default: throw new NotImplementedException($"The switch case {autoPatchResult} is not implemented or should not have happened."); } } private static IEnumerable GetAutoPatchClasses(Assembly assembly) { return from type in assembly.GetTypes() where type.IsClass && !type.IsAbstract && type.IsSubclassOf(typeof(AutoPatchedInstanceBase)) select type; } public static string[] GetHarmonyInstanceIdsForAttribute(Type[] autoPatchTypes) { return autoPatchTypes.Select((Type autoPatchType) => AutoPatchContainer.GetAbstractInstance(autoPatchType).harmonyPatchInstance.Value.GetHarmonyInstanceId()).ToArray(); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Interfaces { public interface IAutoPatchSupport : ISelfPatch { bool IsAutoPatchEnabled { get; } bool IsRollbackOnAutoPatchFail { get; } string ErrorMessageOnAutoPatchFail { get; } void RaiseEventOnAutoPatchFinish(AutoPatcher.AutoPatchResult patchResult); } public interface IConfigPatchDependence { void SetSettingPatchDependence(ConfigEntry configEntry); } public interface ISelfPatch { bool IsPatchActive { get; } event Action OnPatchFinished; List PatchInstance(); void UnpatchInstance(); } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.BaseClasses { public abstract class AutoPatchedInstanceBase : IAutoPatchSupport, ISelfPatch, IConfigPatchDependence { internal readonly Lazy harmonyPatchInstance; public abstract bool IsAutoPatchEnabled { get; } public abstract bool IsRollbackOnAutoPatchFail { get; } public abstract bool IsPatchActive { get; protected set; } public abstract string ErrorMessageOnAutoPatchFail { get; protected set; } public abstract event Action OnPatchFinished; internal AutoPatchedInstanceBase() { harmonyPatchInstance = new Lazy(() => new HarmonyInstancePatcher(GetType())); } public List PatchInstance() { return harmonyPatchInstance.Value.PatchInstance(); } public void UnpatchInstance() { harmonyPatchInstance.Value.UnpatchInstance(); } public int GetPatchedCount() { return harmonyPatchInstance.Value.GetPatchedCount(); } public abstract void RaiseEventOnAutoPatchFinish(AutoPatcher.AutoPatchResult autoPatchResult); public void SetSettingPatchDependence(ConfigEntry configEntry) { OnPatchFinished += delegate(bool IsPatchActive) { configEntry.SetConfigAttribute(ConfigurationManagerAttributes.ConfigAttributes.Browsable, IsPatchActive); }; } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.BaseClasses.Inheritable { public abstract class FullyAutoPatchedInstance : AutoPatchedInstanceBase { public override bool IsRollbackOnAutoPatchFail => true; public override bool IsPatchActive { get; protected set; } public override event Action OnPatchFinished; public void AutoPatchResultEvent(AutoPatcher.AutoPatchResult autoPatchResult) { IsPatchActive = AutoPatcher.IsPatchActiveFromResult(autoPatchResult); } public virtual void OnPatchFinishedVirtual(bool IsActive) { } public override void RaiseEventOnAutoPatchFinish(AutoPatcher.AutoPatchResult autoPatchResult) { AutoPatchResultEvent(autoPatchResult); if (OnPatchFinished != null) { OnPatchFinished(IsPatchActive); } OnPatchFinishedVirtual(IsPatchActive); } } public abstract class HybridPatchedInstance : AutoPatchedInstanceBase { public override void RaiseEventOnAutoPatchFinish(AutoPatcher.AutoPatchResult autoPatchResult) { } public List PatchClassByType(Type classType) { return harmonyPatchInstance.Value.PatchClassByType(classType); } public void UnpatchMethod(Type classType, string methodName) { harmonyPatchInstance.Value.UnpatchMethod(classType, methodName); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Attributes { [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class AutoPatchIgnoreClass : AutoPatchAttribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class AutoPatchIgnoreClassAndNested : AutoPatchAttribute { } public abstract class AutoPatchAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class HarmonyBeforeInstance : HarmonyPatch { private HarmonyBeforeInstance() { } public HarmonyBeforeInstance(params Type[] beforeInstances) { ((HarmonyAttribute)this).info.before = AutoPatcher.GetHarmonyInstanceIdsForAttribute(beforeInstances); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class HarmonyAfterInstance : HarmonyPatch { private HarmonyAfterInstance() { } public HarmonyAfterInstance(params Type[] afterInstances) { ((HarmonyAttribute)this).info.after = AutoPatcher.GetHarmonyInstanceIdsForAttribute(afterInstances); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.Attributes { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate, AllowMultiple = true)] public class HarmonyPatchStringTypes : HarmonyAttribute { public HarmonyPatchStringTypes(string fullTypeName, string methodName) { SetMethodInfo(fullTypeName, methodName, (Type[])null); } public HarmonyPatchStringTypes(string fullTypeName, string methodName, Type[] argumentTypes) { SetMethodInfo(fullTypeName, methodName, argumentTypes); } private void SetMethodInfo(string fullTypeName, string methodName, string[] argumentFullTypeNames) { Type[] typesFromLoadedAssemblies = AssemblyUtils.GetTypesFromLoadedAssemblies(true, argumentFullTypeNames); SetMethodInfo(fullTypeName, methodName, typesFromLoadedAssemblies); } private void SetMethodInfo(string fullTypeName, string methodName, Type[] argumentTypes) { Type typeFromLoadedAssemblies = AssemblyUtils.GetTypeFromLoadedAssemblies(fullTypeName, true); base.info.declaringType = typeFromLoadedAssemblies; base.info.methodName = methodName; base.info.argumentTypes = argumentTypes; } } } namespace Damntry.UtilsBepInEx.Debug { public interface ITimeScaleMethods { void ReadVanillaValues(); void SetAdjustedSpeed(float timeScaleDiff); } public static class TimeScaleDebug { private class TimeScaleDebugBehaviour : MonoBehaviour { private bool vanillaValuesRead; public void Update() { float timeScale = Time.timeScale; if (!vanillaValuesRead && timeScale == Settings.DefaultTimeScale) { TimeScaleMethods.ReadVanillaValues(); vanillaValuesRead = true; } if (((IEnumerable)Settings.KeyModifiers).All((Func)Input.GetKey)) { bool flag = false; bool isIncrease = false; if (Input.GetAxis("Mouse ScrollWheel") > 0f) { isIncrease = true; flag = true; } else if (Input.GetAxis("Mouse ScrollWheel") < 0f) { flag = true; } if (flag) { Time.timeScale = CalculateNewTimeScale(timeScale, isIncrease); float adjustedSpeed = Settings.DefaultTimeScale / Time.timeScale; TimeScaleMethods.SetAdjustedSpeed(adjustedSpeed); TimeLogger.Logger.Log((LogTier)8, "Time Scale : " + Math.Round(Time.timeScale, 3), (LogCategories)int.MinValue, Settings.PreprocessMsgFunc, Settings.ShowInGameNotification(), Settings.SkipNotificationQueue, Array.Empty<(Action, bool)>()); } } } } private class TimeScaleSettings { public int TotalScrollNotches { get; init; } public float DefaultTimeScale { get; init; } public float MinTimeScale { get; init; } public float MaxTimeScale { get; init; } public int DefaultStartStep { get; init; } public float[] FixedThresholds { get; init; } public PreprocessMessageFunc PreprocessMsgFunc { get; init; } public Func ShowInGameNotification { get; init; } public bool SkipNotificationQueue { get; init; } public KeyCode[] KeyModifiers { get; init; } } public static readonly KeyCode[] DefaultKeyModifiers = (KeyCode[])(object)new KeyCode[2] { (KeyCode)308, (KeyCode)306 }; private static TimeScaleSettings Settings; private static ITimeScaleMethods TimeScaleMethods; private static TimeScaleDebugBehaviour behaviourRef; private static AnimationCurve timeScaleCurve; private static int currentNotch; public static void Initialize(GameObject parent, KeyCode[] keyModifiers = null, float defaultTimeScale = 1f, float minTimeScale = 0.002f, float maxTimeScale = 50f, int totalScrollNotches = 100, float notchRatio = 0.3f, float[] thresholds = null, PreprocessMessageFunc preprocessMsgFunc = null, Func showInGameNotification = null, bool skipNotificationQueue = true) where T : ITimeScaleMethods, new() { if (Object.op_Implicit((Object)(object)behaviourRef)) { TimeLogger.Logger.LogWarning("There is still an active MonoBehaviour " + $"added previously ({behaviourRef}).", (LogCategories)int.MinValue); return; } if (!Object.op_Implicit((Object)(object)parent)) { TimeLogger.Logger.LogWarning("parent parameter is null. TimeScaleDebugfunctionality wont work.", (LogCategories)int.MinValue); return; } TimeScaleMethods = new T(); TimeScaleSettings timeScaleSettings = new TimeScaleSettings(); timeScaleSettings.TotalScrollNotches = totalScrollNotches; timeScaleSettings.DefaultTimeScale = defaultTimeScale; timeScaleSettings.MinTimeScale = minTimeScale; timeScaleSettings.MaxTimeScale = maxTimeScale; timeScaleSettings.DefaultStartStep = (int)((float)totalScrollNotches * Mathf.Clamp01(notchRatio)); timeScaleSettings.FixedThresholds = thresholds ?? new float[1] { defaultTimeScale }; timeScaleSettings.KeyModifiers = keyModifiers ?? DefaultKeyModifiers; timeScaleSettings.PreprocessMsgFunc = preprocessMsgFunc; timeScaleSettings.ShowInGameNotification = showInGameNotification; timeScaleSettings.SkipNotificationQueue = skipNotificationQueue; Settings = timeScaleSettings; behaviourRef = parent.AddComponent(); CreateTimeScaleCurve(); currentNotch = Settings.DefaultStartStep; } private static void CreateTimeScaleCurve() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) timeScaleCurve = new AnimationCurve(); timeScaleCurve.AddKey(new Keyframe(0f, Settings.MinTimeScale, 0f, 0f)); timeScaleCurve.AddKey(new Keyframe((float)Settings.DefaultStartStep, Settings.DefaultTimeScale, 0f, 0f)); timeScaleCurve.AddKey(new Keyframe((float)Settings.TotalScrollNotches, Settings.MaxTimeScale, 0f, 0f)); } private static float CalculateNewTimeScale(float currentTimeScale, bool isIncrease) { currentNotch += (isIncrease ? 1 : (-1)); currentNotch = NumberExtensionMethods.ClampReturn(currentNotch, 0, Settings.TotalScrollNotches); float num = timeScaleCurve.Evaluate((float)currentNotch); float[] fixedThresholds = Settings.FixedThresholds; foreach (float num2 in fixedThresholds) { if ((isIncrease && num2 > currentTimeScale && num2 < num) || (!isIncrease && num2 > num && num2 < currentTimeScale)) { num = num2; break; } } return num; } } } namespace Damntry.UtilsBepInEx.Configuration { public static class BepinexConfigExtensions { public static T GetMaxValue(this AcceptableValueBase acceptableVal) where T : IComparable, IEquatable { if (acceptableVal is AcceptableValueRange) { return ((AcceptableValueRange)(object)acceptableVal).GetMaxNumericValue(); } if (acceptableVal is AcceptableValueList) { return ((AcceptableValueList)(object)acceptableVal).GetMaxNumericValue(); } return default(T); } public static T GetMaxNumericValue(this AcceptableValueRange acceptableVal) where T : IComparable { return acceptableVal.MaxValue; } public static T GetMaxNumericValue(this AcceptableValueList acceptableVal) where T : IEquatable, IComparable { if (!NumberExtensionMethods.IsNumeric(typeof(T))) { throw new InvalidOperationException("Only numeric types are allowed, but received a " + typeof(T).FullName); } return acceptableVal.AcceptableValues.OrderBy((T x) => x).Last(); } } public class ConfigMethods { public static bool GetExternalConfigValue(string ModGUID, string configKey, out T value) { value = default(T); if (Chainloader.PluginInfos.TryGetValue(ModGUID, out var value2)) { ConfigEntryBase val = (from c in (IEnumerable>)value2.Instance.Config where c.Key.Key == configKey select c.Value).FirstOrDefault(); if (val != null) { value = (T)val.BoxedValue; return true; } } return false; } } } namespace Damntry.UtilsBepInEx.Configuration.ConfigurationManager { public enum MultiplayerModInstallSide { Unspecified, Any, HostSideOnly, HostAndOptionalClient, HostAndSoftClient, HostAndHardClient } public class ConfigManagerController { internal const string ConfigMngFullTypeName = "ConfigurationManager.ConfigurationManager"; private const string InstallSideInitialDescription = "[Multiplayer requirements] This setting requires that the mod "; private const string UnusedSettingText = "# Old unused setting, can be deleted safely: "; private ConfigFile configFile; private string currentSectionName; private int currentSectionOrder; private int currentConfigOrder; public ConfigManagerController(ConfigFile configFile) { this.configFile = configFile ?? throw new ArgumentNullException("configFile"); currentSectionOrder = 0; currentConfigOrder = int.MaxValue; if (AssemblyUtils.GetTypeFromLoadedAssemblies("ConfigurationManager.ConfigurationManager", true) != null) { ConfigurationManagerPatch.PatchSelf(); } ConfigEntryBasePatch.PatchSelf(); } private void SetSection(string sectionName, bool skipSectionOrder = false) { if (!(sectionName == currentSectionName)) { if (!skipSectionOrder) { currentSectionOrder++; } currentConfigOrder = int.MaxValue; currentSectionName = sectionName; } } public static bool RefreshGUI() { if (ConfigurationManagerPatch.ConfigMngInstance != null) { string text = "BuildSettingList"; try { ReflectionHelper.CallMethod(ConfigurationManagerPatch.ConfigMngInstance, text, (object[])null); return true; } catch (Exception ex) { TimeLogger.Logger.LogExceptionWithMessage("Error when trying to call the method to refresh ConfigurationManager GUI.", ex, (LogCategories)64); } } return false; } public ConfigEntry AddSectionNote(string sectionText, string description = null, IConfigPatchDependence patchInstanceDependency = null, bool hidden = false, bool isAdvanced = false) { string text = "\u200b"; ConfigEntryBasePatch.AddNote(text); return AddConfig(sectionText, text, defaultValue: true, description, patchInstanceDependency, MultiplayerModInstallSide.Unspecified, hidden, disabled: true, null, showRangeAsPercent: false, hideDefaultButton: true, isAdvanced, skipSectionIncrease: true); } public ConfigEntry AddQuasiNote(string sectionName, string key, string textboxMessage, string description = null, IConfigPatchDependence patchInstanceDependency = null, bool hidden = false, bool isAdvanced = false) { return AddQuasiNote(sectionName, key, textboxMessage, description, patchInstanceDependency, hidden, isAdvanced, skipSectionIncrease: false); } public ConfigEntry AddGUIHiddenNote(string sectionName, string key, string description = null, IConfigPatchDependence patchInstanceDependency = null, bool isAdvanced = false) { return AddQuasiNote(sectionName, key, null, description, patchInstanceDependency, hidden: true, isAdvanced, skipSectionIncrease: true); } private ConfigEntry AddQuasiNote(string sectionName, string key, string textboxMessage, string description = null, IConfigPatchDependence patchInstanceDependency = null, bool hidden = false, bool isAdvanced = false, bool skipSectionIncrease = false) { if (textboxMessage == null) { textboxMessage = ""; } ConfigEntryBasePatch.AddNote(key); return AddConfig(sectionName, key, textboxMessage, description, patchInstanceDependency, MultiplayerModInstallSide.Unspecified, hidden, disabled: true, null, showRangeAsPercent: false, hideDefaultButton: true, isAdvanced, skipSectionIncrease); } public ConfigEntry AddConfig(string sectionName, string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, bool hideDefaultButton = false, bool isAdvanced = false) { return AddConfig(sectionName, key, defaultValue, description, patchInstanceDependency, modInstallSide, hidden, disabled, null, showRangeAsPercent: false, hideDefaultButton, isAdvanced); } public ConfigEntry AddConfigWithAcceptableValues(string sectionName, string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, AcceptableValueList acceptableValueList = null, bool hideDefaultButton = false, bool isAdvanced = false) where T : IEquatable { return AddConfig(sectionName, key, defaultValue, description, patchInstanceDependency, modInstallSide, hidden, disabled, (AcceptableValueBase)(object)acceptableValueList, showRangeAsPercent: false, hideDefaultButton, isAdvanced); } public ConfigEntry AddConfigWithAcceptableValues(string sectionName, string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, AcceptableValueRange acceptableValueRange = null, bool showRangeAsPercent = false, bool hideDefaultButton = false, bool isAdvanced = false) where T : IComparable { return AddConfig(sectionName, key, defaultValue, description, patchInstanceDependency, modInstallSide, hidden, disabled, (AcceptableValueBase)(object)acceptableValueRange, showRangeAsPercent, hideDefaultButton, isAdvanced); } private ConfigEntry AddConfig(string sectionName, string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, AcceptableValueBase acceptableVal = null, bool showRangeAsPercent = false, bool hideDefaultButton = false, bool isAdvanced = false, bool skipSectionIncrease = false) { SetSection(sectionName, skipSectionIncrease); return AddConfig(key, defaultValue, description, patchInstanceDependency, modInstallSide, hidden, disabled, acceptableVal, showRangeAsPercent, hideDefaultButton, isAdvanced); } private ConfigEntry AddConfig(string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, AcceptableValueBase acceptableVal = null, bool showRangeAsPercent = false, bool hideDefaultButton = false, bool isAdvanced = false) { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown if (description == null) { description = ""; } if (patchInstanceDependency != null) { hidden = true; } if (modInstallSide != 0) { string descriptionFromInstallSide = GetDescriptionFromInstallSide(modInstallSide); if (!Utility.IsNullOrWhiteSpace(description)) { description += "\n\n"; } description += descriptionFromInstallSide; } ConfigDescription val = new ConfigDescription(description, acceptableVal, new object[1] { new ConfigurationManagerAttributes { IsAdvanced = isAdvanced, Order = currentConfigOrder--, Browsable = !hidden, DefaultValue = defaultValue, ShowRangeAsPercent = showRangeAsPercent, ReadOnly = disabled, HideDefaultButton = hideDefaultButton, Category = currentSectionOrder.ToString("D2") + ". " + currentSectionName } }); ConfigEntry val2 = configFile.Bind(currentSectionName, key, defaultValue, val); patchInstanceDependency?.SetSettingPatchDependence(val2); return val2; } public void RenameOrphanedSettings() { try { string backingFieldName = GetBackingFieldName("OrphanedEntries"); FieldInfo fieldInfo = AccessTools.Field(((object)configFile).GetType(), backingFieldName); if (fieldInfo == null) { TimeLogger.Logger.LogWarning("The field '" + backingFieldName + "' could not be " + $"found in type {((object)configFile).GetType()}", (LogCategories)64); return; } IDictionary dictionary = (IDictionary)fieldInfo.GetValue(configFile); if (dictionary == null) { return; } string text = ""; bool flag = false; foreach (KeyValuePair item in dictionary) { if (!item.Key.Key.StartsWith("# Old unused setting, can be deleted safely: ")) { if (!string.IsNullOrEmpty(text)) { text += " || "; } text = text + "\"" + item.Key.Key + "\""; RenameSettingKeyname(item.Key); flag = true; } } if (flag) { configFile.Save(); TimeLogger.Logger.LogDebug("Some config keys were found to be obsolete: " + text + ". They got renamed to make clear that they are of no use anymore.", (LogCategories)64); } } catch (Exception ex) { TimeLogger.Logger.LogExceptionWithMessage("Error while trying to rename orphaned Bepinex config settings.", ex, (LogCategories)64); } } private void RenameSettingKeyname(ConfigDefinition key) { AccessTools.Field(((object)key).GetType(), GetBackingFieldName("Key")).SetValue(key, "# Old unused setting, can be deleted safely: " + key.Key); } public string GetBackingFieldName(string varName) { return "<" + varName + ">k__BackingField"; } public bool Remove(string section, string key) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown return configFile.Remove(new ConfigDefinition(section, key)); } public bool Remove(ConfigDefinition key) { return configFile.Remove(key); } public void ClearAllConfigs() { configFile.Clear(); } public int Count() { return configFile.Count; } private string GetDescriptionFromInstallSide(MultiplayerModInstallSide modInstallSide) { return "[Multiplayer requirements] This setting requires that the mod " + modInstallSide switch { MultiplayerModInstallSide.Any => "is installed for the player/s that wants its functionality, hosting or not.", MultiplayerModInstallSide.HostSideOnly => "is installed in the host. It is not needed in the client.", MultiplayerModInstallSide.HostAndOptionalClient => "is installed in the host. Clients can also install it.", MultiplayerModInstallSide.HostAndSoftClient => "is installed in the host, and should be installed in the clients.", MultiplayerModInstallSide.HostAndHardClient => "is installed in both the host and the clients.", _ => "", }; } public bool SetConfigAttribute(string section, string key, ConfigurationManagerAttributes.ConfigAttributes configAttribute, T2 attrValue) { return GetConfigEntry(section, key).SetConfigAttribute(configAttribute, attrValue); } public R GetConfigAttribute(string section, string key, ConfigurationManagerAttributes.ConfigAttributes configAttribute) { return GetConfigEntry(section, key).GetConfigAttribute(configAttribute); } private ConfigEntry GetConfigEntry(string section, string key) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown ConfigEntry val = default(ConfigEntry); if (!configFile.TryGetEntry(new ConfigDefinition(section, key), ref val) || ((ConfigEntryBase)val).Description == null || ((ConfigEntryBase)val).Description.Tags == null) { throw new InvalidOperationException("The config entry with section \"" + section + "\" and key \"" + key + "\" could not be found."); } return val; } } public static class ConfigManagerExtension { public static bool SetConfigAttribute(this ConfigEntry configEntry, ConfigurationManagerAttributes.ConfigAttributes configAttribute, T2 attrValue) { ConfigurationManagerAttributes configManagerAttribInstance; FieldInfo configAttributeFieldInfo = GetConfigAttributeFieldInfo(configEntry, configAttribute, out configManagerAttribInstance); if (configAttributeFieldInfo == null) { return false; } configAttributeFieldInfo.SetValue(configManagerAttribInstance, attrValue); return true; } public static R GetConfigAttribute(this ConfigEntry configEntry, ConfigurationManagerAttributes.ConfigAttributes configAttribute) { ConfigurationManagerAttributes configManagerAttribInstance; FieldInfo configAttributeFieldInfo = GetConfigAttributeFieldInfo(configEntry, configAttribute, out configManagerAttribInstance); if (configAttributeFieldInfo == null) { throw new InvalidOperationException("The attribute \"" + configAttribute.ToString() + "\" could not be found."); } object value = configAttributeFieldInfo.GetValue(configManagerAttribInstance); if (value != null && !(value is R)) { throw new InvalidOperationException("The attribute value is of type \"" + value.GetType().FullName + "\" and is not compatible with the type \"" + typeof(R).FullName + "\" passed by parameter."); } return (R)value; } private static FieldInfo GetConfigAttributeFieldInfo(ConfigEntry configEntry, ConfigurationManagerAttributes.ConfigAttributes configAttribute, out ConfigurationManagerAttributes configManagerAttribInstance) { configManagerAttribInstance = null; object[] tags = ((ConfigEntryBase)configEntry).Description.Tags; foreach (object obj in tags) { if (obj is ConfigurationManagerAttributes) { configManagerAttribInstance = (ConfigurationManagerAttributes)obj; break; } } if (configManagerAttribInstance == null) { return null; } FieldInfo fieldInfo = AccessTools.Field(typeof(ConfigurationManagerAttributes), configAttribute.ToString()); if (fieldInfo == null) { return null; } return fieldInfo; } } } namespace Damntry.UtilsBepInEx.Configuration.ConfigurationManager.SettingAttributes { public sealed class ConfigurationManagerAttributes { public enum ConfigAttributes { ShowRangeAsPercent, CustomDrawer, CustomHotkeyDrawer, Browsable, Category, DefaultValue, HideDefaultButton, HideSettingName, Description, DispName, Order, ReadOnly, IsAdvanced } public delegate void CustomHotkeyDrawerFunc(ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput); public bool? ShowRangeAsPercent; public Action CustomDrawer; public CustomHotkeyDrawerFunc CustomHotkeyDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public Func ObjToStr; public Func StrToObj; } } namespace Damntry.UtilsBepInEx.Configuration.ConfigurationManager.Patch { internal class ConfigEntryBasePatch { private static readonly HashSet keyNotes; internal static Lazy Harmony { get; } static ConfigEntryBasePatch() { Harmony = new Lazy((Func)(() => new Harmony(typeof(ConfigEntryBasePatch).FullName))); keyNotes = new HashSet(); } internal static void PatchSelf() { try { Harmony.Value.PatchAll(typeof(ConfigEntryBasePatch)); } catch (Exception ex) { TimeLogger.Logger.LogExceptionWithMessage("Error while trying to apply patch in type ConfigEntryBasePatch", ex, (LogCategories)64); } } public static void AddNote(string configKey) { keyNotes.Add(configKey); } [HarmonyPatch(typeof(ConfigEntryBase), "WriteDescription")] [HarmonyPrefix] public static bool WriteDescriptionPatch(ConfigEntryBase __instance, StreamWriter writer) { if (keyNotes.Contains(__instance.Definition.Key)) { if (!string.IsNullOrEmpty(__instance.Description.Description)) { writer.WriteLine("## " + __instance.Description.Description.Replace("\n", "\n## ")); } return false; } return true; } } [HarmonyPatch] public class ConfigurationManagerPatch { private static Lazy Harmony { get; } = new Lazy((Func)(() => new Harmony(typeof(ConfigurationManagerPatch).FullName))); internal static object ConfigMngInstance { get; private set; } internal static void PatchSelf() { try { Harmony.Value.PatchAll(typeof(ConfigurationManagerPatch)); } catch (Exception ex) { TimeLogger.Logger.LogExceptionWithMessage("Error while trying to apply patch in type ConfigurationManagerPatch", ex, (LogCategories)64); } } [HarmonyPatchStringTypes("ConfigurationManager.ConfigurationManager", "Update")] [HarmonyPostfix] internal static void GetConfigManagerInstancePatch(object __instance) { if (__instance != null) { ConfigMngInstance = __instance; Harmony.Value.UnpatchSelf(); } } } } namespace Damntry.UtilsBepInEx.Components { public class InputBepInEx : InputDetection { private static InputBepInEx _instance; private readonly Dictionary> configKeyPresses; public static InputBepInEx Instance => _instance ?? (_instance = new InputBepInEx()); public InputBepInEx() : base(typeof(InputBepInEx), (OnValidationError)0, false, (List)null) { configKeyPresses = new Dictionary>(); } protected InputBepInEx(Type callingKeypressClass, OnValidationError onError, bool restrictAllModifiers, [ParamCollection] List restrictedKeyBinds) : base(callingKeypressClass, onError, restrictAllModifiers, restrictedKeyBinds) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) configKeyPresses = new Dictionary>(); } protected override void HotkeyValidationError(string hotkeyName, string message, KeyBind keyBind) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (IsConfigManagedKeyPress(hotkeyName, out var configEntry)) { EventMethods.ExecuteBypassingEvent((Action)delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) configEntry.Value = new KeyboardShortcut(keyBind.KeyCode, keyBind.Modifiers); }, (EventData[])(object)new EventData[1] { new EventData(((object)configEntry).GetType(), (object)configEntry, "SettingChanged") }); ConfigManagerController.RefreshGUI(); } ((InputDetection)this).HotkeyValidationError(hotkeyName, message, keyBind); } public void AddHotkeyFromConfig(ConfigEntry hotkeyConfig, InputState inputState, HotkeyContext hotkeyContext, Action action) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) string hotkeyName = GetHotkeyNameFromConfig(hotkeyConfig, inputState); string hotkeyName2 = hotkeyName; KeyboardShortcut value = hotkeyConfig.Value; KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey; value = hotkeyConfig.Value; AddHotkeyBepInExInternal(hotkeyName2, mainKey, ((KeyboardShortcut)(ref value)).Modifiers.ToArray(), inputState, hotkeyContext, 65, action, null, hotkeyConfig); hotkeyConfig.SettingChanged += delegate { //IL_0013: Unknown result type (might be due to invalid IL or missing references) ChangeConfigHotkey(hotkeyConfig, hotkeyName, inputState, hotkeyContext, 65, action); }; } public void AddHotkeyFromConfig(ConfigEntry hotkeyConfig, InputState inputState, HotkeyContext hotkeyContext, int cooldownMillis, Action action) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) string hotkeyName = GetHotkeyNameFromConfig(hotkeyConfig, inputState); string hotkeyName2 = hotkeyName; KeyboardShortcut value = hotkeyConfig.Value; KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey; value = hotkeyConfig.Value; AddHotkeyBepInExInternal(hotkeyName2, mainKey, ((KeyboardShortcut)(ref value)).Modifiers.ToArray(), inputState, hotkeyContext, cooldownMillis, action, null, hotkeyConfig); hotkeyConfig.SettingChanged += delegate { //IL_0013: Unknown result type (might be due to invalid IL or missing references) ChangeConfigHotkey(hotkeyConfig, hotkeyName, inputState, hotkeyContext, cooldownMillis, action); }; } private string GetHotkeyNameFromConfig(ConfigEntry hotkeyConfig, InputState inputState) { return ((ConfigEntryBase)hotkeyConfig).Definition.Key + "-" + ((object)(InputState)(ref inputState)).ToString(); } private void ChangeConfigHotkey(ConfigEntry hotkeyConfig, string hotkeyName, InputState inputState, HotkeyContext hotkeyContext, int cooldownMillis, Action action) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) InputData value; bool num = ((InputDetection)this).KeyPressActions.TryGetValue(hotkeyName, out value); KeyboardShortcut value2 = hotkeyConfig.Value; KeyCode[] modifiers = ((KeyboardShortcut)(ref value2)).Modifiers.ToArray(); if (num) { ChangeHotkeyBepInExInternal(hotkeyName, ((KeyboardShortcut)(ref value2)).MainKey, modifiers, fromConfig: true); } else { AddHotkeyBepInExInternal(hotkeyName, ((KeyboardShortcut)(ref value2)).MainKey, modifiers, inputState, hotkeyContext, cooldownMillis, action, null, hotkeyConfig); } } public override bool TryAddHotkey(string hotkeyName, KeyCode keyCode, KeyCode[] modifiers, InputState inputState, HotkeyContext hotkeyContext, Action action) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) return AddHotkeyBepInExInternal(hotkeyName, keyCode, modifiers, inputState, hotkeyContext, 65, action, null); } public override bool TryAddHotkey(string hotkeyName, KeyCode keyCode, KeyCode[] modifiers, InputState inputState, HotkeyContext hotkeyContext, int cooldownMillis, Action action) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) return AddHotkeyBepInExInternal(hotkeyName, keyCode, modifiers, inputState, hotkeyContext, cooldownMillis, action, null); } public bool TryAddHotkey(string hotkeyName, KeyCode keyCode, KeyCode[] modifiers, InputState inputState, HotkeyContext hotkeyContext, Action action, string groupName) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) return AddHotkeyBepInExInternal(hotkeyName, keyCode, modifiers, inputState, hotkeyContext, 65, action, groupName); } public bool TryAddHotkey(string hotkeyName, KeyCode keyCode, KeyCode[] modifiers, InputState inputState, HotkeyContext hotkeyContext, int cooldownMillis, Action action, string groupName) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) return AddHotkeyBepInExInternal(hotkeyName, keyCode, modifiers, inputState, hotkeyContext, cooldownMillis, action, groupName); } protected bool AddHotkeyBepInExInternal(string hotkeyName, KeyCode keyCode, KeyCode[] modifiers, InputState inputState, HotkeyContext hotkeyContext, int cooldownMillis, Action action, string groupName, ConfigEntry hotkeyConfig = null) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 HotkeyAddResult val = ((InputDetection)this).AddHotkeyInternal(hotkeyName, keyCode, modifiers, inputState, hotkeyContext, cooldownMillis, action, groupName); if (hotkeyConfig != null && (int)val != 2) { if (hotkeyConfig == null) { throw new InvalidOperationException("Tried to add config hotkey "); } configKeyPresses.Add(hotkeyName, hotkeyConfig); } return (int)val == 0; } public override bool ChangeHotkey(string hotkeyName, KeyCode keyCode, KeyCode[] modifiers) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return ChangeHotkeyBepInExInternal(hotkeyName, keyCode, modifiers, fromConfig: false); } protected bool ChangeHotkeyBepInExInternal(string hotkeyName, KeyCode keyCode, KeyCode[] modifiers, bool fromConfig) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (IsConfigManagedKeyPress(hotkeyName, out var _) && !fromConfig) { LogConfigManagedWarningMessage(hotkeyName); return false; } return ((InputDetection)this).ChangeHotkeyInternal(hotkeyName, keyCode, modifiers); } public override bool RemoveHotkey(string hotkeyName) { return RemoveHotkeyBepInExInternal(hotkeyName, null, fromConfig: false); } public override bool RemoveHotkeyGroup(string groupName) { return RemoveHotkeyBepInExInternal(null, groupName, fromConfig: false); } protected bool RemoveHotkeyBepInExInternal(string hotkeyName, string groupName, bool fromConfig) { if (!string.IsNullOrEmpty(hotkeyName)) { return RemoveHotkeyFromName(hotkeyName, fromConfig); } if (!string.IsNullOrEmpty(groupName)) { if (((InputDetection)this).NamedGroupsCache.TryGetValue(groupName, out var value)) { bool flag = true; string[] array = value.ToArray(); foreach (string hotkeyName2 in array) { flag &= RemoveHotkeyFromName(hotkeyName2, fromConfig); } return flag; } return false; } throw new ArgumentException("Both hotkeyName and groupName cant be empty."); } private bool RemoveHotkeyFromName(string hotkeyName, bool fromConfig) { ConfigEntry configEntry; bool flag = IsConfigManagedKeyPress(hotkeyName, out configEntry); if (flag && !fromConfig) { LogConfigManagedWarningMessage(hotkeyName); return false; } bool flag2 = ((InputDetection)this).RemoveHotkeysInternal(hotkeyName, (string)null); if (flag && flag2) { configKeyPresses.Remove(hotkeyName); } return flag2; } public bool IsConfigManagedKeyPress(string hotkeyName, out ConfigEntry configEntry) { return configKeyPresses.TryGetValue(hotkeyName, out configEntry); } private void LogConfigManagedWarningMessage(string hotkeyName) { TimeLogger.Logger.LogWarning("The hotkey '" + hotkeyName + "' is being automatically managed from the BepInEx settings and cant be manually handled.", (LogCategories)33554432); } } }