using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using BepInEx.Preloader.Patching; using HarmonyLib; using JG224.JotunnCompatibilityPatch.Rewriting; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("JG224.JotunnCompatibilityPatch.Preloader")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.5.0.0")] [assembly: AssemblyInformationalVersion("0.5.0")] [assembly: AssemblyProduct("JG224.JotunnCompatibilityPatch.Preloader")] [assembly: AssemblyTitle("JG224.JotunnCompatibilityPatch.Preloader")] [assembly: AssemblyVersion("0.5.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 JG224.JotunnCompatibilityPatch.Rewriting { public static class CompatibilityRewriter { public const string OriginalHash = "F65751BC15E7AE7466B0F7D3B38C758397741C99A50890DEB19ACF738376BFB1"; private static IEnumerable Types(ModuleDefinition module) { return ((IEnumerable)module.Types).SelectMany(Types); } private static IEnumerable Types(TypeDefinition type) { return ((IEnumerable)(object)new TypeDefinition[1] { type }).Concat(((IEnumerable)type.NestedTypes).SelectMany(Types)); } private static void Require(bool condition, string reason) { if (!condition) { throw new InvalidOperationException(reason); } } public static byte[] Rewrite(byte[] input, string managed) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //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_0060: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0803: Unknown result type (might be due to invalid IL or missing references) //IL_0808: Unknown result type (might be due to invalid IL or missing references) //IL_0817: Unknown result type (might be due to invalid IL or missing references) //IL_081c: Unknown result type (might be due to invalid IL or missing references) //IL_0820: Unknown result type (might be due to invalid IL or missing references) //IL_0825: Unknown result type (might be due to invalid IL or missing references) //IL_0827: Unknown result type (might be due to invalid IL or missing references) //IL_082a: Unknown result type (might be due to invalid IL or missing references) //IL_083c: Expected I4, but got Unknown //IL_0840: Unknown result type (might be due to invalid IL or missing references) //IL_084e: Unknown result type (might be due to invalid IL or missing references) //IL_085c: Unknown result type (might be due to invalid IL or missing references) //IL_06e8: Unknown result type (might be due to invalid IL or missing references) //IL_072c: Unknown result type (might be due to invalid IL or missing references) //IL_074c: Unknown result type (might be due to invalid IL or missing references) //IL_0a2f: Unknown result type (might be due to invalid IL or missing references) //IL_0a34: Unknown result type (might be due to invalid IL or missing references) //IL_0a45: Expected O, but got Unknown using SHA256 sHA = SHA256.Create(); Require(BitConverter.ToString(sHA.ComputeHash(input)).Replace("-", "") == "F65751BC15E7AE7466B0F7D3B38C758397741C99A50890DEB19ACF738376BFB1", "Unsupported Jotunn input. Requires the exact original 2.29.2 release; original file was not changed."); DefaultAssemblyResolver val = new DefaultAssemblyResolver(); try { ((BaseAssemblyResolver)val).AddSearchDirectory(managed); AssemblyDefinition game = AssemblyDefinition.ReadAssembly(Path.Combine(managed, "assembly_valheim.dll"), new ReaderParameters { AssemblyResolver = (IAssemblyResolver)(object)val }); ModuleDefinition module; try { AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly((Stream)new MemoryStream(input, writable: false), new ReaderParameters { AssemblyResolver = (IAssemblyResolver)(object)val }); try { module = val2.MainModule; FieldDefinition val3 = ((IEnumerable)Native("PieceTable").Fields).Single((FieldDefinition f) => ((MemberReference)f).Name == "m_availablePiecesByCategory"); Require(((MemberReference)((FieldReference)val3).FieldType).FullName == "System.Collections.Generic.List`1>", "Unexpected piece category storage."); MethodDefinition val4 = NativeMethod("Character", "Message", new string[5] { "MessageHud/MessageType", "System.String", "System.Int32", "UnityEngine.Sprite", "System.Boolean" }); MethodDefinition val5 = NativeMethod("TerrainComp", "Save", new string[1] { "System.Boolean" }); MethodDefinition val6 = NativeMethod("Heightmap", "Poke", new string[2] { "System.Int32", "System.Boolean" }); Require(object.Equals(((IEnumerable)((MethodReference)val4).Parameters).Last().Constant, false) && object.Equals(((MethodReference)val5).Parameters[0].Constant, false) && object.Equals(((MethodReference)val6).Parameters[0].Constant, 0) && object.Equals(((MethodReference)val6).Parameters[1].Constant, false), "Native defaults changed."); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; foreach (MethodDefinition item in from m in Types(module).SelectMany((TypeDefinition t) => (IEnumerable)t.Methods) where m.HasBody select m) { Instruction[] array = item.Body.Instructions.ToArray(); foreach (Instruction val7 in array) { object operand = val7.Operand; FieldReference val8 = (FieldReference)((operand is FieldReference) ? operand : null); if (val8 != null && ((MemberReference)((MemberReference)val8).DeclaringType).FullName == "PieceTable" && ((MemberReference)val8).Name == "m_availablePieces") { Require(((MemberReference)val8.FieldType).FullName == ((MemberReference)((FieldReference)val3).FieldType).FullName, "Unexpected original piece field signature."); val7.Operand = module.ImportReference((FieldReference)(object)val3); num++; } object operand2 = val7.Operand; MethodReference val9 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); if (val9 != null) { if (((MemberReference)((MemberReference)val9).DeclaringType).FullName == "Character" && ((MemberReference)val9).Name == "Message" && val9.Parameters.Count == 4) { AppendFalseArgument(item, val7, module.ImportReference((MethodReference)(object)val4)); num2++; } if (((MemberReference)((MemberReference)val9).DeclaringType).FullName == "TerrainComp" && ((MemberReference)val9).Name == "Save" && val9.Parameters.Count == 0) { AppendFalseArgument(item, val7, module.ImportReference((MethodReference)(object)val5)); num3++; } if (((MemberReference)((MemberReference)val9).DeclaringType).FullName == "Heightmap" && ((MemberReference)val9).Name == "Poke" && val9.Parameters.Count == 1) { Require(Integer(val7.Previous) == 0, "Only immediate full terrain Poke is supported."); AppendFalseArgument(item, val7, module.ImportReference((MethodReference)(object)val6)); num4++; } } } } Require(num == 7 && num2 == 3 && num3 == 1 && num4 == 1, $"Unexpected original call counts: pieces={num}, messages={num2}, saves={num3}, pokes={num4}."); TypeDefinition val10 = Owner("Jotunn.Utils.ExtEquipment"); string[] array2 = new string[3] { "RightItem", "RightBackItem", "ChestItem" }; foreach (string slot in array2) { MethodDefinition val11 = ((IEnumerable)val10.Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == "VisEquipment_Set" + slot); Require(((ParameterReference)((MethodReference)((slot == "ChestItem") ? NativeMethod("VisEquipment", "Set" + slot, new string[1] { "System.Int32" }) : NativeMethod("VisEquipment", "Set" + slot, new string[2] { "System.Int32", "System.Int32" }))).Parameters[0]).Name == "itemHash", "Unexpected native equipment argument."); Require(((MethodReference)val11).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)val11).Parameters[1]).ParameterType).FullName == "System.String", "Unexpected equipment prefix."); ((ParameterReference)((MethodReference)val11).Parameters[1]).Name = "itemHash"; ((ParameterReference)((MethodReference)val11).Parameters[1]).ParameterType = module.TypeSystem.Int32; Instruction oldField = ((IEnumerable)val11.Body.Instructions).Single(delegate(Instruction i) { object operand3 = i.Operand; FieldReference val15 = (FieldReference)((operand3 is FieldReference) ? operand3 : null); return val15 != null && ((MemberReference)((MemberReference)val15).DeclaringType).FullName == "VisEquipment" && ((MemberReference)val15.FieldType).FullName == "System.String"; }); FieldDefinition val12 = ((IEnumerable)Native("VisEquipment").Fields).Single((FieldDefinition f) => ((MemberReference)f).Name == ((MemberReference)(FieldReference)oldField.Operand).Name && ((MemberReference)((FieldReference)f).FieldType).FullName == "System.Int32"); oldField.Operand = module.ImportReference((FieldReference)(object)val12); Instruction obj = ((IEnumerable)val11.Body.Instructions).Single(delegate(Instruction i) { object operand3 = i.Operand; MethodReference val15 = (MethodReference)((operand3 is MethodReference) ? operand3 : null); return val15 != null && ((MemberReference)((MemberReference)val15).DeclaringType).FullName == "System.String" && ((MemberReference)val15).Name == "op_Equality"; }); obj.OpCode = OpCodes.Ceq; obj.Operand = null; Instruction val13 = ((IEnumerable)val11.Body.Instructions).Single(delegate(Instruction i) { object operand3 = i.Operand; MethodReference val15 = (MethodReference)((operand3 is MethodReference) ? operand3 : null); return val15 != null && ((MemberReference)((MemberReference)val15).DeclaringType).FullName == "System.String" && ((MemberReference)val15).Name == "IsNullOrEmpty"; }); val13.OpCode = OpCodes.Ldc_I4_0; val13.Operand = null; val11.Body.GetILProcessor().InsertAfter(val13, Instruction.Create(OpCodes.Ceq)); } MethodDefinition val14 = ((IEnumerable)Owner("Jotunn.Managers.SkillManager/Patches").Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == "Skills_IsSkillValid"); Require(NativeMethod("Skills", "IsSkillValid", new string[1] { "Skills/SkillType" }).IsStatic, "Skills.IsSkillValid must now be static."); Require(((ParameterReference)((MethodReference)val14).Parameters[0]).Name == "__instance", "Unexpected skills injection."); Enumerator enumerator2 = val14.Body.Instructions.GetEnumerator(); try { while (enumerator2.MoveNext()) { Instruction current2 = enumerator2.Current; OpCode opCode = current2.OpCode; Code code = ((OpCode)(ref opCode)).Code; switch (code - 2) { case 0: current2.OpCode = OpCodes.Ldnull; break; case 1: current2.OpCode = OpCodes.Ldarg_0; break; case 2: current2.OpCode = OpCodes.Ldarg_1; break; } } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } ((MethodReference)val14).Parameters.RemoveAt(0); MethodDefinition method = ((IEnumerable)Owner("Jotunn.Managers.CommandManager").Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == "CreateVanillaCommand"); NativeMethod("Terminal/ConsoleCommand", ".ctor", new string[13] { "System.String", "System.String", "Terminal/ConsoleEvent", "System.Boolean", "System.Boolean", "System.Boolean", "System.Boolean", "System.Boolean", "System.Boolean", "Terminal/ConsoleOptionsFetcher", "System.Boolean", "System.Boolean", "System.Boolean" }); ExpandConstructorArray(method, "System.Type", module); ExpandConstructorArray(method, "System.Object", module); PreservePieceAvailability(((IEnumerable)Owner("Jotunn.Managers.PieceManager").Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == "ReorderAllCategoryPieces")); foreach (MethodDefinition item2 in from m in Types(module).SelectMany((TypeDefinition t) => (IEnumerable)t.Methods) where m.HasBody select m) { ExpandBranches(item2); } using MemoryStream memoryStream = new MemoryStream(); val2.Write((Stream)memoryStream, new WriterParameters { Timestamp = 0u }); return memoryStream.ToArray(); } finally { ((IDisposable)val2)?.Dispose(); } } finally { if (game != null) { ((IDisposable)game).Dispose(); } } TypeDefinition Native(string name) { return Types(game.MainModule).Single((TypeDefinition t) => ((MemberReference)t).FullName == name); } MethodDefinition NativeMethod(string type, string name, string[] parameters) { return ((IEnumerable)Native(type).Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == name && ((IEnumerable)((MethodReference)m).Parameters).Select((ParameterDefinition p) => ((MemberReference)((ParameterReference)p).ParameterType).FullName).SequenceEqual(parameters)); } TypeDefinition Owner(string name) { return Types(module).Single((TypeDefinition t) => ((MemberReference)t).FullName == name); } } finally { ((IDisposable)val)?.Dispose(); } } private static void AppendFalseArgument(MethodDefinition method, Instruction instruction, MethodReference replacement) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0008: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) OpCode opCode = instruction.OpCode; Require(opCode == OpCodes.Call || opCode == OpCodes.Callvirt, "Expected a call instruction."); instruction.OpCode = OpCodes.Ldc_I4_0; instruction.Operand = null; method.Body.GetILProcessor().InsertAfter(instruction, Instruction.Create(opCode, replacement)); } private static int? Integer(Instruction instruction) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected I4, but got Unknown OpCode opCode = instruction.OpCode; Code code = ((OpCode)(ref opCode)).Code; switch (code - 21) { case 0: return -1; case 1: return 0; case 2: return 1; case 3: return 2; case 4: return 3; case 5: return 4; case 6: return 5; case 7: return 6; case 8: return 7; case 9: return 8; case 10: case 11: return Convert.ToInt32(instruction.Operand); default: return null; } } private static void SetInteger(Instruction instruction, int value) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) instruction.OpCode = OpCodes.Ldc_I4; instruction.Operand = value; } private static void ExpandConstructorArray(MethodDefinition method, string arrayType, ModuleDefinition module) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) Instruction obj = ((IEnumerable)method.Body.Instructions).Single((Instruction i) => i.OpCode == OpCodes.Newarr && ((MemberReference)(TypeReference)i.Operand).FullName == arrayType); Require(Integer(obj.Previous) == 12, "Unexpected original constructor array length."); SetInteger(obj.Previous, 13); Instruction next = obj.Next; Instruction val = null; for (int num = 0; num < 12; num++) { Require(next.OpCode == OpCodes.Dup && Integer(next.Next) == num, "Unexpected constructor array initializer."); if (num >= 8) { SetInteger(next.Next, num + 1); } while (next.OpCode != OpCodes.Stelem_Ref) { next = next.Next; } if (num == 7) { val = next; } next = next.Next; } List list = new List { Instruction.Create(OpCodes.Dup), Instruction.Create(OpCodes.Ldc_I4, 8) }; if (arrayType == "System.Type") { MethodReference val2 = (MethodReference)((IEnumerable)method.Body.Instructions).First(delegate(Instruction i) { object operand = i.Operand; MethodReference val3 = (MethodReference)((operand is MethodReference) ? operand : null); return val3 != null && ((MemberReference)val3).Name == "GetTypeFromHandle"; }).Operand; list.Add(Instruction.Create(OpCodes.Ldtoken, module.TypeSystem.Boolean)); list.Add(Instruction.Create(OpCodes.Call, val2)); } else { list.Add(Instruction.Create(OpCodes.Ldc_I4_0)); list.Add(Instruction.Create(OpCodes.Box, module.TypeSystem.Boolean)); } list.Add(Instruction.Create(OpCodes.Stelem_Ref)); ILProcessor iLProcessor = method.Body.GetILProcessor(); foreach (Instruction item in list) { iLProcessor.InsertAfter(val, item); val = item; } } private static void PreservePieceAvailability(MethodDefinition method) { //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) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Expected O, but got Unknown //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) Instruction remove = ((IEnumerable)method.Body.Instructions).Single(delegate(Instruction i) { object operand2 = i.Operand; MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); return val2 != null && ((MemberReference)val2).Name == "Remove"; }); Require(remove.Next.OpCode == OpCodes.Pop, "Expected discarded removal result."); Instruction previous = ((IEnumerable)method.Body.Instructions).First(delegate(Instruction i) { if (i.Offset > remove.Offset) { object operand2 = i.Operand; MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); if (val2 != null) { return ((MemberReference)val2).Name == "MoveNext"; } } return false; }).Previous; remove.Next.OpCode = OpCodes.Brfalse; remove.Next.Operand = previous; Instruction previous2 = ((IEnumerable)method.Body.Instructions).Single(delegate(Instruction i) { object operand2 = i.Operand; MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); return val2 != null && ((MemberReference)val2).Name == "Min"; }).Previous; object operand = previous2.Operand; MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null); Require(val != null && ((MemberReference)val).Name == "get_Count" && previous2.Previous.OpCode == OpCodes.Ldfld, "Unexpected reorder index expression."); previous2.Previous.Previous.OpCode = OpCodes.Ldloc_3; previous2.Previous.Previous.Operand = null; previous2.Previous.OpCode = OpCodes.Nop; previous2.Previous.Operand = null; TypeReference declaringType = ((MemberReference)(MethodReference)remove.Operand).DeclaringType; previous2.Operand = (object)new MethodReference("get_Count", ((MemberReference)method).Module.TypeSystem.Int32, declaringType) { HasThis = true }; } private static void ExpandBranches(MethodDefinition method) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = (from f in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public) where f.FieldType == typeof(OpCode) select f).Select((Func)((FieldInfo f) => (OpCode)f.GetValue(null))).ToDictionary((OpCode c) => ((OpCode)(ref c)).Name); Enumerator enumerator = method.Body.Instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; OpCode opCode = current.OpCode; if ((int)((OpCode)(ref opCode)).OperandType == 15) { opCode = current.OpCode; string name = ((OpCode)(ref opCode)).Name; opCode = current.OpCode; current.OpCode = dictionary[name.Substring(0, ((OpCode)(ref opCode)).Name.Length - 2)]; } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } } } namespace JG224.JotunnCompatibilityPatch.Preloader { public static class Loader { private static readonly object Gate = new object(); private static string _originalPath; public static bool Applied { get; private set; } public static string InputHash { get; private set; } public static Assembly LoadedJotunn { get; private set; } public static string FailureReason { get; private set; } internal static void Refuse(Exception exception) { FailureReason = exception.Message; } internal static void ValidateStartup() { RequireJotunnNotLoaded(); GetLocationMap(); } public static Assembly LoadPlugin(string path) { string fullPath = Path.GetFullPath(path); AssemblyName assemblyName = AssemblyName.GetAssemblyName(fullPath); if (!string.Equals(assemblyName.Name, "Jotunn", StringComparison.Ordinal)) { return Assembly.LoadFile(path); } lock (Gate) { try { if (FailureReason != null) { throw new InvalidOperationException(FailureReason); } if (LoadedJotunn != null) { if (!string.Equals(fullPath, _originalPath, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("A second Jotunn DLL was requested. Keep exactly one pristine baseline Jotunn installation."); } return LoadedJotunn; } RequireJotunnNotLoaded(); RequireSinglePluginCopy(fullPath); IDictionary locationMap = GetLocationMap(); byte[] rawAssembly = CompatibilityRewriter.Rewrite(File.ReadAllBytes(fullPath), Paths.ManagedPath); if (locationMap.TryGetValue(assemblyName.FullName, out var value) && !string.Equals(Path.GetFullPath(value), fullPath, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("BepInEx already mapped Jotunn to a different location: " + value); } locationMap[assemblyName.FullName] = fullPath; Assembly assembly = Assembly.Load(rawAssembly); if (!string.Equals(assembly.FullName, assemblyName.FullName, StringComparison.Ordinal) || !string.Equals(assembly.Location, fullPath, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("BepInEx did not preserve Jotunn's identity and original assembly location."); } _originalPath = fullPath; LoadedJotunn = assembly; InputHash = "F65751BC15E7AE7466B0F7D3B38C758397741C99A50890DEB19ACF738376BFB1"; Applied = true; Patcher.Log.LogInfo((object)("Applied validated Jotunn 2.29.2 compatibility changes in memory. Original DLL unchanged: " + fullPath)); return assembly; } catch (Exception ex) { Refuse(ex); throw new InvalidOperationException("Jotunn Compatibility Patch refused to load Jotunn: " + ex.Message + " Restore pristine Jotunn 2.29.2, BepInExPack 5.4.2333 with runtime patches enabled, and the supported Valheim 1.0.7 API. See the add-on README.", ex); } } } private static void RequireJotunnNotLoaded() { if (AppDomain.CurrentDomain.GetAssemblies().Any((Assembly assembly) => string.Equals(assembly.GetName().Name, "Jotunn", StringComparison.Ordinal))) { throw new InvalidOperationException("Jotunn was loaded before the compatibility bridge. Remove conflicting preloaders or duplicate DLLs and restart."); } } private static void RequireSinglePluginCopy(string target) { string text = Path.GetFullPath(Paths.PluginPath).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); char directorySeparatorChar = Path.DirectorySeparatorChar; string text2 = text + directorySeparatorChar; if (!target.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Jotunn must be loaded from the active BepInEx plugins directory."); } List list = new List(); foreach (string item in Directory.EnumerateFiles(text2, "*.dll", SearchOption.AllDirectories)) { AssemblyName assemblyName; try { assemblyName = AssemblyName.GetAssemblyName(item); } catch (BadImageFormatException) { continue; } if (string.Equals(assemblyName.Name, "Jotunn", StringComparison.Ordinal)) { list.Add(Path.GetFullPath(item)); } } if (list.Count != 1 || !string.Equals(list[0], target, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Expected exactly one baseline Jotunn assembly in the active plugin tree; found " + list.Count + ". Remove the old replacement package and retain upstream Jotunn."); } } private static IDictionary GetLocationMap() { Type patches = typeof(AssemblyPatcher).Assembly.GetType("BepInEx.Preloader.RuntimeFixes.UnityPatches", throwOnError: true); if (!(patches.GetProperty("AssemblyLocations", BindingFlags.Static | BindingFlags.Public)?.GetValue(null, null) is IDictionary result)) { throw new InvalidOperationException("Supported BepInEx assembly-location mapping is unavailable."); } string[] array = new string[2] { "Location", "CodeBase" }; foreach (string propertyName in array) { MethodInfo methodInfo = AccessTools.DeclaredPropertyGetter(typeof(Loader).Assembly.GetType(), propertyName) ?? AccessTools.DeclaredPropertyGetter(typeof(Assembly), propertyName); Patches val = ((methodInfo == null) ? null : Harmony.GetPatchInfo((MethodBase)methodInfo)); if (val == null || !val.Postfixes.Any((Patch patch) => patch.PatchMethod.DeclaringType?.FullName == patches.FullName + "+Get" + propertyName)) { throw new InvalidOperationException("BepInEx runtime assembly " + propertyName + " mapping is inactive. Enable Preloader.ApplyRuntimePatches and restart."); } } return result; } } public static class Patcher { internal const string HarmonyId = "com.jg224.jotunncompatibilitypatch.preloader"; internal static readonly ManualLogSource Log = Logger.CreateLogSource("Jotunn Compatibility Preloader"); private static Harmony _harmony; private static bool _bridgeInstalled; public static IEnumerable TargetDLLs => new string[0]; public static void Patch(AssemblyDefinition assembly) { } public static void Finish() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown if (_harmony != null) { return; } try { _harmony = new Harmony("com.jg224.jotunncompatibilitypatch.preloader"); MethodInfo methodInfo = AccessTools.Method(typeof(Chainloader), "Start", Type.EmptyTypes, (Type[])null); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Patcher), "RequireBridgeInstalled", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patcher), "RoutePluginLoads", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); _bridgeInstalled = true; Loader.ValidateStartup(); AppDomain.CurrentDomain.ProcessExit += OnExit; AppDomain.CurrentDomain.DomainUnload += OnExit; Log.LogInfo((object)"Add-on 3.0.1 armed for pristine Jotunn 2.29.2; the installed DLL stays unchanged."); } catch (Exception ex) { Loader.Refuse(ex); Log.LogError((object)("Compatibility bridge could not initialize: " + ex)); throw; } } public static void RequireBridgeInstalled() { if (!_bridgeInstalled) { throw new InvalidOperationException("Jotunn compatibility plugin-loading bridge failed. Startup stopped to prevent loading an incompatible baseline. " + Loader.FailureReason); } } public static IEnumerable RoutePluginLoads(IEnumerable instructions) { List list = instructions.ToList(); MethodInfo original = typeof(Assembly).GetMethod("LoadFile", new Type[1] { typeof(string) }); MethodInfo method = typeof(Loader).GetMethod("LoadPlugin"); List list2 = list.Where((CodeInstruction instruction) => CodeInstructionExtensions.Calls(instruction, original)).ToList(); if (list2.Count != 1) { throw new InvalidOperationException("Unsupported BepInEx Chainloader: expected exactly one Assembly.LoadFile plugin call, found " + list2.Count + "."); } list2[0].operand = method; return list; } private static void OnExit(object sender, EventArgs args) { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { Log.LogWarning((object)("Bridge shutdown: " + ex.Message)); } } } }