Decompiled source of AdvancedTerrainCompatibility v1.0.1

patchers/L4zerShark_Team-AdvancedTerrainCompatibility/AdvancedTerrainCompatibility.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.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Advanced Terrain Compatibility")]
[assembly: AssemblyDescription("Valheim 1.0.12 compatibility for AdvancedTerrainModifiers")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimThreeCompatibility
{
	public static class CompatibilityLog
	{
		private static readonly object Gate = new object();

		private static ManualLogSource _source;

		public static ManualLogSource Source
		{
			get
			{
				lock (Gate)
				{
					if (_source == null)
					{
						_source = Logger.CreateLogSource("AdvancedTerrainCompatibility");
					}
					return _source;
				}
			}
		}

		public static void Info(string message)
		{
			Source.LogInfo((object)message);
		}

		public static void Warning(string message)
		{
			Source.LogWarning((object)message);
		}

		public static void Error(string message)
		{
			Source.LogError((object)message);
		}
	}
	public static class Loader
	{
		private sealed class PreparedEntry
		{
			public string CachePath;

			public string SimpleName;
		}

		public const string PatcherName = "AdvancedTerrainCompatibility";

		public const string PatcherVersion = "1.0.0";

		public const string HarmonyId = "ValheimThreeCompatibility.AdvancedTerrainCompatibility";

		private const string CombinedPatcherName = "ThreeModCompatibility";

		private const string CombinedHarmonyId = "ValheimThreeCompatibility.ThreeModCompatibility";

		private const string CombinedReplacementMethodName = "LoadCompatible";

		private const string LoaderTypeFullName = "ValheimThreeCompatibility.Loader";

		private const string PrepareMethodName = "PrepareCompatiblePath";

		private const string CacheFolderName = "ValheimThreeCompatibility";

		private const string GameAssemblyFileName = "assembly_valheim.dll";

		private static readonly string[] SupportedFileNames = new string[1] { "TerrainTools.dll" };

		private static readonly object SyncRoot = new object();

		private static readonly Dictionary<string, PreparedEntry> PreparedByOriginalPath = new Dictionary<string, PreparedEntry>(StringComparer.OrdinalIgnoreCase);

		private static bool _finishRan;

		private static int _transpilerRuns;

		private static bool _lastTranspileInserted;

		private static string _lastTranspileFailure;

		private static readonly MethodInfo LoadFileMethod = typeof(Assembly).GetMethod("LoadFile", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);

		private static readonly MethodInfo PrepareCompatiblePathMethod = typeof(Loader).GetMethod("PrepareCompatiblePath", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);

		public static IEnumerable<string> TargetDLLs => new string[0];

		public static void Patch(AssemblyDefinition assembly)
		{
		}

		public static void Finish()
		{
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Expected O, but got Unknown
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Expected O, but got Unknown
			lock (SyncRoot)
			{
				if (_finishRan)
				{
					CompatibilityLog.Warning("Finish invoked more than once; ignoring duplicate call.");
					return;
				}
				_finishRan = true;
			}
			try
			{
				ValidateBuildTarget();
				if (LoadFileMethod == null)
				{
					throw new MissingMethodException("System.Reflection.Assembly", "LoadFile(string)");
				}
				if (PrepareCompatiblePathMethod == null)
				{
					throw new MissingMethodException(typeof(Loader).FullName, "PrepareCompatiblePath(string)");
				}
				string text = FindCombinedAddon();
				if (text != null)
				{
					CompatibilityLog.Error("AdvancedTerrainCompatibility 1.0.0: the archived combined ThreeModCompatibility patcher is installed (" + text + "). It cannot coexist with the split compatibility addons. Remove the combined patcher from BepInEx/patchers. TerrainTools.dll will NOT be repaired by AdvancedTerrainCompatibility this session.");
					return;
				}
				Type type = Type.GetType("BepInEx.Bootstrap.Chainloader, BepInEx", throwOnError: false);
				if (type == null)
				{
					throw new TypeLoadException("BepInEx.Bootstrap.Chainloader was not found in assembly BepInEx.");
				}
				MethodInfo methodInfo = AccessTools.Method(type, "Start", Type.EmptyTypes, (Type[])null);
				if (methodInfo == null || !methodInfo.IsStatic || methodInfo.ReturnType != typeof(void))
				{
					throw new MissingMethodException(type.FullName, "static void Start()");
				}
				FieldInfo fieldInfo = AccessTools.Field(type, "_loaded");
				if (fieldInfo != null && fieldInfo.IsStatic && fieldInfo.FieldType == typeof(bool) && (bool)fieldInfo.GetValue(null))
				{
					throw new InvalidOperationException("Chainloader has already started; the load hook can no longer be installed.");
				}
				Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
				if (patchInfo != null)
				{
					foreach (Patch item in AllPatches(patchInfo))
					{
						if (string.Equals(item.owner, "ValheimThreeCompatibility.ThreeModCompatibility", StringComparison.Ordinal))
						{
							CompatibilityLog.Error("AdvancedTerrainCompatibility 1.0.0: Chainloader.Start is already patched by the archived combined ThreeModCompatibility patcher (Harmony owner ValheimThreeCompatibility.ThreeModCompatibility). Remove the combined patcher. TerrainTools.dll will NOT be repaired by AdvancedTerrainCompatibility this session.");
							return;
						}
					}
					foreach (Patch transpiler in patchInfo.Transpilers)
					{
						if (string.Equals(transpiler.owner, "ValheimThreeCompatibility.AdvancedTerrainCompatibility", StringComparison.Ordinal))
						{
							MethodInfo patchMethod = transpiler.PatchMethod;
							if (patchMethod != null && patchMethod.DeclaringType == typeof(Loader))
							{
								CompatibilityLog.Warning("Chainloader.Start transpiler is already installed; skipping duplicate application.");
							}
							else
							{
								CompatibilityLog.Error("AdvancedTerrainCompatibility 1.0.0: another copy of AdvancedTerrainCompatibility (" + ((patchMethod != null && patchMethod.DeclaringType != null) ? DescribeAssembly(patchMethod.DeclaringType.Assembly) : "unknown assembly") + ") already installed Harmony owner ValheimThreeCompatibility.AdvancedTerrainCompatibility. Remove the duplicate package; this copy will not install.");
							}
							return;
						}
					}
				}
				MethodInfo methodInfo2 = AccessTools.Method(typeof(Loader), "ChainloaderStartTranspiler", (Type[])null, (Type[])null);
				if (methodInfo2 == null)
				{
					throw new MissingMethodException(typeof(Loader).FullName, "ChainloaderStartTranspiler");
				}
				int transpilerRuns = _transpilerRuns;
				_lastTranspileInserted = false;
				_lastTranspileFailure = null;
				Harmony val = new Harmony("ValheimThreeCompatibility.AdvancedTerrainCompatibility");
				val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null);
				if (_transpilerRuns == transpilerRuns)
				{
					throw new InvalidOperationException("Harmony did not invoke the Chainloader.Start transpiler.");
				}
				if (!_lastTranspileInserted)
				{
					try
					{
						val.Unpatch((MethodBase)methodInfo, (HarmonyPatchType)3, "ValheimThreeCompatibility.AdvancedTerrainCompatibility");
					}
					catch (Exception ex)
					{
						CompatibilityLog.Warning("AdvancedTerrainCompatibility: could not remove inactive transpiler: " + ex.Message);
					}
					CompatibilityLog.Error("AdvancedTerrainCompatibility 1.0.0: FAILED to install plugin load hook: " + (_lastTranspileFailure ?? "unknown transpiler failure") + " TerrainTools.dll will NOT be repaired this session.");
				}
				else
				{
					CompatibilityLog.Info("AdvancedTerrainCompatibility 1.0.0: installed plugin load hook for " + string.Join(", ", SupportedFileNames) + ".");
				}
			}
			catch (Exception ex2)
			{
				CompatibilityLog.Error("AdvancedTerrainCompatibility 1.0.0: FAILED to install plugin load hook. Supported target (" + string.Join(", ", SupportedFileNames) + ") will NOT be repaired this session." + Environment.NewLine + ex2);
			}
		}

		private static void ValidateBuildTarget()
		{
			string text = "TerrainTools.dll";
			if (string.IsNullOrEmpty("AdvancedTerrainCompatibility"))
			{
				throw new InvalidOperationException("BuildTarget.Name is empty.");
			}
			if (string.Equals("AdvancedTerrainCompatibility", "ThreeModCompatibility", StringComparison.OrdinalIgnoreCase))
			{
				throw new InvalidOperationException("BuildTarget.Name must not be the archived combined name 'ThreeModCompatibility'.");
			}
			if (string.IsNullOrEmpty(text))
			{
				throw new InvalidOperationException("BuildTarget.TargetFile is empty.");
			}
			if (!string.Equals(Path.GetFileName(text), text, StringComparison.Ordinal))
			{
				throw new InvalidOperationException("BuildTarget.TargetFile must be a bare file name: '" + text + "'.");
			}
			if (!text.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
			{
				throw new InvalidOperationException("BuildTarget.TargetFile must be a .dll: '" + text + "'.");
			}
		}

		private static IEnumerable<Patch> AllPatches(Patches patches)
		{
			foreach (Patch prefix in patches.Prefixes)
			{
				yield return prefix;
			}
			foreach (Patch postfix in patches.Postfixes)
			{
				yield return postfix;
			}
			foreach (Patch transpiler in patches.Transpilers)
			{
				yield return transpiler;
			}
			foreach (Patch finalizer in patches.Finalizers)
			{
				yield return finalizer;
			}
		}

		private static string FindCombinedAddon()
		{
			Assembly assembly = typeof(Loader).Assembly;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly2 in assemblies)
			{
				if (!(assembly2 == assembly))
				{
					string name;
					try
					{
						name = assembly2.GetName().Name;
					}
					catch (Exception)
					{
						continue;
					}
					if (string.Equals(name, "ThreeModCompatibility", StringComparison.OrdinalIgnoreCase))
					{
						return DescribeAssembly(assembly2);
					}
					Type type;
					try
					{
						type = assembly2.GetType("ValheimThreeCompatibility.Loader", throwOnError: false);
					}
					catch (Exception)
					{
						continue;
					}
					if (!(type == null) && string.Equals(ReadPatcherName(type), "ThreeModCompatibility", StringComparison.Ordinal))
					{
						return DescribeAssembly(assembly2);
					}
				}
			}
			return null;
		}

		private static string ReadPatcherName(Type loaderType)
		{
			try
			{
				FieldInfo field = loaderType.GetField("PatcherName", BindingFlags.Static | BindingFlags.Public);
				if (field == null)
				{
					return null;
				}
				return (field.IsLiteral ? field.GetRawConstantValue() : field.GetValue(null)) as string;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static string DescribeAssembly(Assembly a)
		{
			string text;
			try
			{
				text = a.GetName().Name;
			}
			catch (Exception)
			{
				text = "<unnamed>";
			}
			return "assembly '" + text + "' at '" + (SafeLocation(a) ?? "<no location>") + "'";
		}

		private static IEnumerable<CodeInstruction> ChainloaderStartTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Invalid comparison between Unknown and I4
			_transpilerRuns++;
			_lastTranspileInserted = false;
			_lastTranspileFailure = null;
			if (instructions == null)
			{
				RecordTranspileFailure("Harmony supplied no instructions for Chainloader.Start.");
				return instructions;
			}
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			string text;
			int matchIndex;
			try
			{
				text = FindInsertionPoint(list, out matchIndex);
			}
			catch (Exception ex)
			{
				matchIndex = -1;
				text = "unexpected error while inspecting Chainloader.Start: " + ex.Message;
			}
			if (text != null)
			{
				RecordTranspileFailure(text);
				return list;
			}
			List<CodeInstruction> list2 = new List<CodeInstruction>(list.Count + 1);
			for (int i = 0; i < list.Count; i++)
			{
				if (i != matchIndex)
				{
					list2.Add(list[i]);
					continue;
				}
				CodeInstruction val = list[i];
				CodeInstruction val2 = new CodeInstruction(OpCodes.Call, (object)PrepareCompatiblePathMethod);
				val2.labels.AddRange(val.labels);
				CodeInstruction val3 = new CodeInstruction(val.opcode, val.operand);
				foreach (ExceptionBlock block in val.blocks)
				{
					if ((int)block.blockType == 5)
					{
						val3.blocks.Add(block);
					}
					else
					{
						val2.blocks.Add(block);
					}
				}
				list2.Add(val2);
				list2.Add(val3);
			}
			_lastTranspileInserted = true;
			return list2;
		}

		private static void RecordTranspileFailure(string failure)
		{
			_lastTranspileFailure = failure;
			CompatibilityLog.Error("AdvancedTerrainCompatibility 1.0.0: " + failure + " Chainloader.Start left unchanged by this addon; TerrainTools.dll will NOT be repaired.");
		}

		private static string FindInsertionPoint(List<CodeInstruction> list, out int matchIndex)
		{
			matchIndex = -1;
			int num = 0;
			int num2 = -1;
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (val == null)
				{
					return "Chainloader.Start contains a null IL instruction at index " + i + "; refusing to patch.";
				}
				MethodInfo methodInfo = val.operand as MethodInfo;
				if (methodInfo == null)
				{
					continue;
				}
				Type declaringType = methodInfo.DeclaringType;
				if (declaringType == typeof(Assembly) && methodInfo.Name == "LoadFile")
				{
					if (!IsExactLoadFileString(methodInfo))
					{
						return "Chainloader.Start calls an unexpected Assembly.LoadFile overload at index " + i + "; refusing to patch.";
					}
					if (val.opcode != OpCodes.Call)
					{
						return "Assembly.LoadFile(string) at index " + i + " uses unexpected opcode " + val.opcode.ToString() + "; refusing to patch.";
					}
					num++;
					num2 = i;
				}
				else
				{
					if (declaringType == null || !string.Equals(declaringType.FullName, "ValheimThreeCompatibility.Loader", StringComparison.Ordinal))
					{
						continue;
					}
					if (methodInfo.Name == "LoadCompatible")
					{
						return "Chainloader.Start already contains the archived combined ThreeModCompatibility replacement (LoadCompatible from " + DescribeAssembly(declaringType.Assembly) + ") at index " + i + ". Remove the combined patcher; it cannot coexist with split addons.";
					}
					if (methodInfo.Name == "PrepareCompatiblePath")
					{
						if (declaringType == typeof(Loader))
						{
							return "this addon's PrepareCompatiblePath call is already present at index " + i + "; refusing duplicate insertion.";
						}
						string text = ReadPatcherName(declaringType);
						if (text == null)
						{
							return "unrecognised PrepareCompatiblePath call from " + DescribeAssembly(declaringType.Assembly) + " at index " + i + "; refusing to patch.";
						}
						if (string.Equals(text, "ThreeModCompatibility", StringComparison.Ordinal))
						{
							return "Chainloader.Start contains a call from the archived combined ThreeModCompatibility patcher at index " + i + ". Remove the combined patcher.";
						}
						if (string.Equals(text, "AdvancedTerrainCompatibility", StringComparison.Ordinal))
						{
							return "another copy of AdvancedTerrainCompatibility (" + DescribeAssembly(declaringType.Assembly) + ") already inserted its hook at index " + i + ". Remove the duplicate package.";
						}
					}
				}
			}
			if (num != 1)
			{
				return "Expected exactly one Assembly.LoadFile(string) call in Chainloader.Start but found " + num + "; refusing to patch.";
			}
			matchIndex = num2;
			return null;
		}

		private static bool IsExactLoadFileString(MethodInfo method)
		{
			if (!method.IsStatic || method.ReturnType != typeof(Assembly))
			{
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			if (parameters.Length == 1)
			{
				return parameters[0].ParameterType == typeof(string);
			}
			return false;
		}

		public static string PrepareCompatiblePath(string path)
		{
			string text = TryGetSupportedFileName(path);
			if (text == null)
			{
				return path;
			}
			lock (SyncRoot)
			{
				return PrepareSupportedTarget(path, text);
			}
		}

		private static string TryGetSupportedFileName(string path)
		{
			if (string.IsNullOrEmpty(path))
			{
				return null;
			}
			string fileName;
			try
			{
				fileName = Path.GetFileName(path);
			}
			catch (ArgumentException)
			{
				return null;
			}
			string[] supportedFileNames = SupportedFileNames;
			foreach (string text in supportedFileNames)
			{
				if (string.Equals(text, fileName, StringComparison.OrdinalIgnoreCase))
				{
					return text;
				}
			}
			return null;
		}

		private static string PrepareSupportedTarget(string path, string supported)
		{
			string text = "resolving original path";
			string text2 = path;
			string text3 = null;
			try
			{
				text2 = Path.GetFullPath(path);
				if (PreparedByOriginalPath.TryGetValue(text2, out var value))
				{
					text = "checking already-loaded assemblies";
					EnsureNoConflictingLoadedAssembly(value.CachePath, value.SimpleName);
					return value.CachePath;
				}
				text = "reading original file";
				byte[] array = File.ReadAllBytes(text2);
				text = "hashing original file";
				text3 = Sha256Hex(array);
				text = "querying RepairCatalog";
				if (!RepairCatalog.TryGet(supported, text3, out var repair))
				{
					CompatibilityLog.Warning(supported + " at '" + text2 + "' has unrecognised SHA256 " + text3 + "; no repair is known for this build. Chainloader will load the original file UNMODIFIED.");
					return path;
				}
				if (repair == null)
				{
					throw new InvalidOperationException("RepairCatalog reported a match but returned a null repair delegate.");
				}
				text = "building repaired image";
				string simpleName;
				byte[] array2 = BuildRepairedImage(text2, array, repair, out simpleName);
				text = "writing/verifying cache file";
				string cachePath = GetCachePath(text3, supported);
				EnsureCacheFile(cachePath, array2);
				text = "checking already-loaded assemblies";
				EnsureNoConflictingLoadedAssembly(cachePath, simpleName);
				PreparedByOriginalPath[text2] = new PreparedEntry
				{
					CachePath = cachePath,
					SimpleName = simpleName
				};
				CompatibilityLog.Info("Prepared repaired " + supported + " (source sha256 " + text3 + ", repaired sha256 " + Sha256Hex(array2) + ") at '" + cachePath + "'.");
				return cachePath;
			}
			catch (Exception ex)
			{
				string text4 = "AdvancedTerrainCompatibility: refusing to load unpatched supported target " + supported + " ('" + text2 + "', sha256 " + (text3 ?? "unknown") + ") - failed while " + text + ": " + ex.Message;
				CompatibilityLog.Error(text4 + Environment.NewLine + ex);
				throw new InvalidOperationException(text4, ex);
			}
		}

		private static byte[] BuildRepairedImage(string originalPath, byte[] originalBytes, Action<AssemblyDefinition, AssemblyDefinition> repair, out string simpleName)
		{
			//IL_0030: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Expected O, but got Unknown
			string text = Path.Combine(Paths.ManagedPath, "assembly_valheim.dll");
			if (!File.Exists(text))
			{
				throw new FileNotFoundException("Game assembly not found.", text);
			}
			DefaultAssemblyResolver val = CreateResolver(Path.GetDirectoryName(originalPath));
			byte[] array;
			try
			{
				ReaderParameters val2 = new ReaderParameters
				{
					AssemblyResolver = (IAssemblyResolver)(object)val,
					ReadingMode = (ReadingMode)1,
					InMemory = true,
					ReadSymbols = false,
					ReadWrite = false
				};
				ReaderParameters val3 = new ReaderParameters
				{
					AssemblyResolver = (IAssemblyResolver)(object)val,
					ReadingMode = (ReadingMode)1,
					ReadSymbols = false,
					ReadWrite = false
				};
				AssemblyDefinition val4 = AssemblyDefinition.ReadAssembly(text, val2);
				try
				{
					using MemoryStream memoryStream = new MemoryStream(originalBytes, writable: false);
					AssemblyDefinition val5 = AssemblyDefinition.ReadAssembly((Stream)memoryStream, val3);
					try
					{
						if (val5.Name == null || string.IsNullOrEmpty(((AssemblyNameReference)val5.Name).Name))
						{
							throw new BadImageFormatException("Target assembly has no name.");
						}
						simpleName = ((AssemblyNameReference)val5.Name).Name;
						repair(val5, val4);
						if (val5.Name == null || !string.Equals(((AssemblyNameReference)val5.Name).Name, simpleName, StringComparison.Ordinal))
						{
							throw new InvalidOperationException("Repair changed the assembly simple name; refusing.");
						}
						using MemoryStream memoryStream2 = new MemoryStream();
						val5.Write((Stream)memoryStream2);
						array = memoryStream2.ToArray();
					}
					finally
					{
						((IDisposable)val5)?.Dispose();
					}
				}
				finally
				{
					((IDisposable)val4)?.Dispose();
				}
			}
			finally
			{
				((IDisposable)val)?.Dispose();
			}
			if (array == null || array.Length == 0)
			{
				throw new InvalidOperationException("Repair produced an empty image.");
			}
			using MemoryStream memoryStream3 = new MemoryStream(array, writable: false);
			AssemblyDefinition val6 = AssemblyDefinition.ReadAssembly((Stream)memoryStream3, new ReaderParameters
			{
				ReadingMode = (ReadingMode)2,
				ReadSymbols = false
			});
			try
			{
				if (val6.Name == null || !string.Equals(((AssemblyNameReference)val6.Name).Name, simpleName, StringComparison.Ordinal))
				{
					throw new BadImageFormatException("Repaired image failed metadata re-read verification.");
				}
				return array;
			}
			finally
			{
				((IDisposable)val6)?.Dispose();
			}
		}

		private static DefaultAssemblyResolver CreateResolver(string sourceDir)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			DefaultAssemblyResolver val = new DefaultAssemblyResolver();
			HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			AddSearchDir(val, seen, sourceDir);
			AddSearchDir(val, seen, Paths.ManagedPath);
			try
			{
				AddSearchDir(val, seen, Path.GetDirectoryName(typeof(Paths).Assembly.Location));
			}
			catch (Exception)
			{
			}
			try
			{
				AddSearchDir(val, seen, Path.GetDirectoryName(typeof(Harmony).Assembly.Location));
			}
			catch (Exception)
			{
			}
			AddSearchDir(val, seen, Paths.PluginPath);
			try
			{
				if (!string.IsNullOrEmpty(Paths.PluginPath) && Directory.Exists(Paths.PluginPath))
				{
					string[] directories = Directory.GetDirectories(Paths.PluginPath, "*", SearchOption.AllDirectories);
					foreach (string dir in directories)
					{
						AddSearchDir(val, seen, dir);
					}
				}
			}
			catch (IOException)
			{
			}
			catch (UnauthorizedAccessException)
			{
			}
			return val;
		}

		private static void AddSearchDir(DefaultAssemblyResolver resolver, HashSet<string> seen, string dir)
		{
			if (!string.IsNullOrEmpty(dir) && Directory.Exists(dir) && seen.Add(dir))
			{
				((BaseAssemblyResolver)resolver).AddSearchDirectory(dir);
			}
		}

		private static string GetCachePath(string sourceHash, string fileName)
		{
			return Path.GetFullPath(Path.Combine(Path.Combine(Path.Combine(Path.Combine(Paths.CachePath, "ValheimThreeCompatibility"), "1.0.0"), sourceHash.Substring(0, 16)), fileName));
		}

		private static void EnsureCacheFile(string cachePath, byte[] expected)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
			if (FileMatches(cachePath, expected))
			{
				return;
			}
			string text = Path.Combine(Path.GetDirectoryName(cachePath), "t" + Guid.NewGuid().ToString("N").Substring(0, 12) + ".tmp");
			try
			{
				using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None))
				{
					fileStream.Write(expected, 0, expected.Length);
					fileStream.Flush(flushToDisk: true);
				}
				if (!FileMatches(text, expected))
				{
					throw new IOException("Temporary cache file failed verification: " + text);
				}
				try
				{
					if (File.Exists(cachePath))
					{
						File.Replace(text, cachePath, null);
					}
					else
					{
						File.Move(text, cachePath);
					}
				}
				catch (Exception ex)
				{
					if (FileMatches(cachePath, expected))
					{
						return;
					}
					try
					{
						if (File.Exists(cachePath))
						{
							File.Delete(cachePath);
						}
						File.Move(text, cachePath);
					}
					catch (Exception ex2)
					{
						throw new IOException("Could not install cache file '" + cachePath + "': " + ex.Message + " / fallback: " + ex2.Message, ex2);
					}
				}
			}
			finally
			{
				TryDelete(text);
			}
			if (FileMatches(cachePath, expected))
			{
				return;
			}
			throw new IOException("Cache file failed post-write verification: " + cachePath);
		}

		private static void EnsureNoConflictingLoadedAssembly(string cachePath, string simpleName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				string name;
				try
				{
					name = assembly.GetName().Name;
				}
				catch (Exception)
				{
					continue;
				}
				if (string.Equals(name, simpleName, StringComparison.OrdinalIgnoreCase))
				{
					string text = SafeLocation(assembly);
					if (text == null || !PathsEqual(text, cachePath))
					{
						throw new InvalidOperationException("An assembly named '" + simpleName + "' is already loaded from '" + (text ?? "<no location>") + "'; refusing to load a mixed or unpatched copy.");
					}
				}
			}
		}

		private static bool FileMatches(string path, byte[] expected)
		{
			try
			{
				if (!File.Exists(path))
				{
					return false;
				}
				byte[] array = File.ReadAllBytes(path);
				if (array.Length != expected.Length)
				{
					return false;
				}
				for (int i = 0; i < array.Length; i++)
				{
					if (array[i] != expected[i])
					{
						return false;
					}
				}
				return true;
			}
			catch (IOException)
			{
				return false;
			}
			catch (UnauthorizedAccessException)
			{
				return false;
			}
		}

		private static void TryDelete(string path)
		{
			try
			{
				if (File.Exists(path))
				{
					File.Delete(path);
				}
			}
			catch (Exception)
			{
			}
		}

		private static string SafeLocation(Assembly assembly)
		{
			try
			{
				string location = assembly.Location;
				return string.IsNullOrEmpty(location) ? null : location;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static bool PathsEqual(string a, string b)
		{
			try
			{
				return string.Equals(Path.GetFullPath(a), Path.GetFullPath(b), StringComparison.OrdinalIgnoreCase);
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static string Sha256Hex(byte[] data)
		{
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(data)).Replace("-", string.Empty).ToLowerInvariant();
		}
	}
	public static class RepairCatalog
	{
		public static bool TryGet(string fileName, string hash, out Action<AssemblyDefinition, AssemblyDefinition> repair)
		{
			repair = null;
			if (!fileName.Equals("TerrainTools.dll", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (fileName.Equals("CreatureLevelControl.dll", StringComparison.OrdinalIgnoreCase) && hash.Equals("601739B998F956BC49A871A4B0C7761E1F6389E5F8604D3234868F9E92D2761E", StringComparison.OrdinalIgnoreCase))
			{
				repair = delegate(AssemblyDefinition a, AssemblyDefinition g)
				{
					new Repairs(a, g).Creature();
				};
			}
			if (fileName.Equals("TerrainTools.dll", StringComparison.OrdinalIgnoreCase) && hash.Equals("BE8F51E45BAFA1C6B9BC2587360A22B4D33D69516EDF191A46383DE18E608D15", StringComparison.OrdinalIgnoreCase))
			{
				repair = delegate(AssemblyDefinition a, AssemblyDefinition g)
				{
					new Repairs(a, g).Terrain();
				};
			}
			if (fileName.Equals("QuickTeleport.dll", StringComparison.OrdinalIgnoreCase) && hash.Equals("25283FA2010A0EE8AD9A1B921FF7F447AE5C63406F46FFAB9F5949E782A318D2", StringComparison.OrdinalIgnoreCase))
			{
				repair = delegate(AssemblyDefinition a, AssemblyDefinition g)
				{
					new Repairs(a, g).Teleport();
				};
			}
			return repair != null;
		}
	}
	internal sealed class Repairs
	{
		private readonly ModuleDefinition mod;

		private readonly ModuleDefinition game;

		private readonly TypeDefinition bridges;

		private readonly Dictionary<string, MethodDefinition> forwards = new Dictionary<string, MethodDefinition>();

		public Repairs(AssemblyDefinition target, AssemblyDefinition currentGame)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			mod = target.MainModule;
			game = currentGame.MainModule;
			bridges = new TypeDefinition("ValheimThreeCompatibility.Generated", "GameApi", (TypeAttributes)384, mod.TypeSystem.Object);
			mod.Types.Add(bridges);
		}

		private static IEnumerable<TypeDefinition> AllTypes(IEnumerable<TypeDefinition> types)
		{
			foreach (TypeDefinition t in types)
			{
				yield return t;
				foreach (TypeDefinition item in AllTypes((IEnumerable<TypeDefinition>)t.NestedTypes))
				{
					yield return item;
				}
			}
		}

		private IEnumerable<MethodDefinition> Methods()
		{
			return (from m in (from t in AllTypes((IEnumerable<TypeDefinition>)mod.Types)
					where t != bridges
					select t).SelectMany((TypeDefinition t) => (IEnumerable<MethodDefinition>)t.Methods)
				where m.HasBody
				select m).ToArray();
		}

		private TypeDefinition Type(string name)
		{
			return AllTypes((IEnumerable<TypeDefinition>)mod.Types).Single((TypeDefinition t) => ((MemberReference)t).FullName == name);
		}

		private TypeDefinition GameType(string name)
		{
			return game.GetType(name) ?? throw new InvalidOperationException("Missing game type " + name);
		}

		private MethodDefinition GameMethod(string type, string name, int count)
		{
			return ((IEnumerable<MethodDefinition>)GameType(type).Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == name && ((MethodReference)m).Parameters.Count == count);
		}

		private static void Require(bool condition, string message)
		{
			if (!condition)
			{
				throw new InvalidOperationException(message);
			}
		}

		private MethodReference Import(MethodReference method)
		{
			return mod.ImportReference(method);
		}

		private FieldReference Import(FieldReference field)
		{
			return mod.ImportReference(field);
		}

		private MethodDefinition Forward(MethodReference old, MethodDefinition current)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Expected O, but got Unknown
			//IL_041e: Unknown result type (might be due to invalid IL or missing references)
			//IL_043d: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_066b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0664: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d5: Expected O, but got Unknown
			//IL_05f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0601: Unknown result type (might be due to invalid IL or missing references)
			//IL_0613: Unknown result type (might be due to invalid IL or missing references)
			//IL_058c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0808: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0399: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f8: Expected O, but got Unknown
			//IL_0724: Unknown result type (might be due to invalid IL or missing references)
			//IL_0731: Unknown result type (might be due to invalid IL or missing references)
			//IL_073e: Unknown result type (might be due to invalid IL or missing references)
			//IL_077a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0787: Unknown result type (might be due to invalid IL or missing references)
			//IL_07c3: Unknown result type (might be due to invalid IL or missing references)
			Require(old.HasThis == ((MethodReference)current).HasThis, "Changed instance/static contract: " + ((MemberReference)old).FullName);
			if (forwards.TryGetValue(((MemberReference)old).FullName, out var value))
			{
				return value;
			}
			value = new MethodDefinition("Forward" + forwards.Count, (MethodAttributes)22, mod.ImportReference(old.ReturnType));
			bridges.Methods.Add(value);
			forwards.Add(((MemberReference)old).FullName, value);
			if (old.HasThis)
			{
				((MethodReference)value).Parameters.Add(new ParameterDefinition("instance", (ParameterAttributes)0, mod.ImportReference(((MemberReference)old).DeclaringType)));
			}
			Enumerator<ParameterDefinition> enumerator = old.Parameters.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					ParameterDefinition current2 = enumerator.Current;
					((MethodReference)value).Parameters.Add(new ParameterDefinition(((ParameterReference)current2).Name, (ParameterAttributes)0, mod.ImportReference(((ParameterReference)current2).ParameterType)));
				}
			}
			finally
			{
				((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
			ILProcessor iLProcessor = value.Body.GetILProcessor();
			int num = (old.HasThis ? 1 : 0);
			if (((MemberReference)((MemberReference)old).DeclaringType).FullName == "ZDOMan" && ((MemberReference)old).Name == "FindSectorObjects")
			{
				Require(old.HasThis && old.Parameters.Count == 5 && ((MethodReference)current).Parameters.Count == 4, "Unexpected sector query contract");
				Require(((MemberReference)((ParameterReference)((MethodReference)current).Parameters[0]).ParameterType).FullName == "Vector2s" && ((MemberReference)((ParameterReference)((MethodReference)current).Parameters[1]).ParameterType).FullName == "SimulationDistance" && ((MemberReference)((ParameterReference)((MethodReference)current).Parameters[2]).ParameterType).FullName == ((MemberReference)((ParameterReference)old.Parameters[3]).ParameterType).FullName && ((MemberReference)((ParameterReference)((MethodReference)current).Parameters[3]).ParameterType).FullName == ((MemberReference)((ParameterReference)old.Parameters[4]).ParameterType).FullName, "Changed sector query parameter types");
				iLProcessor.Emit(OpCodes.Ldarg, ((MethodReference)value).Parameters[0]);
				iLProcessor.Emit(OpCodes.Ldarg, ((MethodReference)value).Parameters[1]);
				TypeDefinition val = ((ParameterReference)((MethodReference)current).Parameters[0]).ParameterType.Resolve();
				iLProcessor.Emit(OpCodes.Newobj, Import((MethodReference)(object)((IEnumerable<MethodDefinition>)val.Methods).Single((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "Vector2i")));
				iLProcessor.Emit(OpCodes.Ldarg, ((MethodReference)value).Parameters[2]);
				iLProcessor.Emit(OpCodes.Ldarg, ((MethodReference)value).Parameters[3]);
				iLProcessor.Emit(OpCodes.Ldc_I4_0);
				MethodDefinition val2 = ((IEnumerable<MethodDefinition>)((ParameterReference)((MethodReference)current).Parameters[1]).ParameterType.Resolve().Methods).Single((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 3);
				Require(((MethodReference)val2).Parameters[2].HasConstant && object.Equals(((MethodReference)val2).Parameters[2].Constant, false), "Unexpected SimulationDistance default");
				iLProcessor.Emit(OpCodes.Newobj, Import((MethodReference)(object)val2));
				iLProcessor.Emit(OpCodes.Ldarg, ((MethodReference)value).Parameters[4]);
				iLProcessor.Emit(OpCodes.Ldarg, ((MethodReference)value).Parameters[5]);
				iLProcessor.Emit(OpCodes.Callvirt, Import((MethodReference)(object)current));
				iLProcessor.Emit(OpCodes.Ret);
				return value;
			}
			Require(((MethodReference)current).Parameters.Count >= old.Parameters.Count, "Removed API parameters: " + ((MemberReference)old).FullName);
			if (old.HasThis)
			{
				iLProcessor.Emit(OpCodes.Ldarg, ((MethodReference)value).Parameters[0]);
			}
			for (int num2 = 0; num2 < old.Parameters.Count; num2++)
			{
				iLProcessor.Emit(OpCodes.Ldarg, ((MethodReference)value).Parameters[num2 + num]);
				string fullName = ((MemberReference)((ParameterReference)old.Parameters[num2]).ParameterType).FullName;
				string fullName2 = ((MemberReference)((ParameterReference)((MethodReference)current).Parameters[num2]).ParameterType).FullName;
				if (!(fullName == fullName2))
				{
					Require(fullName == "Vector2i" && fullName2 == "Vector2s", "Unsupported parameter conversion " + ((MemberReference)old).FullName);
					TypeDefinition val3 = ((ParameterReference)((MethodReference)current).Parameters[num2]).ParameterType.Resolve();
					iLProcessor.Emit(OpCodes.Newobj, Import((MethodReference)(object)((IEnumerable<MethodDefinition>)val3.Methods).Single((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "Vector2i")));
				}
			}
			for (int num3 = old.Parameters.Count; num3 < ((MethodReference)current).Parameters.Count; num3++)
			{
				ParameterDefinition val4 = ((MethodReference)current).Parameters[num3];
				if (((MemberReference)((ParameterReference)val4).ParameterType).FullName == "System.Boolean" && val4.HasConstant && object.Equals(val4.Constant, false))
				{
					iLProcessor.Emit(OpCodes.Ldc_I4_0);
					continue;
				}
				if (((MemberReference)((ParameterReference)val4).ParameterType).FullName == "ZDOID" && val4.IsOptional)
				{
					VariableDefinition val5 = new VariableDefinition(mod.ImportReference(((ParameterReference)val4).ParameterType));
					value.Body.Variables.Add(val5);
					value.Body.InitLocals = true;
					iLProcessor.Emit(OpCodes.Ldloca, val5);
					iLProcessor.Emit(OpCodes.Initobj, ((VariableReference)val5).VariableType);
					iLProcessor.Emit(OpCodes.Ldloc, val5);
					continue;
				}
				throw new InvalidOperationException("No reviewed default for " + ((MemberReference)current).FullName + " parameter " + ((ParameterReference)val4).Name);
			}
			iLProcessor.Emit(((MethodReference)current).HasThis ? OpCodes.Callvirt : OpCodes.Call, Import((MethodReference)(object)current));
			if (((MemberReference)old.ReturnType).FullName != ((MemberReference)((MethodReference)current).ReturnType).FullName)
			{
				Require(((MemberReference)old.ReturnType).FullName == "Vector2i" && ((MemberReference)((MethodReference)current).ReturnType).FullName == "Vector2s", "Unsupported result conversion " + ((MemberReference)old).FullName);
				VariableDefinition val6 = new VariableDefinition(mod.ImportReference(((MethodReference)current).ReturnType));
				value.Body.Variables.Add(val6);
				value.Body.InitLocals = true;
				TypeDefinition val7 = ((MethodReference)current).ReturnType.Resolve();
				iLProcessor.Emit(OpCodes.Stloc, val6);
				iLProcessor.Emit(OpCodes.Ldloca, val6);
				iLProcessor.Emit(OpCodes.Ldfld, Import((FieldReference)(object)((IEnumerable<FieldDefinition>)val7.Fields).Single((FieldDefinition f) => ((MemberReference)f).Name == "x")));
				iLProcessor.Emit(OpCodes.Ldloca, val6);
				iLProcessor.Emit(OpCodes.Ldfld, Import((FieldReference)(object)((IEnumerable<FieldDefinition>)val7.Fields).Single((FieldDefinition f) => ((MemberReference)f).Name == "y")));
				iLProcessor.Emit(OpCodes.Newobj, Import((MethodReference)(object)((IEnumerable<MethodDefinition>)old.ReturnType.Resolve().Methods).Single((MethodDefinition m) => m.IsConstructor && ((MethodReference)m).Parameters.Count == 2 && ((IEnumerable<ParameterDefinition>)((MethodReference)m).Parameters).All((ParameterDefinition p) => ((MemberReference)((ParameterReference)p).ParameterType).FullName == "System.Int32"))));
			}
			iLProcessor.Emit(OpCodes.Ret);
			return value;
		}

		private int RewriteCalls(Func<MethodReference, MethodDefinition> choose)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (MethodDefinition item in Methods())
			{
				Enumerator<Instruction> enumerator2 = item.Body.Instructions.GetEnumerator();
				try
				{
					while (enumerator2.MoveNext())
					{
						Instruction current = enumerator2.Current;
						object operand = current.Operand;
						MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null);
						if (val != null && (!(current.OpCode != OpCodes.Call) || !(current.OpCode != OpCodes.Callvirt)))
						{
							MethodDefinition val2 = choose(val);
							if (val2 != null)
							{
								current.OpCode = OpCodes.Call;
								current.Operand = Forward(val, val2);
								num++;
							}
						}
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
			}
			return num;
		}

		private int MessageCalls()
		{
			return RewriteCalls((MethodReference r) => (!(((MemberReference)((MemberReference)r).DeclaringType).FullName == "Character") || !(((MemberReference)r).Name == "Message") || r.Parameters.Count != 4) ? null : GameMethod("Character", "Message", 5));
		}

		public void Teleport()
		{
			int num = RewriteCalls(delegate(MethodReference r)
			{
				if (((MemberReference)((MemberReference)r).DeclaringType).FullName == "ZoneSystem" && ((MemberReference)r).Name == "GetZone" && ((MemberReference)r.ReturnType).FullName == "Vector2i")
				{
					return GameMethod("ZoneSystem", "GetZone", 1);
				}
				if (((MemberReference)((MemberReference)r).DeclaringType).FullName == "ZoneSystem" && ((MemberReference)r).Name == "IsZoneLoaded" && ((MemberReference)((ParameterReference)r.Parameters[0]).ParameterType).FullName == "Vector2i")
				{
					return ((IEnumerable<MethodDefinition>)GameType("ZoneSystem").Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == "IsZoneLoaded" && ((MethodReference)m).Parameters.Count == 1 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "Vector2s");
				}
				return (((MemberReference)((MemberReference)r).DeclaringType).FullName == "ZDOMan" && ((MemberReference)r).Name == "FindSectorObjects" && ((MemberReference)((ParameterReference)r.Parameters[0]).ParameterType).FullName == "Vector2i") ? GameMethod("ZDOMan", "FindSectorObjects", 4) : null;
			});
			Require(num == 3, "QuickTeleport expected three sector API calls, found " + num);
		}

		public void Terrain()
		{
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Expected O, but got Unknown
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Expected O, but got Unknown
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Expected O, but got Unknown
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			TypeDefinition val = Type("TerrainTools.Helpers.PreciseTerrainModifier");
			MethodDefinition val2 = ((IEnumerable<MethodDefinition>)val.Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == "PaintClearedPrefix");
			Require(((MethodReference)val2).Parameters.Count == 5 && ((MemberReference)((ParameterReference)((MethodReference)val2).Parameters[2]).ParameterType).FullName == "System.Single", "Unexpected terrain prefix signature");
			MethodDefinition val3 = GameMethod("TerrainComp", "PaintCleared", 3);
			TypeDefinition val4 = ((ParameterReference)((MethodReference)val3).Parameters[2]).ParameterType.Resolve();
			Require(((MemberReference)((ParameterReference)((MethodReference)val3).Parameters[0]).ParameterType).FullName == ((MemberReference)((ParameterReference)((MethodReference)val2).Parameters[1]).ParameterType).FullName && ((MemberReference)val4).FullName == "TerrainOp/Settings", "Changed terrain paint contract");
			Require(!((IEnumerable<CustomAttribute>)val2.CustomAttributes).Where((CustomAttribute a) => ((MemberReference)a.AttributeType).FullName == "HarmonyLib.HarmonyPatch").Any((CustomAttribute a) => ((IEnumerable<CustomAttributeArgument>)a.ConstructorArguments).Any((CustomAttributeArgument v) => ((CustomAttributeArgument)(ref v)).Type.IsArray)), "Unexpected explicit terrain patch argument list");
			((MemberReference)val2).Name = "PaintClearedLegacy141";
			MethodDefinition val5 = new MethodDefinition("PaintClearedPrefix", val2.Attributes, mod.TypeSystem.Boolean);
			CustomAttribute[] array = val2.CustomAttributes.ToArray();
			foreach (CustomAttribute val6 in array)
			{
				val5.CustomAttributes.Add(val6);
				val2.CustomAttributes.Remove(val6);
			}
			((MethodReference)val5).Parameters.Add(new ParameterDefinition("__instance", (ParameterAttributes)0, ((ParameterReference)((MethodReference)val2).Parameters[0]).ParameterType));
			((MethodReference)val5).Parameters.Add(new ParameterDefinition(((ParameterReference)((MethodReference)val3).Parameters[0]).Name, (ParameterAttributes)0, ((ParameterReference)((MethodReference)val2).Parameters[1]).ParameterType));
			((MethodReference)val5).Parameters.Add(new ParameterDefinition(((ParameterReference)((MethodReference)val3).Parameters[2]).Name, (ParameterAttributes)0, mod.ImportReference((TypeReference)(object)val4)));
			val.Methods.Add(val5);
			ILProcessor iLProcessor = val5.Body.GetILProcessor();
			iLProcessor.Emit(OpCodes.Ldarg_0);
			iLProcessor.Emit(OpCodes.Ldarg_1);
			int num2 = 2;
			string[] array2 = new string[3] { "m_paintRadius", "m_paintType", "m_paintHeightCheck" };
			foreach (string name in array2)
			{
				FieldDefinition val7 = ((IEnumerable<FieldDefinition>)val4.Fields).Single((FieldDefinition f) => ((MemberReference)f).Name == name);
				Require(((MemberReference)((FieldReference)val7).FieldType).FullName == ((MemberReference)((ParameterReference)((MethodReference)val2).Parameters[num2++]).ParameterType).FullName, "Changed terrain settings field " + name);
				iLProcessor.Emit(OpCodes.Ldarg_2);
				iLProcessor.Emit(OpCodes.Ldfld, Import((FieldReference)(object)val7));
			}
			iLProcessor.Emit(OpCodes.Call, (MethodReference)(object)val2);
			iLProcessor.Emit(OpCodes.Ret);
			Require(MessageCalls() == 1, "Expected one terrain message API call");
		}

		public void Creature()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			FieldDefinition val = ((IEnumerable<FieldDefinition>)GameType("ZRoutedRpc").Fields).Single((FieldDefinition f) => ((MemberReference)f).Name == "Everybody");
			Require(val.IsLiteral && val.Constant is long, "Unexpected RPC broadcast constant");
			foreach (MethodDefinition item in Methods())
			{
				Enumerator<Instruction> enumerator2 = item.Body.Instructions.GetEnumerator();
				try
				{
					while (enumerator2.MoveNext())
					{
						Instruction current = enumerator2.Current;
						if (current.OpCode == OpCodes.Ldsfld && current.Operand is FieldReference && ((MemberReference)(FieldReference)current.Operand).FullName == "System.Int64 ZRoutedRpc::Everybody")
						{
							current.OpCode = OpCodes.Ldc_I8;
							current.Operand = (long)val.Constant;
							num++;
						}
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
			}
			Require(num == 5, "Expected five embedded broadcast constant loads");
			Require(MessageCalls() == 4, "Expected four creature message calls");
			int num2 = RewriteCalls(delegate(MethodReference r)
			{
				if (((MemberReference)((MemberReference)r).DeclaringType).FullName == "ZDO" && ((MemberReference)r).Name == "GetSector" && ((MemberReference)r.ReturnType).FullName == "Vector2i")
				{
					return GameMethod("ZDO", "GetSector", 0);
				}
				if (((MemberReference)((MemberReference)r).DeclaringType).FullName == "ZoneSystem" && ((MemberReference)r).Name == "GetZonePos" && ((MemberReference)((ParameterReference)r.Parameters[0]).ParameterType).FullName == "Vector2i")
				{
					return GameMethod("ZoneSystem", "GetZonePos", 1);
				}
				if (((MemberReference)((MemberReference)r).DeclaringType).FullName == "EffectList" && ((MemberReference)r).Name == "Create" && r.Parameters.Count == 5)
				{
					return GameMethod("EffectList", "Create", 6);
				}
				return (((MemberReference)((MemberReference)r).DeclaringType).FullName == "Inventory" && ((MemberReference)r).Name == "Changed" && r.Parameters.Count == 0) ? GameMethod("Inventory", "Changed", 2) : null;
			});
			Require(num2 == 13, "Expected thirteen creature sector/effect/inventory API calls, found " + num2);
			int num3 = 0;
			foreach (MethodDefinition item2 in Methods())
			{
				Instruction[] array = item2.Body.Instructions.ToArray();
				foreach (Instruction val2 in array)
				{
					object operand = val2.Operand;
					FieldReference val3 = (FieldReference)((operand is FieldReference) ? operand : null);
					if (val3 != null && !(((MemberReference)((MemberReference)val3).DeclaringType).FullName != "Minimap") && !(((MemberReference)val3).Name != "noForest"))
					{
						Require(val2.OpCode == OpCodes.Ldfld, "Unexpected minimap color access");
						FieldDefinition val4 = ((IEnumerable<FieldDefinition>)GameType("Minimap").Fields).Single((FieldDefinition x) => ((MemberReference)x).Name == "s_noForestColor");
						Require(val4.IsStatic && ((MemberReference)((FieldReference)val4).FieldType).FullName == "UnityEngine.Color", "Minimap replacement must be static Color");
						val2.OpCode = OpCodes.Pop;
						val2.Operand = null;
						item2.Body.GetILProcessor().InsertAfter(val2, Instruction.Create(OpCodes.Ldsfld, Import((FieldReference)(object)val4)));
						num3++;
					}
				}
			}
			Require(num3 == 1, "Expected one minimap color access");
			UpgradeMultiplier("CreatureLevelControl.ItemControl/PatchCookingStationItemMultiplier", "multiplyItemSpawn", "CookingStation", "SpawnItem", 4, "cheated");
			UpgradeMultiplier("CreatureLevelControl.ItemControl/PatchCraftingMultiplier", "multiplyItemAdd", "Inventory", "AddItem", 10, "pickedUp");
		}

		private void UpgradeMultiplier(string typeName, string helperName, string gameType, string gameName, int newCount, string parameterName)
		{
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Expected O, but got Unknown
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Expected O, but got Unknown
			//IL_03c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: Expected O, but got Unknown
			//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Expected O, but got Unknown
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04af: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: 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_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			TypeDefinition val = Type(typeName);
			MethodDefinition val2 = ((IEnumerable<MethodDefinition>)val.Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == helperName);
			MethodDefinition val3 = ((IEnumerable<MethodDefinition>)GameType(gameType).Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == gameName && ((MethodReference)m).Parameters.Count == newCount && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[0]).ParameterType).FullName == "System.String");
			bool flag = gameType == "Inventory";
			Require(string.Join(",", ((IEnumerable<ParameterDefinition>)((MethodReference)val3).Parameters).Select((ParameterDefinition p) => ((MemberReference)((ParameterReference)p).ParameterType).FullName)) == (flag ? "System.String,System.Int32,System.Int32,System.Int32,System.Int64,System.String,Vector2i,System.Boolean,System.Boolean,System.Boolean" : "System.String,System.Int32,UnityEngine.Vector3,System.Boolean"), "Unexpected multiplier API signature");
			Require(((MethodReference)val2).Parameters.Count == (flag ? 8 : 4), "Unexpected multiplier wrapper arity");
			List<ParameterDefinition> list = new List<ParameterDefinition>();
			if (flag)
			{
				ParameterDefinition tail = ((MethodReference)val2).Parameters[7];
				Require(((IEnumerable<Instruction>)val2.Body.Instructions).Count((Instruction i) => i.OpCode == OpCodes.Ldarg_S && i.Operand == tail) == 1, "Unexpected crafting tail use");
				((ParameterReference)tail).Name = "position";
				((ParameterReference)tail).ParameterType = mod.ImportReference(((ParameterReference)((MethodReference)val3).Parameters[6]).ParameterType);
				string[] array = new string[3] { "cheated", "pickedUp", "dropIfFullInv" };
				for (int num = 0; num < array.Length; num++)
				{
					ParameterDefinition val4 = new ParameterDefinition(array[num], (ParameterAttributes)0, mod.TypeSystem.Boolean);
					((MethodReference)val2).Parameters.Add(val4);
					list.Add(val4);
				}
			}
			else
			{
				ParameterDefinition val5 = new ParameterDefinition(parameterName, (ParameterAttributes)0, mod.TypeSystem.Boolean);
				((MethodReference)val2).Parameters.Add(val5);
				list.Add(val5);
			}
			int num2 = 0;
			Instruction[] array2 = val2.Body.Instructions.ToArray();
			foreach (Instruction val6 in array2)
			{
				object operand = val6.Operand;
				MethodReference val7 = (MethodReference)((operand is MethodReference) ? operand : null);
				if (val7 == null || ((MemberReference)((MemberReference)val7).DeclaringType).FullName != gameType || ((MemberReference)val7).Name != gameName)
				{
					continue;
				}
				Require(val7.Parameters.Count == (flag ? 7 : 3), "Unexpected multiplier call");
				OpCode opCode = val6.OpCode;
				val6.OpCode = OpCodes.Ldarg;
				val6.Operand = list[0];
				ILProcessor iLProcessor = val2.Body.GetILProcessor();
				Instruction val8 = val6;
				foreach (ParameterDefinition item in list.Skip(1))
				{
					Instruction val9 = Instruction.Create(OpCodes.Ldarg, item);
					iLProcessor.InsertAfter(val8, val9);
					val8 = val9;
				}
				iLProcessor.InsertAfter(val8, Instruction.Create(opCode, Import((MethodReference)(object)val3)));
				num2++;
			}
			Require(num2 == 1, "Expected one multiplier output call");
			MethodDefinition obj = ((IEnumerable<MethodDefinition>)val.Methods).Single((MethodDefinition m) => m.IsConstructor && m.IsStatic);
			Require(((IEnumerable<Instruction>)obj.Body.Instructions).Count((Instruction i) => i.OpCode == OpCodes.Stsfld) == 2, "Unexpected multiplier initializer side effects");
			obj.Body = new MethodBody(obj);
			ILProcessor iLProcessor2 = obj.Body.GetILProcessor();
			ModuleDefinition mainModule = mod.AssemblyResolver.Resolve((AssemblyNameReference)mod.TypeSystem.CoreLibrary).MainModule;
			MethodReference val10 = mod.ImportReference((MethodReference)(object)((IEnumerable<MethodDefinition>)mainModule.GetType("System.Reflection.MethodBase").Methods).Single((MethodDefinition m) => ((MemberReference)m).Name == "GetMethodFromHandle" && ((MethodReference)m).Parameters.Count == 1));
			TypeReference val11 = mod.ImportReference((TypeReference)(object)mainModule.GetType("System.Reflection.MethodInfo"));
			FieldDefinition val12 = ((IEnumerable<FieldDefinition>)val.Fields).Single((FieldDefinition f) => ((MemberReference)f).Name == ((gameType == "Inventory") ? "ItemAdder" : "ItemSpawner"));
			FieldDefinition val13 = ((IEnumerable<FieldDefinition>)val.Fields).Single((FieldDefinition f) => ((MemberReference)f).Name == ((gameType == "Inventory") ? "ItemAddMultiplier" : "ItemSpawnMultiplier"));
			iLProcessor2.Emit(OpCodes.Ldtoken, Import((MethodReference)(object)val3));
			iLProcessor2.Emit(OpCodes.Call, val10);
			iLProcessor2.Emit(OpCodes.Castclass, val11);
			iLProcessor2.Emit(OpCodes.Stsfld, (FieldReference)(object)val12);
			iLProcessor2.Emit(OpCodes.Ldtoken, (MethodReference)(object)val2);
			iLProcessor2.Emit(OpCodes.Call, val10);
			iLProcessor2.Emit(OpCodes.Castclass, val11);
			iLProcessor2.Emit(OpCodes.Stsfld, (FieldReference)(object)val13);
			iLProcessor2.Emit(OpCodes.Ret);
		}
	}
	internal static class BuildTarget
	{
		public const string Name = "AdvancedTerrainCompatibility";

		public const string TargetFile = "TerrainTools.dll";
	}
}