Decompiled source of Warfare Valheim 1 0 Compatibility Fix v1.0.1

BepInEx/patchers/Warfare_Valheim_1_0_Compatibility_Fix.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using BepInEx;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ADARC.WarfareCompatibility;

public static class WarfareCompatibilityPatcher
{
	private const string SupportedAssemblyName = "Warfare";

	private const string SupportedAssemblyVersion = "1.8.9.0";

	private const string OriginalSha256 = "DF6EBC8616C9D239B8FA4A22875D45984962F87B4AECA88F982895707B4D9F5A";

	private const string PatchedSha256 = "DA26B3E9E74BEFD9FEDBAE5E2C202923405544AD513B74D3A0353211318A8339";

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

	public static void Initialize()
	{
		string pluginPath = Paths.PluginPath;
		if (!Directory.Exists(pluginPath))
		{
			throw new InvalidOperationException("The active BepInEx profile plugin directory was not found. Warfare was not modified.");
		}
		string[] files = Directory.GetFiles(pluginPath, "Warfare.dll", SearchOption.AllDirectories);
		if (files.Length != 1)
		{
			throw new InvalidOperationException("Expected exactly one installed Warfare.dll but found " + files.Length + ". No file was modified.");
		}
		string text = files[0];
		string text2 = Sha256(text);
		if (text2 == "DA26B3E9E74BEFD9FEDBAE5E2C202923405544AD513B74D3A0353211318A8339")
		{
			Console.WriteLine("[Warfare 1.0 Compatibility Fix] Verified existing patched Warfare 1.8.9 DLL.");
			return;
		}
		if (text2 != "DF6EBC8616C9D239B8FA4A22875D45984962F87B4AECA88F982895707B4D9F5A")
		{
			throw new InvalidOperationException("Installed Warfare.dll is not the verified original Warfare 1.8.9 build. Expected SHA-256 DF6EBC8616C9D239B8FA4A22875D45984962F87B4AECA88F982895707B4D9F5A but found " + text2 + ". No file was modified.");
		}
		string text3 = text + ".before-valheim-1.0-fix";
		if (!File.Exists(text3))
		{
			File.Copy(text, text3, overwrite: false);
		}
		if (Sha256(text3) != "DF6EBC8616C9D239B8FA4A22875D45984962F87B4AECA88F982895707B4D9F5A")
		{
			throw new InvalidOperationException("The existing Warfare backup is not an exact original. No file was modified.");
		}
		string text4 = text + ".valheim-1.0-fix.tmp";
		if (File.Exists(text4))
		{
			File.Delete(text4);
		}
		try
		{
			AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(text);
			try
			{
				Patch(ref assembly);
				assembly.Write(text4);
			}
			finally
			{
				assembly.Dispose();
			}
			string text5 = Sha256(text4);
			if (text5 != "DA26B3E9E74BEFD9FEDBAE5E2C202923405544AD513B74D3A0353211318A8339")
			{
				throw new InvalidOperationException("Patched Warfare output failed its exact SHA-256 check. Expected DA26B3E9E74BEFD9FEDBAE5E2C202923405544AD513B74D3A0353211318A8339 but found " + text5 + ". The installed DLL was left unchanged.");
			}
			File.Replace(text4, text, null);
			if (Sha256(text) != "DA26B3E9E74BEFD9FEDBAE5E2C202923405544AD513B74D3A0353211318A8339")
			{
				throw new InvalidOperationException("Warfare replacement did not verify after writing.");
			}
			Console.WriteLine("[Warfare 1.0 Compatibility Fix] Installed and verified the guarded Warfare 1.8.9 repair. Original backup: " + text3);
		}
		finally
		{
			if (File.Exists(text4))
			{
				File.Delete(text4);
			}
		}
	}

