Decompiled source of Valheim10Compatibility v1.4.1

patchers/Valheim10Compatibility.Patcher.dll

Decompiled 2 days 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.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using MonoMod.RuntimeDetour;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Valheim10Compatibility.Patcher")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("BepInEx preloader patcher providing binary backward compatibility for Valheim 1.0.")]
[assembly: AssemblyFileVersion("1.4.1.0")]
[assembly: AssemblyInformationalVersion("1.4.1+392146eab253da24bd329d86438263eeac9a4c7d")]
[assembly: AssemblyProduct("Valheim10Compatibility.Patcher")]
[assembly: AssemblyTitle("Valheim10Compatibility.Patcher")]
[assembly: AssemblyVersion("1.4.1.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 Valheim10Compatibility.Patcher
{
	internal sealed class AmbiguityGuard
	{
		private sealed class PatchInfo
		{
			public string Type;

			public string Method;

			public bool HasArgTypes;

			public int MethodType;

			public bool Any;
		}

		private static readonly string[] PatchMethodNames = new string[5] { "Prefix", "Postfix", "Transpiler", "Finalizer", "ILManipulator" };

		public static readonly string[] AliasedFieldNames = new string[1] { "m_availablePieces" };

		private readonly Dictionary<string, HashSet<string>> _byName = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);

		private readonly Dictionary<string, HashSet<string>> _who = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);

		private readonly Dictionary<string, Dictionary<string, string>> _fieldShape = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal);

		private readonly Dictionary<string, HashSet<string>> _reflectFatal = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);

		private readonly Dictionary<string, HashSet<string>> _reflectHooked = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);

		public bool Available { get; private set; }

		public int PluginsScanned { get; private set; }

		public int ByNameTargets { get; private set; }

		public int OldShapeFieldUsers { get; private set; }

		public string Root { get; private set; }

		public bool HookInstalled { get; set; }

		public static AmbiguityGuard Empty()
		{
			return new AmbiguityGuard
			{
				Available = false
			};
		}

		public static AmbiguityGuard Build(string pluginRoot, IEnumerable<string> extraSearchDirs, ManualLogSource log)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			AmbiguityGuard ambiguityGuard = new AmbiguityGuard
			{
				Root = pluginRoot
			};
			if (string.IsNullOrEmpty(pluginRoot) || !Directory.Exists(pluginRoot))
			{
				log.LogWarning((object)("Ambiguity guard: plugin folder not found ('" + pluginRoot + "'); overload bridges are NOT protected against by-name Harmony patches."));
				return ambiguityGuard;
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			DefaultAssemblyResolver val = new DefaultAssemblyResolver();
			foreach (string item in extraSearchDirs ?? Enumerable.Empty<string>())
			{
				if (!string.IsNullOrEmpty(item) && Directory.Exists(item))
				{
					((BaseAssemblyResolver)val).AddSearchDirectory(item);
				}
			}
			string[] array;
			try
			{
				array = (from f in Directory.GetFiles(pluginRoot, "*.dll", SearchOption.AllDirectories)
					where f.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
					select f).ToArray();
			}
			catch (Exception ex)
			{
				log.LogWarning((object)("Ambiguity guard: could not enumerate '" + pluginRoot + "': " + ex.Message));
				return ambiguityGuard;
			}
			int num = 0;
			string[] array2 = array;
			foreach (string path in array2)
			{
				try
				{
					((BaseAssemblyResolver)val).AddSearchDirectory(Path.GetDirectoryName(path));
					byte[] array3 = File.ReadAllBytes(path);
					AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly((Stream)new MemoryStream(array3), new ReaderParameters
					{
						ReadingMode = (ReadingMode)2,
						AssemblyResolver = (IAssemblyResolver)(object)val,
						ReadSymbols = false
					});
					try
					{
						ModuleDefinition mainModule = val2.MainModule;
						string fileName = Path.GetFileName(path);
						if (((IEnumerable<AssemblyNameReference>)mainModule.AssemblyReferences).Any((AssemblyNameReference r) => r.Name == "0Harmony"))
						{
							ambiguityGuard.PluginsScanned++;
							foreach (TypeDefinition type in mainModule.GetTypes())
							{
								num += ambiguityGuard.ScanType(type, fileName);
							}
						}
						string[] aliasedFieldNames = AliasedFieldNames;
						foreach (string text in aliasedFieldNames)
						{
							bool flag = ContainsBytes(array3, Encoding.UTF8.GetBytes(text));
							bool flag2 = ContainsBytes(array3, Encoding.Unicode.GetBytes(text));
							if (flag || flag2)
							{
								ambiguityGuard.ScanFieldUse(mainModule, fileName, text, flag2);
							}
						}
					}
					finally
					{
						((IDisposable)val2)?.Dispose();
					}
				}
				catch (Exception ex2)
				{
					num++;
					log.LogDebug((object)("Ambiguity guard: skipped '" + Path.GetFileName(path) + "': " + ex2.GetType().Name + ": " + ex2.Message));
				}
			}
			ambiguityGuard.Available = true;
			log.LogInfo((object)($"Ambiguity guard: scanned {ambiguityGuard.PluginsScanned} Harmony plugins under '{pluginRoot}' in {stopwatch.ElapsedMilliseconds} ms; {ambiguityGuard.ByNameTargets} by-name patch targets recorded; {ambiguityGuard.OldShapeFieldUsers} plugin(s) reference a pre-1.0 field shape this patcher aliases" + ((num > 0) ? $"; {num} attribute(s)/file(s) could not be parsed" : "") + "."));
			return ambiguityGuard;
		}

		private static bool ContainsBytes(byte[] hay, byte[] needle)
		{
			if (needle.Length == 0 || hay.Length < needle.Length)
			{
				return false;
			}
			byte b = needle[0];
			int num = hay.Length - needle.Length;
			for (int i = 0; i <= num; i++)
			{
				if (hay[i] == b)
				{
					int j;
					for (j = 1; j < needle.Length && hay[i + j] == needle[j]; j++)
					{
					}
					if (j == needle.Length)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static PatchInfo Merge(PatchInfo baseInfo, IEnumerable<CustomAttribute> attrs, out int unparsable)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			unparsable = 0;
			PatchInfo patchInfo = new PatchInfo
			{
				Type = baseInfo?.Type,
				Method = baseInfo?.Method,
				HasArgTypes = (baseInfo?.HasArgTypes ?? false),
				MethodType = (baseInfo?.MethodType ?? 0),
				Any = (baseInfo?.Any ?? false)
			};
			foreach (CustomAttribute attr in attrs)
			{
				if (((MemberReference)attr.AttributeType).Name != "HarmonyPatch")
				{
					continue;
				}
				patchInfo.Any = true;
				IList<CustomAttributeArgument> constructorArguments;
				try
				{
					constructorArguments = (IList<CustomAttributeArgument>)attr.ConstructorArguments;
				}
				catch (Exception)
				{
					unparsable++;
					continue;
				}
				List<string> list = new List<string>();
				foreach (CustomAttributeArgument item2 in constructorArguments)
				{
					CustomAttributeArgument current2 = item2;
					object value = ((CustomAttributeArgument)(ref current2)).Value;
					TypeReference val = (TypeReference)((value is TypeReference) ? value : null);
					if (val != null)
					{
						patchInfo.Type = ((MemberReference)val).FullName;
					}
					else if (((CustomAttributeArgument)(ref current2)).Value is string item)
					{
						list.Add(item);
					}
					else if (((MemberReference)((CustomAttributeArgument)(ref current2)).Type).FullName == "System.Type[]")
					{
						patchInfo.HasArgTypes = true;
					}
					else if (((MemberReference)((CustomAttributeArgument)(ref current2)).Type).Name == "MethodType")
					{
						try
						{
							patchInfo.MethodType = Convert.ToInt32(((CustomAttributeArgument)(ref current2)).Value);
						}
						catch (Exception)
						{
							unparsable++;
						}
					}
				}
				if (list.Count == 1)
				{
					patchInfo.Method = list[0];
				}
				else if (list.Count >= 2)
				{
					patchInfo.Type = NormalizeTypeName(list[0]);
					patchInfo.Method = list[1];
				}
			}
			return patchInfo;
		}

		private static string NormalizeTypeName(string harmonyTypeName)
		{
			if (string.IsNullOrEmpty(harmonyTypeName))
			{
				return harmonyTypeName;
			}
			int num = harmonyTypeName.IndexOf(',');
			if (num > 0)
			{
				harmonyTypeName = harmonyTypeName.Substring(0, num);
			}
			return harmonyTypeName.Trim().Replace('+', '/');
		}

		private int ScanType(TypeDefinition type, string plugin)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			int unparsable;
			PatchInfo patchInfo = Merge(null, (IEnumerable<CustomAttribute>)type.CustomAttributes, out unparsable);
			if (!patchInfo.Any && !((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((IEnumerable<CustomAttribute>)m.CustomAttributes).Any((CustomAttribute c) => ((MemberReference)c.AttributeType).Name == "HarmonyPatch")))
			{
				return unparsable;
			}
			if (((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == "TargetMethod" || ((MemberReference)m).Name == "TargetMethods"))
			{
				return unparsable;
			}
			Enumerator<MethodDefinition> enumerator = type.Methods.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					MethodDefinition current = enumerator.Current;
					bool num = ((IEnumerable<CustomAttribute>)current.CustomAttributes).Any((CustomAttribute c) => ((MemberReference)c.AttributeType).Name == "HarmonyPatch");
					bool flag = ((IEnumerable<CustomAttribute>)current.CustomAttributes).Any((CustomAttribute c) => ((MemberReference)c.AttributeType).Name.StartsWith("Harmony", StringComparison.Ordinal) && ((MemberReference)c.AttributeType).Name != "HarmonyPatch") || PatchMethodNames.Contains(((MemberReference)current).Name);
					if (!num && !(patchInfo.Any && flag))
					{
						continue;
					}
					int unparsable2;
					PatchInfo patchInfo2 = Merge(patchInfo, (IEnumerable<CustomAttribute>)current.CustomAttributes, out unparsable2);
					unparsable += unparsable2;
					if (patchInfo2.Type == null || patchInfo2.HasArgTypes)
					{
						continue;
					}
					int methodType = patchInfo2.MethodType;
					string text;
					if (methodType != 0)
					{
						if (methodType != 3)
						{
							continue;
						}
						text = ".ctor";
					}
					else
					{
						text = patchInfo2.Method;
					}
					if (!string.IsNullOrEmpty(text))
					{
						Record(patchInfo2.Type, text, plugin);
					}
				}
				return unparsable;
			}
			finally
			{
				((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private void ScanFieldUse(ModuleDefinition module, string plugin, string fieldName, bool checkLiterals)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			string name = ((AssemblyNameReference)module.Assembly.Name).Name;
			bool flag = false;
			foreach (TypeDefinition type in module.GetTypes())
			{
				Enumerator<MethodDefinition> enumerator2 = type.Methods.GetEnumerator();
				try
				{
					while (enumerator2.MoveNext())
					{
						MethodDefinition current2 = enumerator2.Current;
						if (!current2.HasBody)
						{
							continue;
						}
						bool flag2 = false;
						List<MethodReference> list = new List<MethodReference>();
						Enumerator<Instruction> enumerator3 = current2.Body.Instructions.GetEnumerator();
						try
						{
							while (enumerator3.MoveNext())
							{
								Instruction current3 = enumerator3.Current;
								object operand = current3.Operand;
								FieldReference val = (FieldReference)((operand is FieldReference) ? operand : null);
								if (val != null && ((MemberReference)val).Name == fieldName)
								{
									string key = ((MemberReference)((MemberReference)val).DeclaringType).FullName.Replace('/', '+') + "::" + ((MemberReference)val).Name;
									if (!_fieldShape.TryGetValue(key, out var value))
									{
										value = (_fieldShape[key] = new Dictionary<string, string>(StringComparer.Ordinal));
									}
									if (!value.ContainsKey(name))
									{
										value[name] = ((MemberReference)val.FieldType).Name;
										flag = true;
									}
								}
								else
								{
									if (!checkLiterals)
									{
										continue;
									}
									if (current3.OpCode == OpCodes.Ldstr && current3.Operand as string == fieldName)
									{
										flag2 = true;
										continue;
									}
									object operand2 = current3.Operand;
									MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null);
									if (val2 != null && (current3.OpCode == OpCodes.Call || current3.OpCode == OpCodes.Callvirt))
									{
										list.Add(val2);
									}
								}
							}
						}
						finally
						{
							((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose();
						}
						if (flag2)
						{
							bool flag3 = list.Any((MethodReference c) => ((MemberReference)((MemberReference)c).DeclaringType).FullName == "HarmonyLib.AccessTools" && (((MemberReference)c).Name == "Field" || ((MemberReference)c).Name == "DeclaredField"));
							Add((list.Any((MethodReference c) => (((MemberReference)((MemberReference)c).DeclaringType).FullName == "System.Type" && ((MemberReference)c).Name == "GetField") || (((MemberReference)((MemberReference)c).DeclaringType).FullName == "HarmonyLib.Traverse" && ((MemberReference)c).Name == "Field") || (((MemberReference)((MemberReference)c).DeclaringType).FullName == "HarmonyLib.AccessTools" && ((MemberReference)c).Name == "FieldRefAccess")) || !flag3) ? _reflectFatal : _reflectHooked, fieldName, plugin + " (" + ((MemberReference)type).Name + "." + ((MemberReference)current2).Name + ")");
						}
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
			}
			if (flag)
			{
				OldShapeFieldUsers++;
			}
		}

		private static void Add(Dictionary<string, HashSet<string>> map, string key, string value)
		{
			if (!map.TryGetValue(key, out var value2))
			{
				value2 = (map[key] = new HashSet<string>(StringComparer.OrdinalIgnoreCase));
			}
			value2.Add(value);
		}

		private void Record(string type, string method, string plugin)
		{
			if (!_byName.TryGetValue(type, out var value))
			{
				value = (_byName[type] = new HashSet<string>(StringComparer.Ordinal));
			}
			if (value.Add(method))
			{
				ByNameTargets++;
			}
			string key = type + "::" + method;
			if (!_who.TryGetValue(key, out var value2))
			{
				value2 = (_who[key] = new HashSet<string>(StringComparer.OrdinalIgnoreCase));
			}
			value2.Add(plugin);
		}

		private bool PatchedByName(string typeFullName, string method)
		{
			if (_byName.TryGetValue(typeFullName, out var value))
			{
				return value.Contains(method);
			}
			return false;
		}

		public bool WouldBreak(TypeDefinition type, string methodName, out string offenders)
		{
			offenders = null;
			if (!Available)
			{
				return false;
			}
			if (((IEnumerable<MethodDefinition>)type.Methods).Count((MethodDefinition m) => ((MemberReference)m).Name == methodName) != 1)
			{
				return false;
			}
			List<string> list = new List<string>();
			if (PatchedByName(((MemberReference)type).FullName, methodName))
			{
				list.AddRange(_who[((MemberReference)type).FullName + "::" + methodName]);
			}
			foreach (TypeDefinition type2 in ((MemberReference)type).Module.GetTypes())
			{
				if (type2 != type && DerivesFrom(type2, type) && !((IEnumerable<MethodDefinition>)type2.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == methodName) && PatchedByName(((MemberReference)type2).FullName, methodName))
				{
					list.AddRange(_who[((MemberReference)type2).FullName + "::" + methodName]);
				}
			}
			if (list.Count == 0)
			{
				return false;
			}
			offenders = string.Join(", ", (from s in list.Distinct<string>(StringComparer.OrdinalIgnoreCase)
				orderby s
				select s).ToArray());
			return true;
		}

		public bool FieldAliasWouldBreak(string fieldName, out string offenders)
		{
			offenders = null;
			if (!Available)
			{
				return false;
			}
			List<string> list = new List<string>();
			if (_reflectFatal.TryGetValue(fieldName, out var value))
			{
				list.AddRange(value);
			}
			if (!HookInstalled && _reflectHooked.TryGetValue(fieldName, out value))
			{
				list.AddRange(value);
			}
			if (list.Count == 0)
			{
				return false;
			}
			offenders = string.Join(", ", (from s in list.Distinct<string>(StringComparer.OrdinalIgnoreCase)
				orderby s
				select s).ToArray());
			return true;
		}

		public string OldFieldShapeFor(string fieldKey, string assemblyName)
		{
			if (assemblyName == null)
			{
				return null;
			}
			if (!_fieldShape.TryGetValue(fieldKey, out var value) || !value.TryGetValue(assemblyName, out var value2))
			{
				return null;
			}
			return value2;
		}

		private static bool DerivesFrom(TypeDefinition sub, TypeDefinition baseType)
		{
			TypeReference baseType2 = sub.BaseType;
			int num = 0;
			while (baseType2 != null && num++ < 32)
			{
				if (((MemberReference)baseType2).FullName == ((MemberReference)baseType).FullName)
				{
					return true;
				}
				TypeDefinition val;
				try
				{
					val = baseType2.Resolve();
				}
				catch (Exception)
				{
					return false;
				}
				if (val == null)
				{
					return false;
				}
				baseType2 = val.BaseType;
			}
			return false;
		}
	}
	internal static class HarmonyResolver
	{
		private delegate MethodInfo orig_Method(Type type, string name, Type[] parameters, Type[] generics);

		private delegate FieldInfo orig_Field(Type type, string name);

		private delegate void orig_PatchProcessorCtor(PatchProcessor self, Harmony instance, MethodBase original);

		public const string BridgeTag = "Valheim10Compatibility bridge";

		private const string TargetSeparator = " -> ";

		private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty;

		private const BindingFlags AllDeclared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty;

		private static ManualLogSource _log;

		private static AmbiguityGuard _guard;

		private static readonly List<Hook> Hooks = new List<Hook>();

		private static readonly HashSet<string> Logged = new HashSet<string>(StringComparer.Ordinal);

		private static readonly Dictionary<MemberInfo, bool> BridgeCache = new Dictionary<MemberInfo, bool>();

		private static readonly object Lock = new object();

		private static readonly HashSet<string> PatchingTypes = new HashSet<string>(StringComparer.Ordinal) { "HarmonyLib.PatchClassProcessor", "HarmonyLib.PatchProcessor", "HarmonyLib.Harmony", "HarmonyLib.PatchTools", "HarmonyLib.HarmonyMethod", "HarmonyLib.HarmonyMethodExtensions" };

		public static bool Installed { get; private set; }

		public static string Status { get; private set; } = "not installed";

		public static int Resolutions { get; private set; }

		public static bool Install(ManualLogSource log, AmbiguityGuard guard)
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			_log = log;
			_guard = guard;
			if (Installed)
			{
				return true;
			}
			try
			{
				Type typeFromHandle = typeof(AccessTools);
				Type[] sig = new Type[4]
				{
					typeof(Type),
					typeof(string),
					typeof(Type[]),
					typeof(Type[])
				};
				Type[] sig2 = new Type[2]
				{
					typeof(Type),
					typeof(string)
				};
				Add(typeFromHandle, "DeclaredMethod", sig, "DeclaredMethodHook");
				Add(typeFromHandle, "Method", sig, "MethodHook");
				Add(typeFromHandle, "DeclaredField", sig2, "DeclaredFieldHook");
				Add(typeFromHandle, "Field", sig2, "FieldHook");
				Installed = true;
				string arg;
				try
				{
					ConstructorInfo constructor = typeof(PatchProcessor).GetConstructor(new Type[2]
					{
						typeof(Harmony),
						typeof(MethodBase)
					});
					if (constructor == null)
					{
						throw new MissingMethodException("HarmonyLib.PatchProcessor", ".ctor(Harmony, MethodBase)");
					}
					Hooks.Add(new Hook((MethodBase)constructor, typeof(HarmonyResolver).GetMethod("PatchProcessorCtorHook", BindingFlags.Static | BindingFlags.NonPublic)));
					arg = "PatchProcessor..ctor";
				}
				catch (Exception ex)
				{
					arg = "PatchProcessor..ctor NOT hooked, " + ex.GetType().Name + ": " + ex.Message;
				}
				Status = $"installed on HarmonyX {typeFromHandle.Assembly.GetName().Version} (AccessTools.DeclaredMethod, Method, DeclaredField, Field; {arg})";
				return true;
			}
			catch (Exception ex2)
			{
				foreach (Hook hook in Hooks)
				{
					try
					{
						hook.Dispose();
					}
					catch (Exception)
					{
					}
				}
				Hooks.Clear();
				Installed = false;
				Status = "NOT installed (" + ex2.GetType().Name + ": " + ex2.Message + ")";
				return false;
			}
		}

		private static void Add(Type at, string name, Type[] sig, string hookName)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			MethodInfo method = at.GetMethod(name, BindingFlags.Static | BindingFlags.Public, null, sig, null);
			if (method == null)
			{
				throw new MissingMethodException("HarmonyLib.AccessTools", name);
			}
			MethodInfo method2 = typeof(HarmonyResolver).GetMethod(hookName, BindingFlags.Static | BindingFlags.NonPublic);
			Hooks.Add(new Hook((MethodBase)method, method2));
		}

		private static MethodInfo DeclaredMethodHook(orig_Method orig, Type type, string name, Type[] parameters, Type[] generics)
		{
			if (type == null || name == null)
			{
				return orig(type, name, parameters, generics);
			}
			if (parameters != null)
			{
				return Redirect(orig(type, name, parameters, generics), type, name, "AccessTools.DeclaredMethod");
			}
			try
			{
				return orig(type, name, parameters, generics);
			}
			catch (AmbiguousMatchException)
			{
				string how;
				MethodInfo methodInfo = Disambiguate(Named(type, name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty), type, allowParameterless: false, out how);
				if (methodInfo == null)
				{
					throw;
				}
				Report("AccessTools.DeclaredMethod", type, name, Describe(methodInfo), how);
				return (generics != null) ? methodInfo.MakeGenericMethod(generics) : methodInfo;
			}
		}

		private static MethodInfo MethodHook(orig_Method orig, Type type, string name, Type[] parameters, Type[] generics)
		{
			if (type == null || name == null)
			{
				return orig(type, name, parameters, generics);
			}
			if (parameters != null)
			{
				return Redirect(orig(type, name, parameters, generics), type, name, "AccessTools.Method");
			}
			Type type2 = type;
			while (type2 != null)
			{
				MethodInfo[] array = Named(type2, name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty);
				if (array.Length != 0)
				{
					string how = null;
					MethodInfo methodInfo;
					if (array.Length == 1)
					{
						methodInfo = array[0];
					}
					else
					{
						methodInfo = Disambiguate(array, type2, allowParameterless: true, out how);
						if (methodInfo == null)
						{
							throw new AmbiguousMatchException($"Ambiguous match in Harmony patch for {type}:{name}");
						}
						if (how != null)
						{
							Report("AccessTools.Method", type, name, Describe(methodInfo), how);
						}
					}
					if (generics == null)
					{
						return methodInfo;
					}
					return methodInfo.MakeGenericMethod(generics);
				}
				type2 = type2.BaseType;
			}
			return orig(type, name, parameters, generics);
		}

		private static MethodInfo[] Named(Type t, string name, BindingFlags flags)
		{
			return (from m in t.GetMethods(flags)
				where m.Name == name
				select m).ToArray();
		}

		private static MethodInfo Disambiguate(MethodInfo[] named, Type t, bool allowParameterless, out string how)
		{
			how = null;
			MethodInfo[] array = named.Where((MethodInfo m) => !IsBridge(m)).ToArray();
			int num = named.Length - array.Length;
			string text = ((num > 0) ? $"{num} bridge overload(s) excluded" : null);
			if (array.Length == 0)
			{
				return null;
			}
			if (array.Length == 1)
			{
				how = text;
				return array[0];
			}
			if (array.Select(Sig).Distinct().Count() == 1)
			{
				how = text;
				return MostDerived(array, t);
			}
			MethodInfo methodInfo = PreferredOverloads.Pick(array);
			if (methodInfo != null)
			{
				how = "vanilla added an overload in 1.0; the pre-1.0 shape is taken" + ((text != null) ? (", " + text) : "");
				return methodInfo;
			}
			if (allowParameterless)
			{
				MethodInfo[] array2 = array.Where((MethodInfo m) => m.GetParameters().Length == 0).ToArray();
				if (array2.Length == 1)
				{
					how = "HarmonyX's own parameterless fallback" + ((text != null) ? (", " + text) : "");
					return array2[0];
				}
			}
			return null;
		}

		private static MethodInfo MostDerived(MethodInfo[] natives, Type t)
		{
			while (t != null)
			{
				MethodInfo methodInfo = natives.FirstOrDefault((MethodInfo n) => n.DeclaringType == t);
				if (methodInfo != null)
				{
					return methodInfo;
				}
				t = t.BaseType;
			}
			return natives[0];
		}

		private static MethodInfo Redirect(MethodInfo found, Type type, string name, string api)
		{
			if (found == null || !IsBridge(found))
			{
				return found;
			}
			if (!HarmonyIsPatching())
			{
				Report(api, type, name + "(" + SimpleParams(found) + ")", Describe(found), "typed lookup outside Harmony's patch machinery (a reflective call, delegate or Traverse); the bridge it named is returned unchanged so MethodInfo.Invoke keeps the pre-1.0 arity - a later Harmony.Patch on it is redirected at patch time");
				return found;
			}
			MethodInfo methodInfo = NativeFor(found);
			if (methodInfo != null)
			{
				Report(api, type, name + "(" + SimpleParams(found) + ")", Describe(methodInfo), "typed lookup matched a compatibility bridge that only appends defaulted parameters; redirected to the native method it forwards to, so the patch fires on vanilla's own calls");
				return methodInfo;
			}
			Report(api, type, name + "(" + SimpleParams(found) + ")", Describe(found), "typed lookup matched a compatibility bridge that TRANSFORMS its arguments; the patch stays on the bridge. Vanilla never calls a bridge, so this patch only fires when another pre-1.0 plugin calls the old shape", null, warn: true);
			return found;
		}

		private static MethodInfo NativeFor(MethodInfo bridge)
		{
			string text = BridgeTarget(bridge);
			MethodInfo methodInfo = ((text == null) ? null : FindBySimpleSig(bridge.DeclaringType, text));
			if (!(methodInfo != null) || IsBridge(methodInfo) || !LeadingParametersMatch(bridge, methodInfo))
			{
				return null;
			}
			return methodInfo;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool HarmonyIsPatching()
		{
			try
			{
				Assembly assembly = typeof(AccessTools).Assembly;
				StackTrace stackTrace = new StackTrace(fNeedFileInfo: false);
				for (int i = 0; i < stackTrace.FrameCount; i++)
				{
					Type type = stackTrace.GetFrame(i)?.GetMethod()?.DeclaringType;
					if (!(type == null) && !(type.Assembly != assembly))
					{
						while (type.DeclaringType != null)
						{
							type = type.DeclaringType;
						}
						string text = type.FullName ?? string.Empty;
						if (PatchingTypes.Contains(text))
						{
							return true;
						}
						if (text == "HarmonyLib.Traverse" || text == "HarmonyLib.AccessCache")
						{
							return false;
						}
					}
				}
			}
			catch (Exception)
			{
			}
			return false;
		}

		private static void PatchProcessorCtorHook(orig_PatchProcessorCtor orig, PatchProcessor self, Harmony instance, MethodBase original)
		{
			MethodInfo methodInfo = original as MethodInfo;
			if (methodInfo != null && IsBridge(methodInfo))
			{
				MethodInfo methodInfo2 = NativeFor(methodInfo);
				if (methodInfo2 != null)
				{
					Report("PatchProcessor", methodInfo.DeclaringType, methodInfo.Name + "(" + SimpleParams(methodInfo) + ")", Describe(methodInfo2), "Harmony.Patch on a compatibility bridge redirected to the native method it forwards to, so the patch fires on vanilla's own calls");
					original = methodInfo2;
				}
			}
			orig(self, instance, original);
		}

		private static bool LeadingParametersMatch(MethodInfo bridge, MethodInfo native)
		{
			if (bridge.IsStatic != native.IsStatic || bridge.ReturnType != native.ReturnType)
			{
				return false;
			}
			ParameterInfo[] parameters = bridge.GetParameters();
			ParameterInfo[] parameters2 = native.GetParameters();
			if (parameters2.Length < parameters.Length)
			{
				return false;
			}
			for (int i = 0; i < parameters.Length; i++)
			{
				if (parameters[i].ParameterType != parameters2[i].ParameterType)
				{
					return false;
				}
			}
			return true;
		}

		private static MethodInfo FindBySimpleSig(Type declaring, string sig)
		{
			int num = sig.IndexOf('(');
			if (num < 0 || !sig.EndsWith(")"))
			{
				return null;
			}
			string name = sig.Substring(0, num);
			string text = sig.Substring(num + 1, sig.Length - num - 2);
			string[] want = ((text.Length == 0) ? new string[0] : text.Split(new char[1] { ',' }));
			MethodInfo[] array = (from m in declaring.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty)
				where m.Name == name && (from p in m.GetParameters()
					select p.ParameterType.Name).SequenceEqual(want)
				select m).ToArray();
			if (array.Length != 1)
			{
				return null;
			}
			return array[0];
		}

		private static FieldInfo DeclaredFieldHook(orig_Field orig, Type type, string name)
		{
			if (type == null || name == null)
			{
				return orig(type, name);
			}
			try
			{
				return orig(type, name);
			}
			catch (AmbiguousMatchException)
			{
				FieldInfo fieldInfo = PickField((from f in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty)
					where f.Name == name
					select f).ToArray(), type, name, "AccessTools.DeclaredField");
				if (fieldInfo == null)
				{
					throw;
				}
				return fieldInfo;
			}
		}

		private static FieldInfo FieldHook(orig_Field orig, Type type, string name)
		{
			if (type == null || name == null)
			{
				return orig(type, name);
			}
			try
			{
				return orig(type, name);
			}
			catch (AmbiguousMatchException)
			{
				Type type2 = type;
				while (type2 != null)
				{
					FieldInfo[] array = (from f in type2.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty)
						where f.Name == name
						select f).ToArray();
					if (array.Length != 0)
					{
						FieldInfo fieldInfo = PickField(array, type, name, "AccessTools.Field");
						if (fieldInfo == null)
						{
							throw;
						}
						return fieldInfo;
					}
					type2 = type2.BaseType;
				}
				throw;
			}
		}

		private static FieldInfo PickField(FieldInfo[] named, Type type, string name, string api)
		{
			FieldInfo[] array = named.Where((FieldInfo f) => !IsBridge(f)).ToArray();
			FieldInfo[] array2 = named.Where(IsBridge).ToArray();
			if (array.Length != 1 || array2.Length == 0)
			{
				return null;
			}
			string text = CallerAssembly();
			string oldShape = _guard?.OldFieldShapeFor(type.FullName + "::" + name, text);
			if (oldShape != null)
			{
				FieldInfo fieldInfo = array2.FirstOrDefault((FieldInfo a) => a.FieldType.Name == oldShape) ?? array2[0];
				Report(api, type, name, Describe(fieldInfo), text + " references the pre-1.0 " + oldShape + " field in its IL, so the compatibility alias is returned", text);
				return fieldInfo;
			}
			Report(api, type, name, Describe(array[0]), (text ?? "the caller") + " does not reference the pre-1.0 shape in its IL, so the native field is returned", text);
			return array[0];
		}

		public static bool IsBridge(MemberInfo m)
		{
			if (m == null)
			{
				return false;
			}
			lock (Lock)
			{
				if (BridgeCache.TryGetValue(m, out var value))
				{
					return value;
				}
				bool flag = false;
				try
				{
					object[] customAttributes = m.GetCustomAttributes(typeof(ObsoleteAttribute), inherit: false);
					for (int i = 0; i < customAttributes.Length; i++)
					{
						if (customAttributes[i] is ObsoleteAttribute { Message: not null } obsoleteAttribute && obsoleteAttribute.Message.StartsWith("Valheim10Compatibility bridge", StringComparison.Ordinal))
						{
							flag = true;
							break;
						}
					}
				}
				catch (Exception)
				{
				}
				BridgeCache[m] = flag;
				return flag;
			}
		}

		private static string BridgeTarget(MemberInfo m)
		{
			try
			{
				object[] customAttributes = m.GetCustomAttributes(typeof(ObsoleteAttribute), inherit: false);
				for (int i = 0; i < customAttributes.Length; i++)
				{
					if (customAttributes[i] is ObsoleteAttribute { Message: not null } obsoleteAttribute)
					{
						int num = obsoleteAttribute.Message.IndexOf(" -> ", StringComparison.Ordinal);
						if (num >= 0)
						{
							return obsoleteAttribute.Message.Substring(num + " -> ".Length).Trim();
						}
					}
				}
			}
			catch (Exception)
			{
			}
			return null;
		}

		private static string Sig(MethodInfo m)
		{
			return string.Join(",", (from p in m.GetParameters()
				select p.ParameterType.ToString()).ToArray());
		}

		private static string SimpleParams(MethodBase m)
		{
			return string.Join(", ", (from p in m.GetParameters()
				select p.ParameterType.Name).ToArray());
		}

		private static string Describe(MethodInfo m)
		{
			return m.ReturnType.Name + " " + m.DeclaringType?.Name + "." + m.Name + "(" + SimpleParams(m) + ")";
		}

		private static string Describe(FieldInfo f)
		{
			return f.FieldType.Name + " " + f.DeclaringType?.Name + "." + f.Name;
		}

		private static string CallerAssembly()
		{
			try
			{
				StackTrace stackTrace = new StackTrace(fNeedFileInfo: false);
				for (int i = 0; i < stackTrace.FrameCount; i++)
				{
					MethodBase methodBase = stackTrace.GetFrame(i)?.GetMethod();
					if (methodBase == null || methodBase.DeclaringType == null)
					{
						continue;
					}
					string name = methodBase.DeclaringType.Assembly.GetName().Name;
					switch (name)
					{
					case "0Harmony":
					case "Valheim10Compatibility.Patcher":
					case "mscorlib":
					case "netstandard":
						continue;
					}
					if (!name.StartsWith("MonoMod", StringComparison.Ordinal) && !name.StartsWith("System", StringComparison.Ordinal) && !name.StartsWith("BepInEx", StringComparison.Ordinal))
					{
						return name;
					}
				}
			}
			catch (Exception)
			{
			}
			return null;
		}

		private static void Report(string api, Type type, string name, string picked, string how, string caller = null, bool warn = false)
		{
			if (caller == null)
			{
				caller = CallerAssembly();
			}
			string item = api + "|" + type.FullName + "|" + name + "|" + picked + "|" + caller;
			lock (Lock)
			{
				if (!Logged.Add(item))
				{
					return;
				}
				Resolutions++;
			}
			string text = "Harmony hook: " + api + "(" + type.FullName + ", \"" + name + "\") -> " + picked + "  [" + how + "]" + ((caller != null) ? ("  <- " + caller) : "");
			if (warn)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)text);
				}
			}
			else
			{
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogInfo((object)text);
				}
			}
		}
	}
	internal enum ArgKind
	{
		FromParameter,
		ConstI4,
		ConstNull,
		DefaultOf,
		Vector2sFromV2i,
		ZonePosOfV2i,
		SimulationDist,
		ThisField,
		OutLocal
	}
	internal struct Arg
	{
		public ArgKind Kind;

		public int Index;

		public int Index2;

		public int Value;

		public TypeReference TypeRef;

		public FieldReference Field;

		public bool NarrowToI2;

		public static Arg P(int i)
		{
			return new Arg
			{
				Kind = ArgKind.FromParameter,
				Index = i
			};
		}

		public static Arg I4(int v)
		{
			return new Arg
			{
				Kind = ArgKind.ConstI4,
				Value = v
			};
		}

		public static Arg I2(int v)
		{
			return new Arg
			{
				Kind = ArgKind.ConstI4,
				Value = v,
				NarrowToI2 = true
			};
		}

		public static Arg False()
		{
			return I4(0);
		}

		public static Arg True()
		{
			return I4(1);
		}

		public static Arg Null()
		{
			return new Arg
			{
				Kind = ArgKind.ConstNull
			};
		}

		public static Arg Default(TypeReference t)
		{
			return new Arg
			{
				Kind = ArgKind.DefaultOf,
				TypeRef = t
			};
		}

		public static Arg V2s(int i)
		{
			return new Arg
			{
				Kind = ArgKind.Vector2sFromV2i,
				Index = i
			};
		}

		public static Arg ZonePos(int i)
		{
			return new Arg
			{
				Kind = ArgKind.ZonePosOfV2i,
				Index = i
			};
		}

		public static Arg SimDist(int near, int far)
		{
			return new Arg
			{
				Kind = ArgKind.SimulationDist,
				Index = near,
				Index2 = far
			};
		}

		public static Arg Fld(FieldReference f)
		{
			return new Arg
			{
				Kind = ArgKind.ThisField,
				Field = f
			};
		}

		public static Arg Out(TypeReference t)
		{
			return new Arg
			{
				Kind = ArgKind.OutLocal,
				TypeRef = t
			};
		}
	}
	internal sealed class Injector
	{
		private readonly ModuleDefinition _module;

		private readonly ManualLogSource _log;

		private readonly AmbiguityGuard _guard;

		public readonly List<string> SkipDetails = new List<string>();

		public readonly List<string> BlockDetails = new List<string>();

		public readonly List<string> AdvisoryDetails = new List<string>();

		public TypeReference Vector2i;

		public TypeReference Vector2s;

		public MethodReference Vector2sCtor;

		public FieldReference V2iX;

		public FieldReference V2iY;

		public MethodReference GetZonePosV2s;

		public MethodReference SimulationDistanceCtor;

		public int Injected { get; private set; }

		public int Skipped { get; private set; }

		public int Blocked { get; private set; }

		public int BlockedReturnTypeOnly { get; private set; }

		public int AlreadyPresent { get; private set; }

		public int Advisory { get; private set; }

		public Injector(ModuleDefinition module, ManualLogSource log, AmbiguityGuard guard)
		{
			_module = module;
			_log = log;
			_guard = guard;
		}

		private static bool SameShape(MethodDefinition m, string name, IList<TypeReference> paramTypes)
		{
			if (((MemberReference)m).Name != name || ((MethodReference)m).Parameters.Count != paramTypes.Count)
			{
				return false;
			}
			for (int i = 0; i < paramTypes.Count; i++)
			{
				if (paramTypes[i] != null && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[i]).ParameterType).FullName != ((MemberReference)paramTypes[i]).FullName)
				{
					return false;
				}
			}
			return true;
		}

		private bool RefusedByGuard(TypeDefinition type, string name, string label)
		{
			if (!_guard.WouldBreak(type, name, out var offenders))
			{
				return false;
			}
			if (HarmonyResolver.Installed)
			{
				Advisory++;
				AdvisoryDetails.Add(label + " (by-name patch in " + offenders + ")");
				_log.LogInfo((object)("  by-name Harmony patch on " + ((MemberReference)type).Name + "." + name + " in " + offenders + ": the resolution hook steers it to the native overload, so this bridge is injected anyway."));
				return false;
			}
			Blocked++;
			BlockDetails.Add(label + " (would break: " + offenders + ")");
			_log.LogWarning((object)("BLOCKED " + label + ": adding this overload would make the by-name Harmony patch in " + offenders + " ambiguous, which kills that plugin at PatchAll(). Left unbridged deliberately (the Harmony resolution hook is not installed)."));
			return true;
		}

		public bool Forward(TypeDefinition type, string name, MethodDefinition target, IList<Tuple<string, TypeReference>> bridgeParams, IList<Arg> plan, string label, bool isStatic = false, bool virtualCall = false, bool isConstructor = false)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Expected O, but got Unknown
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			if (target == null)
			{
				Skipped++;
				SkipDetails.Add(label);
				_log.LogWarning((object)("SKIPPED " + label + ": no matching target on this build - plugins calling it stay broken."));
				return false;
			}
			List<TypeReference> wanted = bridgeParams.Select((Tuple<string, TypeReference> p) => p.Item2).ToList();
			if (((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => SameShape(m, name, wanted)))
			{
				AlreadyPresent++;
				_log.LogInfo((object)("present natively, no bridge needed: " + label));
				return false;
			}
			if (!isConstructor && RefusedByGuard(type, name, label))
			{
				return false;
			}
			MethodAttributes val = (MethodAttributes)134;
			if (isStatic)
			{
				val = (MethodAttributes)(val | 0x10);
			}
			if (isConstructor)
			{
				val = (MethodAttributes)(val | 0x1800);
			}
			MethodDefinition val2 = new MethodDefinition(name, val, isConstructor ? _module.TypeSystem.Void : _module.ImportReference(((MethodReference)target).ReturnType));
			foreach (Tuple<string, TypeReference> bridgeParam in bridgeParams)
			{
				((MethodReference)val2).Parameters.Add(new ParameterDefinition(bridgeParam.Item1, (ParameterAttributes)0, _module.ImportReference(bridgeParam.Item2)));
			}
			ILProcessor iLProcessor = val2.Body.GetILProcessor();
			if (!isStatic)
			{
				iLProcessor.Emit(OpCodes.Ldarg_0);
			}
			foreach (Arg item in plan)
			{
				if (!EmitArg(iLProcessor, val2, item))
				{
					Skipped++;
					SkipDetails.Add(label);
					_log.LogWarning((object)("SKIPPED " + label + ": a required helper (Vector2s / GetZonePos / SimulationDistance) is unavailable on this build."));
					return false;
				}
			}
			iLProcessor.Emit((virtualCall && !target.IsStatic) ? OpCodes.Callvirt : OpCodes.Call, _module.ImportReference((MethodReference)(object)target));
			iLProcessor.Emit(OpCodes.Ret);
			type.Methods.Add(val2);
			Tag(val2, target);
			Injected++;
			_log.LogInfo((object)("Injected " + label));
			return true;
		}

		private bool EmitArg(ILProcessor il, MethodDefinition bridge, Arg a)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_0099: Expected O, but got Unknown
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: 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_0103: Expected O, but got Unknown
			//IL_0121: 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_01aa: 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_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			switch (a.Kind)
			{
			case ArgKind.FromParameter:
				il.Emit(OpCodes.Ldarg_S, ((MethodReference)bridge).Parameters[a.Index]);
				return true;
			case ArgKind.ConstI4:
				EmitI4(il, a.Value);
				if (a.NarrowToI2)
				{
					il.Emit(OpCodes.Conv_I2);
				}
				return true;
			case ArgKind.ConstNull:
				il.Emit(OpCodes.Ldnull);
				return true;
			case ArgKind.DefaultOf:
			{
				VariableDefinition val2 = new VariableDefinition(_module.ImportReference(a.TypeRef));
				bridge.Body.Variables.Add(val2);
				bridge.Body.InitLocals = true;
				il.Emit(OpCodes.Ldloca_S, val2);
				il.Emit(OpCodes.Initobj, _module.ImportReference(a.TypeRef));
				il.Emit(OpCodes.Ldloc_S, val2);
				return true;
			}
			case ArgKind.OutLocal:
			{
				VariableDefinition val = new VariableDefinition(_module.ImportReference(a.TypeRef));
				bridge.Body.Variables.Add(val);
				bridge.Body.InitLocals = true;
				il.Emit(OpCodes.Ldloca_S, val);
				return true;
			}
			case ArgKind.Vector2sFromV2i:
				if (Vector2sCtor == null || V2iX == null)
				{
					return false;
				}
				EmitVector2s(il, ((MethodReference)bridge).Parameters[a.Index]);
				return true;
			case ArgKind.ZonePosOfV2i:
				if (Vector2sCtor == null || V2iX == null || GetZonePosV2s == null)
				{
					return false;
				}
				EmitVector2s(il, ((MethodReference)bridge).Parameters[a.Index]);
				il.Emit(OpCodes.Call, GetZonePosV2s);
				return true;
			case ArgKind.SimulationDist:
				if (SimulationDistanceCtor == null)
				{
					return false;
				}
				il.Emit(OpCodes.Ldarg_S, ((MethodReference)bridge).Parameters[a.Index]);
				il.Emit(OpCodes.Ldarg_S, ((MethodReference)bridge).Parameters[a.Index2]);
				il.Emit(OpCodes.Ldc_I4_1);
				il.Emit(OpCodes.Newobj, SimulationDistanceCtor);
				return true;
			case ArgKind.ThisField:
				il.Emit(OpCodes.Ldarg_0);
				il.Emit(OpCodes.Ldfld, a.Field);
				return true;
			default:
				return false;
			}
		}

		private void EmitVector2s(ILProcessor il, ParameterDefinition p)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			il.Emit(OpCodes.Ldarga_S, p);
			il.Emit(OpCodes.Ldfld, V2iX);
			il.Emit(OpCodes.Conv_I2);
			il.Emit(OpCodes.Ldarga_S, p);
			il.Emit(OpCodes.Ldfld, V2iY);
			il.Emit(OpCodes.Conv_I2);
			il.Emit(OpCodes.Newobj, Vector2sCtor);
		}

		private static void EmitI4(ILProcessor il, int v)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (v == -1)
			{
				il.Emit(OpCodes.Ldc_I4_M1);
			}
			else
			{
				il.Emit(OpCodes.Ldc_I4, v);
			}
		}

		public MethodDefinition Find(TypeDefinition type, string name, Func<MethodDefinition, bool> pred)
		{
			if (type != null)
			{
				return ((IEnumerable<MethodDefinition>)type.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == name && pred(m)));
			}
			return null;
		}

		public bool Has(TypeDefinition type, string name, int paramCount, string firstParamFullName = null)
		{
			if (type != null)
			{
				return ((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == name && ((MethodReference)m).Parameters.Count == paramCount && (firstParamFullName == null || (((MethodReference)m).Parameters.Count > 0 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == firstParamFullName)));
			}
			return false;
		}

		public void Skip(string label, string why)
		{
			Skipped++;
			SkipDetails.Add(label);
			_log.LogWarning((object)("SKIPPED " + label + ": " + why));
		}

		public MethodDefinition Begin(TypeDefinition type, string name, TypeReference returnType, IList<Tuple<string, TypeReference>> bridgeParams, string label, bool isStatic, out bool ok)
		{
			//IL_0185: 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_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Expected O, but got Unknown
			ok = false;
			List<TypeReference> wanted = bridgeParams.Select((Tuple<string, TypeReference> p) => p.Item2).ToList();
			if (((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => SameShape(m, name, wanted) && ((MemberReference)((MethodReference)m).ReturnType).FullName == ((MemberReference)returnType).FullName))
			{
				AlreadyPresent++;
				_log.LogInfo((object)("present natively, no bridge needed: " + label));
				return null;
			}
			MethodDefinition val = ((IEnumerable<MethodDefinition>)type.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => SameShape(m, name, wanted)));
			if (val != null)
			{
				Blocked++;
				BlockedReturnTypeOnly++;
				BlockDetails.Add(label + " (return-type-only overload of " + ((MemberReference)((MethodReference)val).ReturnType).Name + " " + name + ")");
				_log.LogWarning((object)("BLOCKED " + label + ": this would differ from the existing " + ((MemberReference)((MethodReference)val).ReturnType).Name + " " + name + "(...) by RETURN TYPE ALONE. Type.GetMethod cannot disambiguate that, so every plugin reflecting on this name would throw AmbiguousMatchException at startup. Left unbridged deliberately; pre-1.0 call sites to it stay broken."));
				return null;
			}
			if (RefusedByGuard(type, name, label))
			{
				return null;
			}
			MethodAttributes val2 = (MethodAttributes)134;
			if (isStatic)
			{
				val2 = (MethodAttributes)(val2 | 0x10);
			}
			MethodDefinition val3 = new MethodDefinition(name, val2, _module.ImportReference(returnType));
			foreach (Tuple<string, TypeReference> bridgeParam in bridgeParams)
			{
				((MethodReference)val3).Parameters.Add(new ParameterDefinition(bridgeParam.Item1, (ParameterAttributes)0, _module.ImportReference(bridgeParam.Item2)));
			}
			ok = true;
			return val3;
		}

		public void Commit(TypeDefinition type, MethodDefinition bridge, string label)
		{
			type.Methods.Add(bridge);
			Tag(bridge, null);
			Injected++;
			_log.LogInfo((object)("Injected " + label));
		}

		public bool AliasField(TypeDefinition type, FieldDefinition native, string aliasName, string label)
		{
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Expected O, but got Unknown
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			if (native == null)
			{
				Skip(label, "the native field is not on this build");
				return false;
			}
			if (native.IsStatic)
			{
				Skip(label, "static fields are not aliased");
				return false;
			}
			if (((IEnumerable<FieldDefinition>)type.Fields).Any((FieldDefinition f) => ((MemberReference)f).Name == aliasName && ((MemberReference)((FieldReference)f).FieldType).FullName == ((MemberReference)((FieldReference)native).FieldType).FullName))
			{
				AlreadyPresent++;
				_log.LogInfo((object)("present natively, no alias needed: " + label));
				return false;
			}
			if (_guard.FieldAliasWouldBreak(aliasName, out var offenders))
			{
				Blocked++;
				BlockDetails.Add(label + " (reflected by name in " + offenders + ")");
				_log.LogWarning((object)("BLOCKED " + label + ": " + offenders + " look this field up by name through Type.GetField / Traverse.Field, which throws AmbiguousMatchException the moment two fields share the name. Left unaliased deliberately."));
				return false;
			}
			List<string> list = (from m in _module.GetTypes().SelectMany((TypeDefinition t) => (IEnumerable<MethodDefinition>)t.Methods)
				where m.HasBody && (!m.IsConstructor || m.DeclaringType != type) && ((IEnumerable<Instruction>)m.Body.Instructions).Any(delegate(Instruction i)
				{
					//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)
					if (i.OpCode == OpCodes.Stfld)
					{
						object operand = i.Operand;
						FieldReference val6 = (FieldReference)((operand is FieldReference) ? operand : null);
						if (val6 != null)
						{
							return SameField(val6, native);
						}
					}
					return false;
				})
				select ((MemberReference)m).FullName).ToList();
			if (list.Count > 0)
			{
				Skip(label, ((MemberReference)native).Name + " is reassigned outside the constructor (" + string.Join(", ", list.ToArray()) + "); an alias could not be kept in sync");
				return false;
			}
			FieldDefinition val = new FieldDefinition(aliasName, (FieldAttributes)6, _module.ImportReference(((FieldReference)native).FieldType));
			int num = 0;
			foreach (MethodDefinition item in ((IEnumerable<MethodDefinition>)type.Methods).Where((MethodDefinition m) => m.IsConstructor && !m.IsStatic && m.HasBody))
			{
				ILProcessor iLProcessor = item.Body.GetILProcessor();
				foreach (Instruction item2 in ((IEnumerable<Instruction>)item.Body.Instructions).Where(delegate(Instruction i)
				{
					//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)
					if (i.OpCode == OpCodes.Stfld)
					{
						object operand = i.Operand;
						FieldReference val6 = (FieldReference)((operand is FieldReference) ? operand : null);
						if (val6 != null)
						{
							return SameField(val6, native);
						}
					}
					return false;
				}).ToList())
				{
					Instruction val2 = iLProcessor.Create(OpCodes.Ldarg_0);
					Instruction val3 = iLProcessor.Create(OpCodes.Ldarg_0);
					Instruction val4 = iLProcessor.Create(OpCodes.Ldfld, (FieldReference)(object)native);
					Instruction val5 = iLProcessor.Create(OpCodes.Stfld, (FieldReference)(object)val);
					iLProcessor.InsertAfter(item2, val2);
					iLProcessor.InsertAfter(val2, val3);
					iLProcessor.InsertAfter(val3, val4);
					iLProcessor.InsertAfter(val4, val5);
					num++;
				}
			}
			if (num == 0)
			{
				Skip(label, "no constructor of " + ((MemberReference)type).Name + " assigns " + ((MemberReference)native).Name + "; the alias would stay null");
				return false;
			}
			type.Fields.Add(val);
			TagField(val, native);
			Injected++;
			_log.LogInfo((object)$"Injected {label} (alias of {((MemberReference)((FieldReference)native).FieldType).Name} {((MemberReference)native).Name}, mirrored in {num} constructor assignment(s))");
			return true;
		}

		public bool DeLiteralizeField(TypeDefinition type, string fieldName, string label)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (type == null)
			{
				return false;
			}
			FieldDefinition val = ((IEnumerable<FieldDefinition>)type.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == fieldName));
			if (val == null)
			{
				Skip(label, "field not found on this build");
				return false;
			}
			if (!val.IsLiteral)
			{
				AlreadyPresent++;
				_log.LogInfo((object)("present as regular field, no de-literalization needed: " + label));
				return false;
			}
			val.Attributes = (FieldAttributes)(val.Attributes & 0xFFBF);
			val.Attributes = (FieldAttributes)(val.Attributes & 0x7FFF);
			val.HasConstant = false;
			val.Constant = null;
			Injected++;
			_log.LogInfo((object)("Injected " + label + " (de-literalized const -> static field for ldsfld compatibility)"));
			return true;
		}

		public bool DefaultizeInterfaceMethod(TypeDefinition iface, string methodName, Action<ILProcessor> emitBody, string label)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			if (iface == null)
			{
				return false;
			}
			MethodDefinition val = ((IEnumerable<MethodDefinition>)iface.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == methodName && ((MethodReference)m).Parameters.Count == 0));
			if (val == null)
			{
				Skip(label, "method not found on this build");
				return false;
			}
			if (!val.IsAbstract)
			{
				AlreadyPresent++;
				_log.LogInfo((object)("present as a concrete method, no default needed: " + label));
				return false;
			}
			val.Attributes = (MethodAttributes)(val.Attributes & 0xFBFF);
			val.Body = new MethodBody(val);
			emitBody(val.Body.GetILProcessor());
			Injected++;
			_log.LogInfo((object)("Injected " + label + " (gave the interface's abstract method a default body, so non-overriding implementers still pass VTable setup)"));
			return true;
		}

		private static bool SameField(FieldReference fr, FieldDefinition def)
		{
			if ((object)fr == def)
			{
				return true;
			}
			if (((MemberReference)fr).Name != ((MemberReference)def).Name || ((MemberReference)((MemberReference)fr).DeclaringType).FullName != ((MemberReference)def.DeclaringType).FullName)
			{
				return false;
			}
			return ((MemberReference)fr.FieldType).FullName == ((MemberReference)((FieldReference)def).FieldType).FullName;
		}

		private static string SimpleSig(MethodDefinition m)
		{
			return ((MemberReference)m).Name + "(" + string.Join(",", ((IEnumerable<ParameterDefinition>)((MethodReference)m).Parameters).Select((ParameterDefinition p) => ((MemberReference)((ParameterReference)p).ParameterType).Name).ToArray()) + ")";
		}

		private void Tag(MethodDefinition bridge, MethodDefinition target)
		{
			string text = "Valheim10Compatibility bridge";
			if (target != null && !target.IsConstructor)
			{
				text = text + " -> " + SimpleSig(target);
			}
			AddObsolete((ICollection<CustomAttribute>)bridge.CustomAttributes, text, ((MemberReference)bridge).Name);
		}

		private void TagField(FieldDefinition alias, FieldDefinition native)
		{
			AddObsolete((ICollection<CustomAttribute>)alias.CustomAttributes, "Valheim10Compatibility bridge -> " + ((MemberReference)((FieldReference)native).FieldType).Name + " " + ((MemberReference)native).Name, ((MemberReference)alias).Name);
		}

		private void AddObsolete(ICollection<CustomAttribute> attributes, string message, string what)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				CustomAttribute val = new CustomAttribute(_module.ImportReference((MethodBase)typeof(ObsoleteAttribute).GetConstructor(new Type[1] { typeof(string) })));
				val.ConstructorArguments.Add(new CustomAttributeArgument(_module.TypeSystem.String, (object)message));
				attributes.Add(val);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("Could not tag bridge " + what + " with ObsoleteAttribute: " + ex.Message));
			}
		}
	}
	public static class Patcher
	{
		private static readonly ManualLogSource Logger = Logger.CreateLogSource("Valheim10Compatibility");

		private static AmbiguityGuard _guard;

		private static bool _hookWanted = true;

		private static bool _pieceTableAliasWanted = true;

		private static bool _zRoutedRpcWanted = true;

		private static bool _hoverableDefaultWanted = true;

		public static IEnumerable<string> TargetDLLs { get; } = new string[2] { "assembly_valheim.dll", "assembly_utils.dll" };

		public static void Initialize()
		{
			Version version = typeof(Patcher).Assembly.GetName().Version;
			Logger.LogInfo((object)("Valheim 1.0 Compatibility Patcher " + version.ToString(3) + " initialized (Valheim 1.0.x+; built against 1.0.7, verified on 1.0.12)."));
			ReadConfig();
			try
			{
				List<string> list = new List<string>();
				string text = Environment.GetEnvironmentVariable("VALHEIM10COMPAT_PLUGIN_PATH");
				if (string.IsNullOrEmpty(text))
				{
					text = Paths.PluginPath;
					if (!string.IsNullOrEmpty(Paths.ManagedPath))
					{
						list.Add(Paths.ManagedPath);
					}
					if (!string.IsNullOrEmpty(Paths.BepInExRootPath))
					{
						list.Add(Path.Combine(Paths.BepInExRootPath, "core"));
					}
				}
				else
				{
					Logger.LogInfo((object)("Ambiguity guard: using VALHEIM10COMPAT_PLUGIN_PATH='" + text + "'."));
					string environmentVariable = Environment.GetEnvironmentVariable("VALHEIM10COMPAT_MANAGED_PATH");
					if (!string.IsNullOrEmpty(environmentVariable))
					{
						list.Add(environmentVariable);
					}
				}
				_guard = AmbiguityGuard.Build(text, list, Logger);
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Ambiguity guard unavailable (" + ex.GetType().Name + ": " + ex.Message + "); overload bridges are NOT protected against by-name Harmony patches."));
				_guard = AmbiguityGuard.Empty();
			}
			if (_hookWanted)
			{
				if (HarmonyResolver.Install(Logger, _guard))
				{
					Logger.LogInfo((object)$"Harmony resolution hook {HarmonyResolver.Status}; by-name patches resolve past compatibility bridges, and {PreferredOverloads.Count} vanilla-added overloads resolve to their pre-1.0 shape.");
				}
				else
				{
					Logger.LogWarning((object)("Harmony resolution hook " + HarmonyResolver.Status + ". Falling back to 1.1.x behaviour: every bridge beside a by-name Harmony patch is BLOCKED, and pre-1.0 plugins patching Inventory.Load / PieceTable.SetCategory / Attack.GetAttackEitr by name die at PatchAll() as they do on plain 1.0.7."));
				}
			}
			else
			{
				Logger.LogWarning((object)"Harmony resolution hook disabled in Valheim10Compatibility.cfg; running with 1.1.x guard behaviour.");
			}
			_guard.HookInstalled = HarmonyResolver.Installed;
		}

		private static void ReadConfig()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string configPath = Paths.ConfigPath;
				if (!string.IsNullOrEmpty(configPath))
				{
					ConfigFile val = new ConfigFile(Path.Combine(configPath, "Valheim10Compatibility.cfg"), true);
					_hookWanted = val.Bind<bool>("Harmony", "ResolutionHook", true, "Detour HarmonyX's AccessTools lookups so by-name [HarmonyPatch] targets ignore compatibility bridges and resolve vanilla-added overloads (Inventory.Load, PieceTable.SetCategory, Attack.GetAttackEitr, ...) to their pre-1.0 shape. Off = 1.1.x behaviour: every bridge beside a by-name patch is refused.").Value;
					_pieceTableAliasWanted = val.Bind<bool>("Bridges", "PieceTableAvailablePiecesAlias", true, "Inject the pre-1.0 List<List<Piece>> PieceTable.m_availablePieces beside 1.0's HashSet<Piece> field, sharing the per-category lists with m_availablePiecesByCategory. Restores 15 build-piece mods on the reference profile. Caveat: 1.0 inserted PieceCategory.DeepNorth = 5, so a pre-1.0 mod that bakes category numbers (Feasts=5, Food=6, Meads=7, custom tabs from old Max=8) files pieces one tab off. Turn off if build-menu tabs look wrong.").Value;
					_zRoutedRpcWanted = val.Bind<bool>("Bridges", "ZRoutedRpcEverybodyStatic", true, "De-literalize ZRoutedRpc.Everybody from a const to a static field so legacy mods and ServerSync using ldsfld instructions do not throw MissingFieldException.").Value;
					_hoverableDefaultWanted = val.Bind<bool>("Bridges", "HoverableGetHoverOffsetDefault", true, "Give the Hoverable.GetHoverOffset() interface method a default body (returns 0f) instead of leaving it abstract, so a pre-1.0 class implementing Hoverable without it still passes VTable setup. Off = pre-1.3.0 behaviour: every such class throws TypeLoadException the moment its assembly is scanned, taking every Harmony patch in that assembly down with it.").Value;
					val.Bind<bool>("Shields", "ObjectDBDuplicateItems", true, "Acted on by the Adapter plugin (BepInEx/plugins), declared here so this file documents it. Before ObjectDB.UpdateRegisters rebuilds its hash table, drop any later item prefab whose name hash duplicates an earlier one - the first registration wins, which is vanilla's whenever vanilla owns the name - and log which. Vanilla's Dictionary.Add would otherwise throw and abort FejdStartup.Start on client and server (server never loads a world, client menu spams NullReferenceException). Reached on 1.0 through a Jotunn ordering bug whenever a mod's custom item name collides with new 1.0 content (More Ore Deposits 1.3.5's GoldOre). The shield keeps the game up; the colliding mod still needs a 1.0 build. Off = startup aborts as on plain 1.0.");
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Could not read Valheim10Compatibility.cfg (" + ex.GetType().Name + ": " + ex.Message + "); using defaults (all five switches on)."));
			}
		}

		public static void Patch(AssemblyDefinition assembly)
		{
			ModuleDefinition mainModule = assembly.MainModule;
			if (_guard == null)
			{
				_guard = AmbiguityGuard.Empty();
			}
			Injector injector = new Injector(mainModule, Logger, _guard);
			Logger.LogInfo((object)("Patching " + ((AssemblyNameReference)assembly.Name).Name + " for 1.0 backward compatibility..."));
			if (string.Equals(((AssemblyNameReference)assembly.Name).Name, "assembly_utils", StringComparison.OrdinalIgnoreCase))
			{
				PatchAssemblyUtils(mainModule, injector);
			}
			else
			{
				ResolveSectorHelpers(mainModule, injector);
				PatchZDO(mainModule, injector);
				PatchZoneSystem(mainModule, injector);
				PatchZNetScene(mainModule, injector);
				PatchZDOMan(mainModule, injector);
				PatchItemDrop(mainModule, injector);
				PatchTerminal(mainModule, injector);
				PatchCommonSignatureDrift(mainModule, injector);
				PatchSmallAppendedParameters(mainModule, injector);
				PatchRenamedPatchTargets(mainModule, injector);
				PatchPieceTableAlias(mainModule, injector);
				PatchZRoutedRpc(mainModule, injector);
				PatchHoverable(mainModule, injector);
			}
			int num = injector.Blocked - injector.BlockedReturnTypeOnly;
			Logger.LogInfo((object)$"{((AssemblyNameReference)assembly.Name).Name}: {injector.Injected} bridge(s) injected ({injector.Advisory} of them beside a by-name Harmony patch that the resolution hook steers past them), {injector.AlreadyPresent} already native, {injector.Skipped} skipped (no target on this build), {injector.Blocked} blocked ({num} would break a by-name Harmony patch or a reflective field lookup, {injector.BlockedReturnTypeOnly} would be a return-type-only overload).");
			if (injector.Advisory > 0)
			{
				Logger.LogInfo((object)("  hook-dependent: " + string.Join("; ", injector.AdvisoryDetails.ToArray())));
			}
			if (injector.Skipped > 0)
			{
				Logger.LogWarning((object)("  skipped: " + string.Join("; ", injector.SkipDetails.ToArray())));
			}
			if (injector.Blocked > 0)
			{
				Logger.LogWarning((object)("  blocked: " + string.Join("; ", injector.BlockDetails.ToArray())));
			}
		}

		private static List<Tuple<string, TypeReference>> Ps(params Tuple<string, TypeReference>[] p)
		{
			return p.ToList();
		}

		private static Tuple<string, TypeReference> P(string n, TypeReference t)
		{
			return Tuple.Create<string, TypeReference>(n, t);
		}

		private static List<Arg> Plan(params Arg[] a)
		{
			return a.ToList();
		}

		private static List<Arg> PassThroughPlus(int count, params Arg[] extra)
		{
			List<Arg> list = new List<Arg>();
			for (int i = 0; i < count; i++)
			{
				list.Add(Arg.P(i));
			}
			list.AddRange(extra);
			return list;
		}

		private static void PatchAssemblyUtils(ModuleDefinition module, Injector inj)
		{
			TypeDefinition type = module.GetType("StringExtensionMethods");
			if (type != null)
			{
				if (inj.Has(type, "GetStableHashCode", 1, "System.String"))
				{
					Logger.LogInfo((object)"present natively, no bridge needed: StringExtensionMethods.GetStableHashCode(string) (the playtest's 2-arg form was reverted before 1.0 release)");
				}
				else
				{
					MethodDefinition target = inj.Find(type, "GetStableHashCode", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "System.String" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == "System.Boolean");
					inj.Forward(type, "GetStableHashCode", target, Ps(P("str", module.TypeSystem.String)), Plan(Arg.P(0), Arg.True()), "StringExtensionMethods.GetStableHashCode(string)", isStatic: true);
				}
			}
			TypeDefinition type2 = module.GetType("ZInput");
			if (type2 != null)
			{
				MethodDefinition target2 = inj.Find(type2, "get_pointerPosition", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 0);
				inj.Forward(type2, "get_mousePosition", target2, Ps(), Plan(), "ZInput.get_mousePosition() -> pointerPosition", isStatic: true);
			}
		}

		private static void ResolveSectorHelpers(ModuleDefinition module, Injector inj)
		{
			TypeReference val = (TypeReference)(object)((IEnumerable<TypeDefinition>)module.Types).FirstOrDefault((Func<TypeDefinition, bool>)((TypeDefinition t) => ((MemberReference)t).Name == "Vector2i"));
			TypeReference v2s = (TypeReference)(object)((IEnumerable<TypeDefinition>)module.Types).FirstOrDefault((Func<TypeDefinition, bool>)((TypeDefinition t) => ((MemberReference)t).Name == "Vector2s"));
			if (val == null || v2s == null)
			{
				AssemblyNameReference val2 = ((IEnumerable<AssemblyNameReference>)module.AssemblyReferences).FirstOrDefault((Func<AssemblyNameReference, bool>)((AssemblyNameReference a) => a.Name == "assembly_utils"));
				if (val2 != null)
				{
					AssemblyDefinition val3 = module.AssemblyResolver.Resolve(val2);
					if (val3 != null)
					{
						TypeDefinition type = val3.MainModule.GetType("Vector2i");
						TypeDefinition type2 = val3.MainModule.GetType("Vector2s");
						if (type != null)
						{
							val = module.ImportReference((TypeReference)(object)type);
						}
						if (type2 != null)
						{
							v2s = module.ImportReference((TypeReference)(object)type2);
						}
					}
				}
			}
			if (val == null || v2s == null)
			{
				Logger.LogError((object)"Could not resolve Vector2i or Vector2s; every sector bridge will be skipped.");
				return;
			}
			inj.Vector2i = val;
			inj.Vector2s = v2s;
			TypeDefinition val4 = val.Resolve();
			TypeDefinition val5 = v2s.Resolve();
			inj.V2iX = module.ImportReference((FieldReference)(object)((IEnumerable<FieldDefinition>)val4.Fields).First((FieldDefinition f) => ((MemberReference)f).Name == "x"));
			inj.V2iY = module.ImportReference((FieldReference)(object)((IEnumerable<FieldDefinition>)val4.Fields).First((FieldDefinition f) => ((MemberReference)f).Name == "y"));
			inj.Vector2sCtor = module.ImportReference((MethodReference)(object)((IEnumerable<MethodDefinition>)val5.Methods).First((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 2 && (int)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType.MetadataType != 18));
			TypeDefinition type3 = module.GetType("ZoneSystem");
			MethodDefinition val6 = inj.Find(type3, "GetZonePos", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == ((MemberReference)v2s).FullName);
			if (val6 != null)
			{
				inj.GetZonePosV2s = module.ImportReference((MethodReference)(object)val6);
			}
			TypeDefinition type4 = module.GetType("SimulationDistance");
			if (type4 != null)
			{
				MethodDefinition val7 = ((IEnumerable<MethodDefinition>)type4.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 3 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "System.Int32" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == "System.Int32" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[2]).ParameterType).FullName == "System.Boolean"));
				if (val7 != null)
				{
					inj.SimulationDistanceCtor = module.ImportReference((MethodReference)(object)val7);
				}
			}
		}

		private static void PatchZDO(ModuleDefinition module, Injector inj)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			TypeDefinition type = module.GetType("ZDO");
			if (type == null || inj.Vector2i == null || ((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == "GetSector" && ((MethodReference)m).Parameters.Count == 0 && ((MemberReference)((MethodReference)m).ReturnType).FullName == ((MemberReference)inj.Vector2i).FullName))
			{
				return;
			}
			MethodDefinition val = inj.Find(type, "GetSector", (MethodDefinition m) => ((MethodReference)m).Parameters.Count == 0 && ((MemberReference)((MethodReference)m).ReturnType).FullName == ((MemberReference)inj.Vector2s).FullName);
			if (val == null)
			{
				inj.Skip("ZDO.GetSector() -> Vector2i", "no GetSector() -> Vector2s on this build");
				return;
			}
			bool ok;
			MethodDefinition val2 = inj.Begin(type, "GetSector", inj.Vector2i, Ps(), "ZDO.GetSector() -> Vector2i", isStatic: false, out ok);
			if (ok)
			{
				VariableDefinition val3 = new VariableDefinition(module.ImportReference(inj.Vector2s));
				val2.Body.Variables.Add(val3);
				val2.Body.InitLocals = true;
				ILProcessor iLProcessor = val2.Body.GetILProcessor();
				iLProcessor.Emit(OpCodes.Ldarg_0);
				iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodReference)(object)val));
				iLProcessor.Emit(OpCodes.Stloc_S, val3);
				EmitV2iFromV2s(module, iLProcessor, val3, inj);
				iLProcessor.Emit(OpCodes.Ret);
				inj.Commit(type, val2, "ZDO.GetSector() -> Vector2i");
			}
		}

		private static void EmitV2iFromV2s(ModuleDefinition module, ILProcessor il, VariableDefinition v2sLocal, Injector inj)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			TypeDefinition val = inj.Vector2s.Resolve();
			TypeDefinition val2 = inj.Vector2i.Resolve();
			FieldReference val3 = module.ImportReference((FieldReference)(object)((IEnumerable<FieldDefinition>)val.Fields).First((FieldDefinition f) => ((MemberReference)f).Name == "x"));
			FieldReference val4 = module.ImportReference((FieldReference)(object)((IEnumerable<FieldDefinition>)val.Fields).First((FieldDefinition f) => ((MemberReference)f).Name == "y"));
			MethodReference val5 = module.ImportReference((MethodReference)(object)((IEnumerable<MethodDefinition>)val2.Methods).First((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 2));
			il.Emit(OpCodes.Ldloca_S, v2sLocal);
			il.Emit(OpCodes.Ldfld, val3);
			il.Emit(OpCodes.Conv_I4);
			il.Emit(OpCodes.Ldloca_S, v2sLocal);
			il.Emit(OpCodes.Ldfld, val4);
			il.Emit(OpCodes.Conv_I4);
			il.Emit(OpCodes.Newobj, val5);
		}

		private static void PatchZoneSystem(ModuleDefinition module, Injector inj)
		{
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Expected O, but got Unknown
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			TypeDefinition zs = module.GetType("ZoneSystem");
			if (zs == null || inj.Vector2i == null)
			{
				return;
			}
			string v2sName = ((MemberReference)inj.Vector2s).FullName;
			Func<string, int, MethodDefinition> func = (string name, int count) => inj.Find(zs, name, (MethodDefinition m) => ((MethodReference)m).Parameters.Count == count && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == v2sName);
			inj.Forward(zs, "PokeLocalZone", func("PokeLocalZone", 1), Ps(P("zoneID", inj.Vector2i)), Plan(Arg.V2s(0)), "ZoneSystem.PokeLocalZone(Vector2i)");
			inj.Forward(zs, "IsZoneGenerated", func("IsZoneGenerated", 1), Ps(P("zoneID", inj.Vector2i)), Plan(Arg.V2s(0)), "ZoneSystem.IsZoneGenerated(Vector2i)");
			inj.Forward(zs, "IsZoneLoaded", func("IsZoneLoaded", 1), Ps(P("zoneID", inj.Vector2i)), Plan(Arg.V2s(0)), "ZoneSystem.IsZoneLoaded(Vector2i)");
			inj.Forward(zs, "GetZonePos", func("GetZonePos", 1), Ps(P("id", inj.Vector2i)), Plan(Arg.V2s(0)), "ZoneSystem.GetZonePos(Vector2i)", isStatic: true);
			MethodDefinition val = inj.Find(zs, "GetZone", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == "Vector3" && ((MemberReference)((MethodReference)m).ReturnType).FullName == v2sName);
			if (!((IEnumerable<MethodDefinition>)zs.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == "GetZone" && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((MethodReference)m).ReturnType).FullName == ((MemberReference)inj.Vector2i).FullName))
			{
				if (val == null)
				{
					inj.Skip("ZoneSystem.GetZone(Vector3) -> Vector2i", "no GetZone(Vector3) -> Vector2s on this build");
				}
				else
				{
					bool ok;
					MethodDefinition val2 = inj.Begin(zs, "GetZone", inj.Vector2i, Ps(P("point", ((ParameterReference)((MethodReference)val).Parameters[0]).ParameterType)), "ZoneSystem.GetZone(Vector3) -> Vector2i", isStatic: true, out ok);
					if (ok)
					{
						VariableDefinition val3 = new VariableDefinition(module.ImportReference(inj.Vector2s));
						val2.Body.Variables.Add(val3);
						val2.Body.InitLocals = true;
						ILProcessor iLProcessor = val2.Body.GetILProcessor();
						iLProcessor.Emit(OpCodes.Ldarg_0);
						iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodReference)(object)val));
						iLProcessor.Emit(OpCodes.Stloc_S, val3);
						EmitV2iFromV2s(module, iLProcessor, val3, inj);
						iLProcessor.Emit(OpCodes.Ret);
						inj.Commit(zs, val2, "ZoneSystem.GetZone(Vector3) -> Vector2i");
					}
				}
			}
			else
			{
				Logger.LogInfo((object)"present natively, no bridge needed: ZoneSystem.GetZone(Vector3) -> Vector2i");
			}
			MethodDefinition val4 = func("SpawnZone", 3);
			inj.Forward(zs, "SpawnZone", val4, Ps(P("zoneID", inj.Vector2i), P("mode", (val4 != null) ? ((ParameterReference)((MethodReference)val4).Parameters[1]).ParameterType : module.TypeSystem.Int32), P("root", (val4 != null) ? ((ParameterReference)((MethodReference)val4).Parameters[2]).ParameterType : module.TypeSystem.Object)), Plan(Arg.V2s(0), Arg.P(1), Arg.P(2)), "ZoneSystem.SpawnZone(Vector2i, SpawnMode, out GameObject)");
			MethodDefinition val5 = func("PlaceVegetation", 7);
			if (val5 != null)
			{
				List<Tuple<string, TypeReference>> list = Ps(P("zoneID", inj.Vector2i));
				List<Arg> list2 = new List<Arg> { Arg.V2s(0) };
				for (int num = 1; num < ((MethodReference)val5).Parameters.Count; num++)
				{
					list.Add(P(((ParameterReference)((MethodReference)val5).Parameters[num]).Name, ((ParameterReference)((MethodReference)val5).Parameters[num]).ParameterType));
					list2.Add(Arg.P(num));
				}
				inj.Forward(zs, "PlaceVegetation", val5, list, list2, "ZoneSystem.PlaceVegetation(Vector2i, ...)");
			}
			else
			{
				inj.Skip("ZoneSystem.PlaceVegetation(Vector2i, ...)", "no 7-parameter PlaceVegetation(Vector2s, ...) on this build");
			}
		}

		private static void PatchZNetScene(ModuleDefinition module, Injector inj)
		{
			TypeDefinition type = module.GetType("ZNetScene");
			if (type != null && inj.Vector2i != null)
			{
				string v2sName = ((MemberReference)inj.Vector2s).FullName;
				MethodDefinition target = inj.Find(type, "InActiveArea", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == "Vector3" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == v2sName);
				MethodDefinition val = inj.Find(type, "InActiveArea", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == "Vector3" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).Name == "Vector3");
				inj.Forward(type, "InActiveArea", target, Ps(P("zone", inj.Vector2i), P("refCenterZone", inj.Vector2i)), Plan(Arg.ZonePos(0), Arg.V2s(1)), "ZNetScene.InActiveArea(Vector2i zone, Vector2i refCenterZone) -> InActiveArea(GetZonePos(zone), refCenterZone)", isStatic: true);
				if (val != null)
				{
					inj.Forward(type, "InActiveArea", val, Ps(P("zone", inj.Vector2i), P("refPoint", ((ParameterReference)((MethodReference)val).Parameters[1]).ParameterType)), Plan(Arg.ZonePos(0), Arg.P(1)), "ZNetScene.InActiveArea(Vector2i zone, Vector3 refPoint) -> InActiveArea(GetZonePos(zone), refPoint)", isStatic: true);
				}
				else
				{
					inj.Skip("ZNetScene.InActiveArea(Vector2i, Vector3)", "no static InActiveArea(Vector3, Vector3) on this build");
				}
				inj.Forward(type, "HaveInstanceInSector", inj.Find(type, "HaveInstanceInSector", (MethodDefinition m) => ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == v2sName), Ps(P("sector", inj.Vector2i)), Plan(Arg.V2s(0)), "ZNetScene.HaveInstanceInSector(Vector2i)");
			}
		}

		private static void PatchZDOMan(ModuleDefinition module, Injector inj)
		{
			TypeDefinition type = module.GetType("ZDOMan");
			TypeDefinition type2 = module.GetType("ZDO");
			if (type != null && type2 != null && inj.Vector2i != null)
			{
				string v2sName = ((MemberReference)inj.Vector2s).FullName;
				MethodDefinition val = inj.Find(type, "FindSectorObjects", (MethodDefinition m) => ((MethodReference)m).Parameters.Count == 4 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == v2sName);
				if (val != null && inj.SimulationDistanceCtor == null)
				{
					inj.Skip("ZDOMan.FindSectorObjects(Vector2i, int, int, List<ZDO>, List<ZDO>)", "SimulationDistance(int, int, bool) not found on this build");
				}
				else
				{
					inj.Forward(type, "FindSectorObjects", val, Ps(P("sector", inj.Vector2i), P("area", module.TypeSystem.Int32), P("distantArea", module.TypeSystem.Int32), P("sectorObjects", (val != null) ? ((ParameterReference)((MethodReference)val).Parameters[2]).ParameterType : module.TypeSystem.Object), P("distantSectorObjects", (val != null) ? ((ParameterReference)((MethodReference)val).Parameters[3]).ParameterType : module.TypeSystem.Object)), Plan(Arg.V2s(0), Arg.SimDist(1, 2), Arg.P(3), Arg.P(4)), "ZDOMan.FindSectorObjects(Vector2i, int, int, List<ZDO>, List<ZDO>) -> SimulationDistance(area, distantArea, classic: true)");
				}
				FieldDefinition val2 = ((IEnumerable<FieldDefinition>)type.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "m_visitedSectorIndices"));
				if (val2 == null)
				{
					inj.Skip("ZDOMan.FindObjects/FindDistantObjects(Vector2i, List<ZDO>)", "ZDOMan.m_visitedSectorIndices not found on this build");
				}
				else
				{
					BridgeFindObjects(module, inj, type, "FindObjects", val2);
					BridgeFindObjects(module, inj, type, "FindDistantObjects", val2);
				}
				BridgeSectorToIndex(module, inj, type);
			}
		}

		private static void BridgeFindObjects(ModuleDefinition module, Injector inj, TypeDefinition zm, string name, FieldDefinition visited)
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			string v2sName = ((MemberReference)inj.Vector2s).FullName;
			MethodDefinition val = inj.Find(zm, name, (MethodDefinition m) => ((MethodReference)m).Parameters.Count == 3 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == v2sName);
			string label = "ZDOMan." + name + "(Vector2i, List<ZDO>) (clears and reuses this.m_visitedSectorIndices)";
			if (val == null)
			{
				inj.Skip(label, "no 3-parameter " + name + "(Vector2s, List<ZDO>, HashSet<SectorIndex>) on this build");
			}
			else if (!inj.Has(zm, name, 2, ((MemberReference)inj.Vector2i).FullName))
			{
				bool ok;
				MethodDefinition val2 = inj.Begin(zm, name, module.TypeSystem.Void, Ps(P("sector", inj.Vector2i), P("objects", ((ParameterReference)((MethodReference)val).Parameters[1]).ParameterType)), label, isStatic: false, out ok);
				if (ok)
				{
					FieldReference val3 = module.ImportReference((FieldReference)(object)visited);
					MethodReference val4 = new MethodReference("Clear", module.TypeSystem.Void, val3.FieldType)
					{
						HasThis = true
					};
					ILProcessor iLProcessor = val2.Body.GetILProcessor();
					iLProcessor.Emit(OpCodes.Ldarg_0);
					iLProcessor.Emit(OpCodes.Ldfld, val3);
					iLProcessor.Emit(OpCodes.Callvirt, val4);
					iLProcessor.Emit(OpCodes.Ldarg_0);
					iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val2).Parameters[0]);
					iLProcessor.Emit(OpCodes.Ldfld, inj.V2iX);
					iLProcessor.Emit(OpCodes.Conv_I2);
					iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val2).Parameters[0]);
					iLProcessor.Emit(OpCodes.Ldfld, inj.V2iY);
					iLProcessor.Emit(OpCodes.Conv_I2);
					iLProcessor.Emit(OpCodes.Newobj, inj.Vector2sCtor);
					iLProcessor.Emit(OpCodes.Ldarg_S, ((MethodReference)val2).Parameters[1]);
					iLProcessor.Emit(OpCodes.Ldarg_0);
					iLProcessor.Emit(OpCodes.Ldfld, val3);
					iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodReference)(object)val));
					iLProcessor.Emit(OpCodes.Ret);
					inj.Commit(zm, val2, label);
				}
			}
		}

		private static void BridgeSectorToIndex(ModuleDefinition module, Injector inj, TypeDefinition zm)
		{
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Expected O, but got Unknown
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: 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_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			if (inj.Has(zm, "SectorToIndex", 1, ((MemberReference)inj.Vector2i).FullName))
			{
				return;
			}
			TypeDefinition type = module.GetType("ZoneSystem");
			MethodDefinition val = inj.Find(type, "SectorToIndex", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "System.Int32" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == "System.Int32");
			FieldDefinition val2 = ((IEnumerable<FieldDefinition>)zm.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "m_objectsBySector"));
			if (val == null || val2 == null)
			{
				inj.Skip("ZDOMan.SectorToIndex(Vector2i) -> int (pre-1.0 contract, -1 when out of range)", "ZoneSystem.SectorToIndex(int, int) or ZDOMan.m_objectsBySector not found on this build");
				return;
			}
			TypeDefinition obj = ((MethodReference)val).ReturnType.Resolve();
			FieldDefinition val3 = ((obj != null) ? ((IEnumerable<FieldDefinition>)obj.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "Sector" && !f.IsStatic)) : null);
			if (val3 == null)
			{
				inj.Skip("ZDOMan.SectorToIndex(Vector2i) -> int (pre-1.0 contract, -1 when out of range)", "ZoneSystem.SectorIndex.Sector not found on this build");
				return;
			}
			bool ok;
			MethodDefinition val4 = inj.Begin(zm, "SectorToIndex", module.TypeSystem.Int32, Ps(P("s", inj.Vector2i)), "ZDOMan.SectorToIndex(Vector2i) -> int (pre-1.0 contract, -1 when out of range)", isStatic: false, out ok);
			if (ok)
			{
				VariableDefinition val5 = new VariableDefinition(module.ImportReference(((MethodReference)val).ReturnType));
				val4.Body.Variables.Add(val5);
				val4.Body.InitLocals = true;
				ILProcessor iLProcessor = val4.Body.GetILProcessor();
				Instruction val6 = iLProcessor.Create(OpCodes.Ldc_I4_M1);
				Instruction val7 = iLProcessor.Create(OpCodes.Ldloc_S, val5);
				iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val4).Parameters[0]);
				iLProcessor.Emit(OpCodes.Ldfld, inj.V2iX);
				iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val4).Parameters[0]);
				iLProcessor.Emit(OpCodes.Ldfld, inj.V2iY);
				iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodReference)(object)val));
				iLProcessor.Emit(OpCodes.Stloc_S, val5);
				iLProcessor.Emit(OpCodes.Ldloca_S, val5);
				iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference((FieldReference)(object)val3));
				iLProcessor.Emit(OpCodes.Brtrue_S, val7);
				iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val4).Parameters[0]);
				iLProcessor.Emit(OpCodes.Ldfld, inj.V2iX);
				iLProcessor.Emit(OpCodes.Ldc_I4, -256);
				iLProcessor.Emit(OpCodes.Bne_Un_S, val6);
				iLProcessor.Emit(OpCodes.Ldarga_S, ((MethodReference)val4).Parameters[0]);
				iLProcessor.Emit(OpCodes.Ldfld, inj.V2iY);
				iLProcessor.Emit(OpCodes.Ldc_I4, -256);
				iLProcessor.Emit(OpCodes.Bne_Un_S, val6);
				iLProcessor.Append(val7);
				iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference((FieldReference)(object)val3));
				iLProcessor.Emit(OpCodes.Ret);
				iLProcessor.Append(val6);
				iLProcessor.Emit(OpCodes.Ret);
				inj.Commit(zm, val4, "ZDOMan.SectorToIndex(Vector2i) -> int (pre-1.0 contract, -1 when out of range)");
			}
		}

		private static void PatchItemDrop(ModuleDefinition module, Injector inj)
		{
			TypeDefinition type = module.GetType("ItemDrop");
			if (type == null)
			{
				return;
			}
			ForwardItemDropZdo(module, inj, type, "SaveToZDO", bridgeTwoArg: true);
			ForwardItemDropZdo(module, inj, type, "LoadFromZDO", bridgeTwoArg: false);
			string[] array = new string[2] { "GameObject", "ItemDrop" };
			foreach (string firstParam in array)
			{
				MethodDefinition val = inj.Find(type, "OnCreateNew", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 2 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == firstParam && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[1]).ParameterType).FullName == "System.Boolean");
				if (val == null)
				{
					inj.Skip("ItemDrop.OnCreateNew(" + firstParam + ")", "no OnCreateNew(" + firstParam + ", bool) on this build");
					continue;
				}
				inj.Forward(type, "OnCreateNew", val, Ps(P(((ParameterReference)((MethodReference)val).Parameters[0]).Name, ((ParameterReference)((MethodReference)val).Parameters[0]).ParameterType)), PassThroughPlus(1, Arg.False()), "ItemDrop.OnCreateNew(" + firstParam + ")", isStatic: true);
			}
			TypeDefinition val2 = ((IEnumerable<TypeDefinition>)type.NestedTypes).FirstOrDefault((Func<TypeDefinition, bool>)((TypeDefinition t) => ((MemberReference)t).Name == "ItemData"));
			if (val2 == null)
			{
				return;
			}
			MethodDefinition val3 = inj.Find(val2, "GetTooltip", (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 6);
			if (val3 != null)
			{
				List<Tuple<string, TypeReference>> bridgeParams = (from p in ((IEnumerable<ParameterDefinition>)((MethodReference)val3).Parameters).Take(5)
					select P(((ParameterReference)p).Name, ((ParameterReference)p).ParameterType)).ToList();
				inj.Forward(val2, "GetTooltip", val3, bridgeParams, PassThroughPlus(5, Arg.False()), "ItemDrop.ItemData.GetTooltip(ItemData, int, bool, float, int)", isStatic: true);
			}
			else
			{
				inj.Skip("ItemDrop.ItemData.GetTooltip(ItemData, int, bool, float, int)", "no 6-parameter GetTooltip on this build");
			}
		}

		private static void ForwardItemDropZdo(ModuleDefinition module, Injector inj, TypeDefinition itemDrop, string name, bool bridgeTwoArg)
		{
			MethodDefinition val = inj.Find(itemDrop, name, (MethodDefinition m) => m.IsStatic && ((MethodReference)m).Parameters.Count == 3 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).Name == "ItemData" && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[2]).ParameterType).FullName == "System.Int32");
			if (val == null)
			{
				inj.Skip("ItemDrop." + na

plugins/Valheim10Compatibility.Adapter.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Valheim10Compatibility.Adapter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("BepInEx runtime adapter plugin for Valheim 1.0.")]
[assembly: AssemblyFileVersion("1.4.1.0")]
[assembly: AssemblyInformationalVersion("1.4.1+392146eab253da24bd329d86438263eeac9a4c7d")]
[assembly: AssemblyProduct("Valheim10Compatibility.Adapter")]
[assembly: AssemblyTitle("Valheim10Compatibility.Adapter")]
[assembly: AssemblyVersion("1.4.1.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 Valheim10Compatibility.Adapter
{
	[BepInPlugin("com.wubarrk.valheim10adapter", "Valheim10CompatibilityAdapter", "1.4.1")]
	public class AdapterPlugin : BaseUnityPlugin
	{
		public static class ObjectDBShield
		{
			[HarmonyPatch(typeof(ObjectDB), "UpdateRegisters")]
			[HarmonyPrefix]
			[HarmonyPriority(0)]
			public static void PrefixUpdateRegisters(ObjectDB __instance)
			{
				List<GameObject> list = __instance?.m_items;
				if (list == null)
				{
					return;
				}
				Dictionary<int, GameObject> dictionary = new Dictionary<int, GameObject>(list.Count);
				for (int i = 0; i < list.Count; i++)
				{
					GameObject val = list[i];
					if (!((Object)(object)val == (Object)null))
					{
						int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)val).name);
						if (dictionary.TryGetValue(stableHashCode, out var value))
						{
							Log.LogWarning((object)$"[ObjectDBShield] Duplicate item prefab '{((Object)val).name}' (hash {stableHashCode}) in ObjectDB.m_items: kept the first registration ({Describe(value)}), dropped the later one ({Describe(val)}). Vanilla's Dictionary.Add would have thrown here and aborted FejdStartup.Start. A mod registered an item under a name Valheim 1.0 now uses itself; the game stays up, but that mod needs a 1.0 build.");
							list.RemoveAt(i--);
						}
						else
						{
							dictionary.Add(stableHashCode, val);
						}
					}
				}
			}

			private static string Describe(GameObject prefab)
			{
				string text = prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name;
				if (!string.IsNullOrEmpty(text))
				{
					return $"'{text}', instance {((Object)prefab).GetInstanceID()}";
				}
				return $"no ItemDrop, instance {((Object)prefab).GetInstanceID()}";
			}
		}

		public const string PluginGUID = "com.wubarrk.valheim10adapter";

		public const string PluginName = "Valheim10CompatibilityAdapter";

		public const string PluginVersion = "1.4.1";

		private static ManualLogSource Log;

		private Harmony harmony;

		private static readonly FieldInfo AvailablePiecesByCategory = AccessTools.Field(typeof(PieceTable), "m_availablePiecesByCategory");

		private void Awake()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"Valheim10CompatibilityAdapter v1.4.1 loading...");
			harmony = new Harmony("com.wubarrk.valheim10adapter");
			harmony.PatchAll(typeof(AdapterPlugin));
			if (ReadObjectDBShieldSwitch())
			{
				harmony.PatchAll(typeof(ObjectDBShield));
				Log.LogInfo((object)"[ObjectDBShield] armed on ObjectDB.UpdateRegisters: a later item prefab whose name hash duplicates an earlier one is dropped (first registration wins) instead of aborting FejdStartup.Start.");
			}
			else
			{
				Log.LogWarning((object)"[ObjectDBShield] disabled in Valheim10Compatibility.cfg ([Shields] ObjectDBDuplicateItems = false); a duplicate item name hash aborts startup as on plain 1.0.");
			}
			Log.LogInfo((object)"Valheim10CompatibilityAdapter successfully patched runtime hooks.");
		}

		private static bool ReadObjectDBShieldSwitch()
		{
			//IL_001e: 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)
			try
			{
				string configPath = Paths.ConfigPath;
				if (string.IsNullOrEmpty(configPath))
				{
					return true;
				}
				return new ConfigFile(Path.Combine(configPath, "Valheim10Compatibility.cfg"), false)
				{
					SaveOnConfigSet = false
				}.Bind<bool>("Shields", "ObjectDBDuplicateItems", true, "Read by the adapter; the patcher writes this key's description.").Value;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[ObjectDBShield] Could not read Valheim10Compatibility.cfg (" + ex.GetType().Name + ": " + ex.Message + "); the shield stays on."));
				return true;
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		[HarmonyPatch(typeof(World), "SaveWorldFWLData", new Type[] { typeof(DateTime) })]
		[HarmonyPrefix]
		public static void PrefixSaveWorldFWLData(World __instance)
		{
			try
			{
				if (__instance == null || __instance.IsChunkedSave())
				{
					return;
				}
				string dBPath = __instance.GetDBPath();
				string saveFWLPath = __instance.GetSaveFWLPath();
				if (File.Exists(dBPath))
				{
					string text = Path.Combine(Path.GetDirectoryName(dBPath) ?? "", "pre_1.0_chunked_backups", __instance.m_name);
					Directory.CreateDirectory(text);
					string text2 = DateTime.Now.ToString("yyyyMMdd_HHmmss");
					string text3 = Path.Combine(text, __instance.m_name + "_" + text2 + ".db");
					File.Copy(dBPath, text3, overwrite: true);
					if (File.Exists(saveFWLPath))
					{
						string destFileName = Path.Combine(text, __instance.m_name + "_" + text2 + ".fwl");
						File.Copy(saveFWLPath, destFileName, overwrite: true);
					}
					Log.LogWarning((object)("[SaveWatchdog] World '" + __instance.m_name + "' is migrating from monolithic DB to Chunked Saves!"));
					Log.LogInfo((object)("[SaveWatchdog] Successfully generated pre-migration safety backup at: " + text3));
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[SaveWatchdog] Error executing pre-migration backup for world '{__instance?.m_name}': {arg}");
			}
		}

		public static void EnsurePieceTableCapacity(PieceTable table, int requiredCategory = -1)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected I4, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Invalid comparison between Unknown and I4
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected I4, but got Unknown
			if ((Object)(object)table == (Object)null)
			{
				return;
			}
			int num = requiredCategory;
			if (num < 0)
			{
				num = (int)table.GetSelectedCategory();
			}
			int num2 = Math.Max(num, 8);
			if (table.m_pieces != null)
			{
				foreach (GameObject piece in table.m_pieces)
				{
					if ((Object)(object)piece == (Object)null)
					{
						continue;
					}
					Piece component = piece.GetComponent<Piece>();
					if ((Object)(object)component != (Object)null && (int)component.m_category != 100)
					{
						int num3 = (int)component.m_category;
						if (num3 > num2 && num3 < 100)
						{
							num2 = num3;
						}
					}
				}
			}
			int num4 = num2 + 1;
			if (table.m_selectedPiece == null || table.m_selectedPiece.Length < num4)
			{
				Array.Resize(ref table.m_selectedPiece, num4);
			}
			if (table.m_lastSelectedPiece == null || table.m_lastSelectedPiece.Length < num4)
			{
				Array.Resize(ref table.m_lastSelectedPiece, num4);
			}
			if (AvailablePiecesByCategory?.GetValue(table) is List<List<Piece>> { Count: >0 } list && list.Count < num4)
			{
				while (list.Count < num4)
				{
					list.Add(new List<Piece>());
				}
			}
		}

		[HarmonyPatch(typeof(PieceTable), "SetSelected")]
		[HarmonyPrefix]
		public static void PrefixSetSelected(PieceTable __instance)
		{
			EnsurePieceTableCapacity(__instance);
		}

		[HarmonyPatch(typeof(PieceTable), "SetCategory", new Type[] { typeof(int) })]
		[HarmonyPrefix]
		public static void PrefixSetCategory(PieceTable __instance, int index)
		{
			EnsurePieceTableCapacity(__instance, index);
		}

		[HarmonyPatch(typeof(PieceTable), "UpdateAvailable")]
		[HarmonyPrefix]
		public static void PrefixUpdateAvailable(PieceTable __instance)
		{
			EnsurePieceTableCapacity(__instance);
		}

		[HarmonyPatch(typeof(PieceTable), "LeftPiece")]
		[HarmonyPatch(typeof(PieceTable), "RightPiece")]
		[HarmonyPatch(typeof(PieceTable), "DownPiece")]
		[HarmonyPatch(typeof(PieceTable), "UpPiece")]
		[HarmonyPrefix]
		public static void PrefixPieceNavigation(PieceTable __instance)
		{
			EnsurePieceTableCapacity(__instance);
		}

		[HarmonyPatch(typeof(Hud), "UpdatePieceList")]
		[HarmonyFinalizer]
		public static Exception FinalizerUpdatePieceList(Exception __exception)
		{
			if (__exception != null)
			{
				Log.LogWarning((object)("[BuildMenuShield] Handled UI exception in Hud.UpdatePieceList: " + __exception.Message));
			}
			return null;
		}

		[HarmonyPatch(typeof(ZNet), "SendPeerInfo")]
		[HarmonyPrefix]
		public static void PrefixSendPeerInfo(ZRpc rpc, string password)
		{
			Log.LogInfo((object)("[NetHandshake] Outgoing peer handshake to " + rpc.GetSocket().GetHostName() + " (adapter 1.4.1 for Valheim 1.0.x+; running game " + GameVersionText() + ", network version " + NetworkVersionText() + ")."));
		}

		private static string GameVersionText()
		{
			try
			{
				return Version.GetVersionString(false);
			}
			catch
			{
				return "unknown";
			}
		}

		private static string NetworkVersionText()
		{
			try
			{
				FieldInfo field = typeof(Version).GetField("c_networkVersion", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					return "unknown";
				}
				return (field.IsLiteral ? field.GetRawConstantValue() : field.GetValue(null))?.ToString() ?? "unknown";
			}
			catch
			{
				return "unknown";
			}
		}
	}
}