Decompiled source of BackpacksValheim1Compat v1.0.3

patchers/ADARC-BackpacksValheim1Compat/ADARC.BackpacksValheim1Compat.Patcher.dll

Decompiled 2 hours ago
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<string> 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<TypeDefinition> list = ((IEnumerable<TypeDefinition>)module.Types).SelectMany(GetTypeTree).ToList();
		OpCode opCode;
		foreach (TypeDefinition item in list)
		{
			Enumerator<MethodDefinition> enumerator2 = item.Methods.GetEnumerator();
			try
			{
				while (enumerator2.MoveNext())
				{
					MethodDefinition current = enumerator2.Current;
					if (!current.HasBody)
					{
						continue;
					}
					Enumerator<Instruction> 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<CustomAttribute> 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<CustomAttributeArgument> 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<MethodDefinition>)(((IEnumerable<TypeDefinition>)list).SingleOrDefault((Func<TypeDefinition, bool>)((TypeDefinition t) => ((MemberReference)t).FullName == "ItemDataManager.ItemInfo")) ?? throw new InvalidOperationException("Safety stop: bundled ItemDataManager.ItemInfo type was not found.")).Methods).SingleOrDefault((Func<MethodDefinition, bool>)((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<Instruction> instructions = (IList<Instruction>)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<MethodDefinition> 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<ParameterDefinition>)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<TypeDefinition> GetTypeTree(TypeDefinition type)
	{
		yield return type;
		Enumerator<TypeDefinition> 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<ParameterDefinition> 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);
	}
}