using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Security.Cryptography; using BepInEx; using BepInEx.Logging; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] namespace ADARC.BackpacksValheim1Compat; public static class Patcher { private sealed class PatchSummary { public int RoutedRpcEverybody; public int HarmonyAddItemSignature; public int ItemDataManagerLookups; public int DirectAddItemCalls; public int IsTeleportableCalls; public int AttachArmorCalls; public int CharacterMessageCalls; public int EffectCreateCalls; public override string ToString() { return "Everybody=" + RoutedRpcEverybody + ", HarmonyAddItem=" + HarmonyAddItemSignature + ", ItemDataManager=" + ItemDataManagerLookups + ", directAddItem=" + DirectAddItemCalls + ", IsTeleportable=" + IsTeleportableCalls + ", AttachArmor=" + AttachArmorCalls + ", Message=" + CharacterMessageCalls + ", EffectCreate=" + EffectCreateCalls; } } private const string Original138Sha256 = "AF643D4FC78F0A58AE1851D128E50C9EA040F251B591EC30DE4273342CA94FD4"; private const string PackageFolder = "Smoothbrain-Backpacks"; private const string MarkerName = "Backpacks.dll.adarc-valheim1.sha256"; private const string BackupName = "Backpacks.dll.adarc-original-1.3.8.bak"; private static readonly ManualLogSource Log = Logger.CreateLogSource("ADARC Backpacks Valheim 1 Compat"); public static IEnumerable TargetDLLs { get { InstallCompatibilityPatch(); return new string[1] { "assembly_valheim.dll" }; } } public static void Patch(AssemblyDefinition assembly) { } private static void InstallCompatibilityPatch() { //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown string text = Path.Combine(Paths.BepInExRootPath, "plugins"); if (!Directory.Exists(text)) { throw new DirectoryNotFoundException("BepInEx plugin directory was not found: " + text); } string[] array = (from path2 in Directory.GetFiles(text, "Backpacks.dll", SearchOption.AllDirectories) where HasPathSegment(path2, "Smoothbrain-Backpacks") select path2).ToArray(); if (array.Length != 1) { throw new InvalidOperationException("Expected exactly one official Smoothbrain Backpacks.dll, found " + array.Length + "."); } string text2 = array[0]; string directoryName = Path.GetDirectoryName(text2); string path = Path.Combine(directoryName, "Backpacks.dll.adarc-valheim1.sha256"); string text3 = ComputeSha256(text2); if (File.Exists(path) && string.Equals(File.ReadAllText(path).Trim(), text3, StringComparison.OrdinalIgnoreCase)) { Log.LogInfo((object)"Smoothbrain Backpacks 1.3.8 compatibility patch is already installed."); return; } if (!string.Equals(text3, "AF643D4FC78F0A58AE1851D128E50C9EA040F251B591EC30DE4273342CA94FD4", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Safety stop: Smoothbrain Backpacks.dll does not match the supported 1.3.8 binary (SHA256 " + text3 + ")."); } string text4 = text2 + ".adarc.tmp"; string text5 = Path.Combine(directoryName, "Backpacks.dll.adarc-original-1.3.8.bak"); if (File.Exists(text4)) { File.Delete(text4); } ModuleDefinition val = ModuleDefinition.ReadModule(text2, new ReaderParameters { InMemory = true }); PatchSummary patchSummary; try { patchSummary = PatchModule(val); val.Write(text4); } finally { ((IDisposable)val)?.Dispose(); } File.Copy(text2, text5, overwrite: true); try { File.Copy(text4, text2, overwrite: true); string text6 = ComputeSha256(text2); File.WriteAllText(path, text6 + Environment.NewLine); Log.LogInfo((object)("Patched Smoothbrain Backpacks 1.3.8 for Valheim 1.0.7: " + patchSummary?.ToString() + ".")); Log.LogInfo((object)("Patched Backpacks.dll SHA256 " + text6 + ". Original retained as Backpacks.dll.adarc-original-1.3.8.bak.")); } catch { File.Copy(text5, text2, overwrite: true); throw; } finally { if (File.Exists(text4)) { File.Delete(text4); } } } private static PatchSummary PatchModule(ModuleDefinition module) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //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_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Invalid comparison between Unknown and I4 //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Invalid comparison between Unknown and I4 //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Invalid comparison between Unknown and I4 //IL_0621: Unknown result type (might be due to invalid IL or missing references) //IL_0626: Unknown result type (might be due to invalid IL or missing references) //IL_04f7: Unknown result type (might be due to invalid IL or missing references) //IL_04fc: Unknown result type (might be due to invalid IL or missing references) //IL_0500: Unknown result type (might be due to invalid IL or missing references) //IL_0507: Invalid comparison between Unknown and I4 //IL_053c: Unknown result type (might be due to invalid IL or missing references) //IL_0535: Unknown result type (might be due to invalid IL or missing references) //IL_0563: Unknown result type (might be due to invalid IL or missing references) //IL_0581: Unknown result type (might be due to invalid IL or missing references) //IL_057a: Unknown result type (might be due to invalid IL or missing references) //IL_07bc: Unknown result type (might be due to invalid IL or missing references) //IL_0593: Unknown result type (might be due to invalid IL or missing references) //IL_05b0: Unknown result type (might be due to invalid IL or missing references) //IL_05c4: Unknown result type (might be due to invalid IL or missing references) //IL_083a: Unknown result type (might be due to invalid IL or missing references) //IL_08b8: Unknown result type (might be due to invalid IL or missing references) //IL_094d: Unknown result type (might be due to invalid IL or missing references) //IL_0954: Expected O, but got Unknown //IL_095d: Unknown result type (might be due to invalid IL or missing references) //IL_0964: Expected O, but got Unknown //IL_0968: Unknown result type (might be due to invalid IL or missing references) //IL_073f: Unknown result type (might be due to invalid IL or missing references) PatchSummary patchSummary = new PatchSummary(); List list = ((IEnumerable)module.Types).SelectMany(GetTypeTree).ToList(); OpCode opCode; foreach (TypeDefinition item in list) { Enumerator enumerator2 = item.Methods.GetEnumerator(); try { while (enumerator2.MoveNext()) { MethodDefinition current = enumerator2.Current; if (!current.HasBody) { continue; } Enumerator enumerator3 = current.Body.Instructions.GetEnumerator(); try { while (enumerator3.MoveNext()) { Instruction current2 = enumerator3.Current; object operand = current2.Operand; FieldReference val = (FieldReference)((operand is FieldReference) ? operand : null); opCode = current2.OpCode; if ((int)((OpCode)(ref opCode)).Code == 123 && val != null && ((MemberReference)((MemberReference)val).DeclaringType).FullName == "ZRoutedRpc" && ((MemberReference)val).Name == "Everybody") { current2.OpCode = OpCodes.Ldc_I8; current2.Operand = 0L; patchSummary.RoutedRpcEverybody++; } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } RequireCount("ZRoutedRpc.Everybody field reads", patchSummary.RoutedRpcEverybody, 3); foreach (TypeDefinition item2 in list.Where((TypeDefinition t) => ((MemberReference)t).FullName == "Backpacks.Backpacks/PreventMultipleBackpacksAddItemWithAmount")) { Enumerator enumerator5 = item2.CustomAttributes.GetEnumerator(); try { while (enumerator5.MoveNext()) { CustomAttribute current3 = enumerator5.Current; if (!(((MemberReference)current3.AttributeType).FullName != "HarmonyLib.HarmonyPatch") && current3.ConstructorArguments.Count == 3) { CustomAttributeArgument val2 = current3.ConstructorArguments[2]; if (((CustomAttributeArgument)(ref val2)).Value is CustomAttributeArgument[] array && array.Length == 4 && !(((CustomAttributeArgument)(ref array[0])).Value.ToString() != "ItemDrop/ItemData") && !array.Skip(1).Any((CustomAttributeArgument x) => ((CustomAttributeArgument)(ref x)).Value.ToString() != "System.Int32")) { CustomAttributeArgument[] array2 = (CustomAttributeArgument[])(object)new CustomAttributeArgument[5]; Array.Copy(array, array2, 4); array2[4] = new CustomAttributeArgument(((CustomAttributeArgument)(ref array[0])).Type, (object)module.TypeSystem.Boolean); Collection constructorArguments = current3.ConstructorArguments; val2 = current3.ConstructorArguments[2]; constructorArguments[2] = new CustomAttributeArgument(((CustomAttributeArgument)(ref val2)).Type, (object)array2); patchSummary.HarmonyAddItemSignature++; } } } } finally { ((IDisposable)enumerator5/*cast due to .constrained prefix*/).Dispose(); } } RequireCount("legacy Inventory.AddItem Harmony signatures", patchSummary.HarmonyAddItemSignature, 1); MethodDefinition val3 = ((IEnumerable)(((IEnumerable)list).SingleOrDefault((Func)((TypeDefinition t) => ((MemberReference)t).FullName == "ItemDataManager.ItemInfo")) ?? throw new InvalidOperationException("Safety stop: bundled ItemDataManager.ItemInfo type was not found.")).Methods).SingleOrDefault((Func)((MethodDefinition m) => ((MemberReference)m).Name == ".cctor")); if (val3 == null || !val3.HasBody) { throw new InvalidOperationException("Safety stop: bundled ItemDataManager.ItemInfo static initializer was not found."); } ILProcessor iLProcessor = val3.Body.GetILProcessor(); IList instructions = (IList)val3.Body.Instructions; for (int num = 0; num < instructions.Count - 2; num++) { Instruction val4 = instructions[num]; opCode = val4.OpCode; if ((int)((OpCode)(ref opCode)).Code != 113 || !string.Equals(val4.Operand as string, "AddItem", StringComparison.Ordinal)) { continue; } int num2 = DecodeInt32Constant(instructions[num + 1]); object operand2 = instructions[num + 2].Operand; TypeReference val5 = (TypeReference)((operand2 is TypeReference) ? operand2 : null); if (num2 != 4 && num2 != 7) { continue; } opCode = instructions[num + 2].OpCode; if ((int)((OpCode)(ref opCode)).Code != 138 || val5 == null || ((MemberReference)val5).FullName != "System.Type") { continue; } Instruction val6 = null; MethodReference val7 = null; for (int num3 = num + 3; num3 < instructions.Count; num3++) { object operand3 = instructions[num3].Operand; MethodReference val8 = (MethodReference)((operand3 is MethodReference) ? operand3 : null); if (val8 != null && ((MemberReference)val8).FullName == "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)" && val7 == null) { val7 = val8; } if (val8 != null && ((MemberReference)((MemberReference)val8).DeclaringType).FullName == "HarmonyLib.AccessTools" && ((MemberReference)val8).Name == "DeclaredMethod") { val6 = instructions[num3]; break; } } if (val6 != null && val7 != null) { opCode = val6.Previous.OpCode; if ((int)((OpCode)(ref opCode)).Code == 20) { instructions[num + 1].OpCode = ((num2 == 4) ? OpCodes.Ldc_I4_5 : OpCodes.Ldc_I4_8); instructions[num + 1].Operand = null; Instruction previous = val6.Previous; iLProcessor.InsertBefore(previous, Instruction.Create(OpCodes.Dup)); iLProcessor.InsertBefore(previous, Instruction.Create((num2 == 4) ? OpCodes.Ldc_I4_4 : OpCodes.Ldc_I4_7)); iLProcessor.InsertBefore(previous, Instruction.Create(OpCodes.Ldtoken, module.TypeSystem.Boolean)); iLProcessor.InsertBefore(previous, Instruction.Create(OpCodes.Call, val7)); iLProcessor.InsertBefore(previous, Instruction.Create(OpCodes.Stelem_Ref)); patchSummary.ItemDataManagerLookups++; continue; } } throw new InvalidOperationException("Safety stop: malformed ItemDataManager AddItem lookup with " + num2 + " parameter types."); } RequireCount("legacy ItemDataManager AddItem lookups", patchSummary.ItemDataManagerLookups, 2); foreach (TypeDefinition item3 in list) { Enumerator enumerator2 = item3.Methods.GetEnumerator(); try { while (enumerator2.MoveNext()) { MethodDefinition current4 = enumerator2.Current; if (!current4.HasBody) { continue; } ILProcessor iLProcessor2 = current4.Body.GetILProcessor(); Instruction[] array3 = current4.Body.Instructions.ToArray(); foreach (Instruction val9 in array3) { object operand4 = val9.Operand; MethodReference val10 = (MethodReference)((operand4 is MethodReference) ? operand4 : null); if (val10 != null) { if (((MemberReference)((MemberReference)val10).DeclaringType).FullName == "Inventory" && ((MemberReference)val10).Name == "AddItem" && ((MemberReference)val10.ReturnType).FullName == "System.Boolean" && val10.Parameters.Count == 4 && ((MemberReference)((ParameterReference)val10.Parameters[0]).ParameterType).FullName == "ItemDrop/ItemData" && ((IEnumerable)val10.Parameters).Skip(1).All((ParameterDefinition p) => ((MemberReference)((ParameterReference)p).ParameterType).FullName == "System.Int32")) { iLProcessor2.InsertBefore(val9, Instruction.Create(OpCodes.Ldc_I4_0)); val9.Operand = WithTrailingParameter(val10, module.TypeSystem.Boolean, "skipValidPositionCheck"); patchSummary.DirectAddItemCalls++; } else if (((MemberReference)((MemberReference)val10).DeclaringType).FullName == "Inventory" && ((MemberReference)val10).Name == "IsTeleportable" && val10.Parameters.Count == 0) { iLProcessor2.InsertBefore(val9, Instruction.Create(OpCodes.Ldc_I4_0)); val9.Operand = WithTrailingParameter(val10, module.TypeSystem.Boolean, "allowAllItems"); patchSummary.IsTeleportableCalls++; } else if (((MemberReference)((MemberReference)val10).DeclaringType).FullName == "VisEquipment" && ((MemberReference)val10).Name == "AttachArmor" && val10.Parameters.Count == 2) { iLProcessor2.InsertBefore(val9, Instruction.Create(OpCodes.Ldc_I4_0)); val9.Operand = WithTrailingParameter(val10, module.TypeSystem.Int32, "quality"); patchSummary.AttachArmorCalls++; } else if (((MemberReference)((MemberReference)val10).DeclaringType).FullName == "Character" && ((MemberReference)val10).Name == "Message" && val10.Parameters.Count == 4) { iLProcessor2.InsertBefore(val9, Instruction.Create(OpCodes.Ldc_I4_0)); val9.Operand = WithTrailingParameter(val10, module.TypeSystem.Boolean, "log"); patchSummary.CharacterMessageCalls++; } else if (((MemberReference)((MemberReference)val10).DeclaringType).FullName == "EffectList" && ((MemberReference)val10).Name == "Create" && val10.Parameters.Count == 5) { TypeReference val11 = new TypeReference(string.Empty, "ZDOID", module, ((MemberReference)val10).DeclaringType.Scope, true); FieldReference val12 = new FieldReference("None", val11, val11); iLProcessor2.InsertBefore(val9, Instruction.Create(OpCodes.Ldsfld, val12)); val9.Operand = WithTrailingParameter(val10, val11, "gamepadEffectsExclusiveToPlayer"); patchSummary.EffectCreateCalls++; } } } } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } RequireCount("direct legacy Inventory.AddItem calls", patchSummary.DirectAddItemCalls, 3); RequireCount("Inventory.IsTeleportable calls", patchSummary.IsTeleportableCalls, 1); RequireCount("VisEquipment.AttachArmor calls", patchSummary.AttachArmorCalls, 1); RequireCount("Character.Message calls", patchSummary.CharacterMessageCalls, 3); RequireCount("EffectList.Create calls", patchSummary.EffectCreateCalls, 1); return patchSummary; } private static IEnumerable GetTypeTree(TypeDefinition type) { yield return type; Enumerator enumerator = type.NestedTypes.GetEnumerator(); try { while (enumerator.MoveNext()) { TypeDefinition current = enumerator.Current; foreach (TypeDefinition item in GetTypeTree(current)) { yield return item; } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } private static MethodReference WithTrailingParameter(MethodReference legacyMethod, TypeReference trailingType, string trailingName) { //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) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown MethodReference val = new MethodReference(((MemberReference)legacyMethod).Name, legacyMethod.ReturnType, ((MemberReference)legacyMethod).DeclaringType) { HasThis = legacyMethod.HasThis, ExplicitThis = legacyMethod.ExplicitThis, CallingConvention = legacyMethod.CallingConvention }; Enumerator enumerator = legacyMethod.Parameters.GetEnumerator(); try { while (enumerator.MoveNext()) { ParameterDefinition current = enumerator.Current; val.Parameters.Add(new ParameterDefinition(((ParameterReference)current).ParameterType)); } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } val.Parameters.Add(new ParameterDefinition(trailingName, (ParameterAttributes)0, trailingType)); return val; } private static int DecodeInt32Constant(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: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 OpCode opCode = instruction.OpCode; Code code = ((OpCode)(ref opCode)).Code; if ((int)code != 26) { if ((int)code == 29) { return 7; } return 0; } return 4; } private static void RequireCount(string label, int actual, int expected) { if (actual != expected) { throw new InvalidOperationException("Safety stop: expected exactly " + expected + " " + label + ", found " + actual + "."); } } private static bool HasPathSegment(string path, string segment) { string text = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); char directorySeparatorChar = Path.DirectorySeparatorChar; string text2 = directorySeparatorChar.ToString(); directorySeparatorChar = Path.DirectorySeparatorChar; string value = text2 + segment + directorySeparatorChar; return text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0; } private static string ComputeSha256(string path) { using SHA256 sHA = SHA256.Create(); using FileStream inputStream = File.OpenRead(path); return BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", string.Empty); } }