	public static void Patch(ref AssemblyDefinition assembly)
	{
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0312: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_035e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0365: Expected O, but got Unknown
		//IL_037a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0384: Expected O, but got Unknown
		//IL_01c0: 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_01d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_03fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0417: Expected O, but got Unknown
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0211: Unknown result type (might be due to invalid IL or missing references)
		//IL_0232: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_0467: Unknown result type (might be due to invalid IL or missing references)
		//IL_047f: Unknown result type (might be due to invalid IL or missing references)
		//IL_049a: Expected O, but got Unknown
		//IL_050a: Unknown result type (might be due to invalid IL or missing references)
		//IL_050f: Unknown result type (might be due to invalid IL or missing references)
		//IL_052c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0544: Unknown result type (might be due to invalid IL or missing references)
		//IL_0560: Expected O, but got Unknown
		if (assembly == null || ((AssemblyNameReference)assembly.Name).Name != "Warfare" || ((AssemblyNameReference)assembly.Name).Version.ToString() != "1.8.9.0")
		{
			throw new InvalidOperationException("Warfare 1.0 Compatibility Fix supports only the original Warfare 1.8.9 assembly. The installed Warfare DLL was not modified.");
		}
		List<Instruction> list = new List<Instruction>();
		List<Instruction> list2 = new List<Instruction>();
		List<Instruction> list3 = new List<Instruction>();
		List<Instruction> list4 = new List<Instruction>();
		List<Instruction> list5 = new List<Instruction>();
		Dictionary<Instruction, MethodDefinition> dictionary = new Dictionary<Instruction, MethodDefinition>();
		foreach (TypeDefinition item in AllTypes((IEnumerable<TypeDefinition>)assembly.MainModule.Types))
		{
			Enumerator<MethodDefinition> enumerator2 = item.Methods.GetEnumerator();
			try
			{
				while (enumerator2.MoveNext())
				{
					MethodDefinition current2 = enumerator2.Current;
					if (!current2.HasBody)
					{
						continue;
					}
					Enumerator<Instruction> enumerator3 = current2.Body.Instructions.GetEnumerator();
					try
					{
						while (enumerator3.MoveNext())
						{
							Instruction current3 = enumerator3.Current;
							dictionary[current3] = current2;
							object operand = current3.Operand;
							FieldReference val = (FieldReference)((operand is FieldReference) ? operand : null);
							if (current3.OpCode == OpCodes.Ldsfld && val != null && ((MemberReference)((MemberReference)val).DeclaringType).FullName == "ZRoutedRpc" && ((MemberReference)val).Name == "Everybody" && ((MemberReference)val.FieldType).FullName == "System.Int64")
							{
								list.Add(current3);
								continue;
							}
							if (current3.OpCode == OpCodes.Ldfld && val != null && ((MemberReference)((MemberReference)val).DeclaringType).FullName == "PieceTable" && ((MemberReference)val).Name == "m_availablePieces")
							{
								list5.Add(current3);
								continue;
							}
							object operand2 = current3.Operand;
							MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null);
							if (val2 != null)
							{
								if ((current3.OpCode == OpCodes.Call || current3.OpCode == OpCodes.Callvirt) && IsOldStatusEffectCall(val2))
								{
									list2.Add(current3);
								}
								else if ((current3.OpCode == OpCodes.Call || current3.OpCode == OpCodes.Callvirt) && IsOldCharacterMessage(val2))
								{
									list3.Add(current3);
								}
								else if (current3.OpCode == OpCodes.Newobj && IsOldConsoleConstructor(val2))
								{
									list4.Add(current3);
								}
							}
						}
					}
					finally
					{
						((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose();
					}
				}
			}
			finally
			{
				((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
			}
		}
		RequireCount("ZRoutedRpc.Everybody references", list.Count, 4);
		RequireCount("SEMan.AddStatusEffect calls", list2.Count, 33);
		RequireCount("Character.Message calls", list3.Count, 1);
		RequireCount("Terminal.ConsoleCommand constructors", list4.Count, 1);
		RequireCount("PieceTable.m_availablePieces references", list5.Count, 5);
		foreach (Instruction item2 in list)
		{
			item2.OpCode = OpCodes.Ldc_I8;
			item2.Operand = 0L;
		}
		foreach (Instruction item3 in list5)
		{
			FieldReference val3 = (FieldReference)item3.Operand;
			item3.Operand = (object)new FieldReference("m_availablePiecesByCategory", val3.FieldType, ((MemberReference)val3).DeclaringType);
		}
		foreach (Instruction item4 in list2)
		{
			MethodDefinition val4 = dictionary[item4];
			ILProcessor iLProcessor = val4.Body.GetILProcessor();
			iLProcessor.InsertBefore(item4, iLProcessor.Create(OpCodes.Ldc_I4_0));
			iLProcessor.InsertBefore(item4, iLProcessor.Create(OpCodes.Conv_I2));
			item4.Operand = AppendParameter((MethodReference)item4.Operand, assembly.MainModule.TypeSystem.Int16);
		}
		foreach (Instruction item5 in list3)
		{
			MethodDefinition val5 = dictionary[item5];
			ILProcessor iLProcessor2 = val5.Body.GetILProcessor();
			iLProcessor2.InsertBefore(item5, iLProcessor2.Create(OpCodes.Ldc_I4_0));
			item5.Operand = AppendParameter((MethodReference)item5.Operand, assembly.MainModule.TypeSystem.Boolean);
		}
		foreach (Instruction item6 in list4)
		{
			MethodDefinition val6 = dictionary[item6];
			ILProcessor iLProcessor3 = val6.Body.GetILProcessor();
			Instruction val7 = item6;
			for (int i = 0; i < 4; i++)
			{
				val7 = val7.Previous;
			}
			if (val7 == null || val7.OpCode != OpCodes.Ldnull)
			{
				throw new InvalidOperationException("The Warfare console-command call shape was not recognized.");
			}
			iLProcessor3.InsertBefore(val7, iLProcessor3.Create(OpCodes.Ldc_I4_0));
			item6.Operand = InsertParameter((MethodReference)item6.Operand, 8, assembly.MainModule.TypeSystem.Boolean);
		}
		Console.WriteLine("[Warfare 1.0 Compatibility Fix] Applied 44 guarded method repairs to original Warfare 1.8.9.");
	}

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

	private static bool IsOldStatusEffectCall(MethodReference method)
	{
		if (((MemberReference)((MemberReference)method).DeclaringType).FullName == "SEMan" && ((MemberReference)method).Name == "AddStatusEffect")
		{
			return HasParameters(method, "System.Int32", "System.Boolean", "System.Int32", "System.Single");
		}
		return false;
	}

	private static bool IsOldCharacterMessage(MethodReference method)
	{
		if (((MemberReference)((MemberReference)method).DeclaringType).FullName == "Character" && ((MemberReference)method).Name == "Message")
		{
			return HasParameters(method, "MessageHud/MessageType", "System.String", "System.Int32", "UnityEngine.Sprite");
		}
		return false;
	}

	private static bool IsOldConsoleConstructor(MethodReference method)
	{
		if (((MemberReference)((MemberReference)method).DeclaringType).FullName == "Terminal/ConsoleCommand" && ((MemberReference)method).Name == ".ctor")
		{
			return HasParameters(method, "System.String", "System.String", "Terminal/ConsoleEvent", "System.Boolean", "System.Boolean", "System.Boolean", "System.Boolean", "System.Boolean", "Terminal/ConsoleOptionsFetcher", "System.Boolean", "System.Boolean", "System.Boolean");
		}
		return false;
	}

	private static bool HasParameters(MethodReference method, params string[] types)
	{
		if (method.Parameters.Count != types.Length)
		{
			return false;
		}
		for (int i = 0; i < types.Length; i++)
		{
			if (((MemberReference)((ParameterReference)method.Parameters[i]).ParameterType).FullName != types[i])
			{
				return false;
			}
		}
		return true;
	}

	private static MethodReference AppendParameter(MethodReference source, TypeReference parameterType)
	{
		return InsertParameter(source, source.Parameters.Count, parameterType);
	}

	private static MethodReference InsertParameter(MethodReference source, int index, TypeReference parameterType)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		MethodReference val = new MethodReference(((MemberReference)source).Name, source.ReturnType, ((MemberReference)source).DeclaringType);
		val.HasThis = source.HasThis;
		val.ExplicitThis = source.ExplicitThis;
		val.CallingConvention = source.CallingConvention;
		MethodReference val2 = val;
		for (int i = 0; i <= source.Parameters.Count; i++)
		{
			if (i == index)
			{
				val2.Parameters.Add(new ParameterDefinition(parameterType));
			}
			if (i < source.Parameters.Count)
			{
				val2.Parameters.Add(new ParameterDefinition(((ParameterReference)source.Parameters[i]).ParameterType));
			}
		}
		return val2;
	}

	private static void RequireCount(string repair, int actual, int expected)
	{
		if (actual != expected)
		{
			throw new InvalidOperationException("Warfare 1.0 Compatibility Fix expected " + expected + " " + repair + " in original Warfare 1.8.9 but found " + actual + ". Nothing was patched.");
		}
	}

	private static string Sha256(string path)
	{
		using FileStream inputStream = File.OpenRead(path);
		using SHA256 sHA = SHA256.Create();
		return BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "");
	}
}