Decompiled source of OldModCompat v1.0.0

patchers/OldModCompat.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
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: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("LegacyCompat")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LegacyCompat")]
[assembly: AssemblyTitle("LegacyCompat")]
[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;
		}
	}
}
public static class LegacyCompat
{
	private static readonly ManualLogSource Log = Logger.CreateLogSource("LegacyCompat");

	private static readonly (string Type, string Old, int Params, string New)[] Calls = new(string, string, int, string)[5]
	{
		("MenuManager", "DisplayMenuNotification", 2, "DisplayMenuNotification"),
		("GameNetcodeStuff.PlayerControllerB", "KillPlayer", 5, "KillPlayer"),
		("GameNetcodeStuff.PlayerControllerB", "HasLineOfSightToPosition", 4, "HasLineOfSightToPosition"),
		("GameNetcodeStuff.PlayerControllerB", "DropAllHeldItemsServerRpc", 0, "DropAllHeldItemsAndSyncNonexact"),
		("SpawnableEnemyWithRarity", ".ctor", 0, ".ctor")
	};

	public static IEnumerable<string> TargetDLLs { get; } = new string[1] { "Assembly-CSharp.dll" };

	public static void Initialize()
	{
		FixPlugins(Paths.PluginPath, Paths.ManagedPath);
	}

	public static void Patch(AssemblyDefinition assembly)
	{
		ForwardDunGen(assembly.MainModule);
	}

	public static void ForwardDunGen(ModuleDefinition module)
	{
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Expected O, but got Unknown
		int num = 0;
		foreach (AssemblyNameReference item in ((IEnumerable<AssemblyNameReference>)module.AssemblyReferences).Where((AssemblyNameReference r) => r.Name.StartsWith("DunGen")).ToList())
		{
			Enumerator<TypeDefinition> enumerator2 = module.AssemblyResolver.Resolve(item).MainModule.Types.GetEnumerator();
			try
			{
				while (enumerator2.MoveNext())
				{
					TypeDefinition type = enumerator2.Current;
					if (!(((MemberReference)type).Name == "<Module>") && module.GetType(((MemberReference)type).FullName) == null && !((IEnumerable<ExportedType>)module.ExportedTypes).Any((ExportedType e) => e.FullName == ((MemberReference)type).FullName))
					{
						module.ExportedTypes.Add(new ExportedType(((TypeReference)type).Namespace, ((MemberReference)type).Name, module, (IMetadataScope)(object)item)
						{
							IsForwarder = true
						});
						num++;
					}
				}
			}
			finally
			{
				((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
			}
		}
		Log.LogInfo((object)$"Forwarded {num} DunGen types");
	}

	public static void FixPlugins(string pluginPath, string managedPath)
	{
		ModuleDefinition val = ModuleDefinition.ReadModule(Path.Combine(managedPath, "Assembly-CSharp.dll"));
		try
		{
			Dictionary<(string, string, int), MethodDefinition> dictionary = new Dictionary<(string, string, int), MethodDefinition>();
			(string, string, int, string)[] calls = Calls;
			for (int i = 0; i < calls.Length; i++)
			{
				(string Type, string Old, int Params, string New) c = calls[i];
				TypeDefinition type = val.GetType(c.Type);
				MethodDefinition val2 = ((type != null) ? ((IEnumerable<MethodDefinition>)type.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => (((MemberReference)m).Name == c.New && ((MethodReference)m).Parameters.Count > c.Params) || (((MemberReference)m).Name == c.New && c.Old != c.New && ((MethodReference)m).Parameters.Count == c.Params))) : null);
				if (val2 != null && ((IEnumerable<ParameterDefinition>)((MethodReference)val2).Parameters).Skip(c.Params).All(CanFill))
				{
					dictionary[(c.Type, c.Old, c.Params)] = val2;
					continue;
				}
				Log.LogWarning((object)(c.Type + "." + c.New + ": v80 target not found, not rewriting " + c.Old));
			}
			string[] files = Directory.GetFiles(pluginPath, "*.dll", SearchOption.AllDirectories);
			foreach (string text in files)
			{
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				string text2 = directorySeparatorChar.ToString();
				directorySeparatorChar = Path.DirectorySeparatorChar;
				if (!text.Contains(text2 + "MMHOOK" + directorySeparatorChar))
				{
					try
					{
						FixPlugin(text, dictionary);
					}
					catch (Exception ex)
					{
						Log.LogError((object)("Could not fix " + Path.GetFileName(text) + ": " + ex.Message));
					}
				}
			}
		}
		finally
		{
			((IDisposable)val)?.Dispose();
		}
	}

	private static void FixPlugin(string path, Dictionary<(string, string, int), MethodDefinition> targets)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		byte[] buffer = File.ReadAllBytes(path);
		ModuleDefinition val;
		try
		{
			val = ModuleDefinition.ReadModule((Stream)new MemoryStream(buffer), new ReaderParameters
			{
				ReadingMode = (ReadingMode)2
			});
		}
		catch
		{
			return;
		}
		ModuleDefinition val2 = val;
		try
		{
			if (!val.GetMemberReferences().Any(delegate(MemberReference r)
			{
				MethodReference val3 = (MethodReference)(object)((r is MethodReference) ? r : null);
				return val3 != null && targets.ContainsKey((((MemberReference)((MemberReference)val3).DeclaringType).FullName, ((MemberReference)val3).Name, val3.Parameters.Count));
			}) && !val.GetMemberReferences().Any(IsDarkenField))
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			foreach (MethodDefinition item in from m in val.GetTypes().SelectMany((TypeDefinition t) => (IEnumerable<MethodDefinition>)t.Methods)
				where m.HasBody
				select m)
			{
				num += RewriteCalls(val, item, targets);
				num2 += RemoveDarkenScreen(item);
			}
			if (num + num2 != 0)
			{
				val.Write(path);
				Log.LogInfo((object)$"Rewrote {num} old call(s), {num2} removed-field use(s) in {Path.GetFileName(path)}");
			}
		}
		finally
		{
			((IDisposable)val2)?.Dispose();
		}
	}

	private static bool IsIntLike(TypeReference t)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Invalid comparison between Unknown and I4
		if ((int)t.MetadataType != 2)
		{
			return (int)t.MetadataType == 8;
		}
		return true;
	}

	private static bool CanFill(ParameterDefinition p)
	{
		if (!p.HasConstant && ((ParameterReference)p).ParameterType.IsValueType)
		{
			return IsIntLike(((ParameterReference)p).ParameterType);
		}
		return true;
	}

	private static TypeReference Import(ModuleDefinition module, TypeReference t)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Invalid comparison between Unknown and I4
		if ((int)t.MetadataType != 2)
		{
			if ((int)t.MetadataType != 8)
			{
				return module.ImportReference(t);
			}
			return module.TypeSystem.Int32;
		}
		return module.TypeSystem.Boolean;
	}

	private static int RewriteCalls(ModuleDefinition module, MethodDefinition method, Dictionary<(string, string, int), MethodDefinition> targets)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: 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_00d6: 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_00f2: Expected O, but got Unknown
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Expected O, but got Unknown
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Expected O, but got Unknown
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
		int num = 0;
		ILProcessor iLProcessor = method.Body.GetILProcessor();
		foreach (Instruction item in ((IEnumerable<Instruction>)method.Body.Instructions).ToList())
		{
			object operand = item.Operand;
			MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null);
			if (val == null || (item.OpCode != OpCodes.Call && item.OpCode != OpCodes.Callvirt && item.OpCode != OpCodes.Newobj) || !targets.TryGetValue((((MemberReference)((MemberReference)val).DeclaringType).FullName, ((MemberReference)val).Name, val.Parameters.Count), out var value))
			{
				continue;
			}
			MethodReference val2 = new MethodReference(((MemberReference)value).Name, val.ReturnType, ((MemberReference)val).DeclaringType)
			{
				HasThis = val.HasThis,
				ExplicitThis = val.ExplicitThis,
				CallingConvention = val.CallingConvention
			};
			Enumerator<ParameterDefinition> enumerator2 = val.Parameters.GetEnumerator();
			try
			{
				while (enumerator2.MoveNext())
				{
					ParameterDefinition current2 = enumerator2.Current;
					val2.Parameters.Add(new ParameterDefinition(((ParameterReference)current2).ParameterType));
				}
			}
			finally
			{
				((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
			}
			List<ParameterDefinition> list = ((IEnumerable<ParameterDefinition>)((MethodReference)value).Parameters).Skip(val.Parameters.Count).ToList();
			foreach (ParameterDefinition item2 in list)
			{
				val2.Parameters.Add(new ParameterDefinition(Import(module, ((ParameterReference)item2).ParameterType)));
			}
			Instruction val3 = iLProcessor.Create(item.OpCode, val2);
			if (list.Count == 0)
			{
				item.Operand = val2;
				num++;
				continue;
			}
			Instruction val4 = Fill(iLProcessor, list[0]);
			item.OpCode = val4.OpCode;
			item.Operand = val4.Operand;
			Instruction val5 = item;
			foreach (ParameterDefinition item3 in list.Skip(1))
			{
				Instruction val6 = Fill(iLProcessor, item3);
				iLProcessor.InsertAfter(val5, val6);
				val5 = val6;
			}
			iLProcessor.InsertAfter(val5, val3);
			num++;
		}
		return num;
	}

	private static Instruction Fill(ILProcessor il, ParameterDefinition p)
	{
		//IL_001f: 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_0067: 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)
		if (p.HasConstant && p.Constant is bool flag)
		{
			return il.Create(OpCodes.Ldc_I4, flag ? 1 : 0);
		}
		if (p.HasConstant && p.Constant is int num)
		{
			return il.Create(OpCodes.Ldc_I4, num);
		}
		if (IsIntLike(((ParameterReference)p).ParameterType))
		{
			return il.Create(OpCodes.Ldc_I4_0);
		}
		return il.Create(OpCodes.Ldnull);
	}

	private static bool IsDarkenField(MemberReference r)
	{
		FieldReference val = (FieldReference)(object)((r is FieldReference) ? r : null);
		if (val != null && ((MemberReference)val).Name == "loadingDarkenScreen")
		{
			return ((MemberReference)((MemberReference)val).DeclaringType).FullName == "HUDManager";
		}
		return false;
	}

	private static int RemoveDarkenScreen(MethodDefinition method)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: 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)
		int num = 0;
		Collection<Instruction> instructions = method.Body.Instructions;
		for (int i = 0; i + 2 < instructions.Count; i++)
		{
			if (instructions[i].OpCode == OpCodes.Ldfld && IsDarkenField((MemberReference)/*isinst with value type is only supported in some contexts*/) && (instructions[i + 1].OpCode == OpCodes.Ldc_I4_0 || instructions[i + 1].OpCode == OpCodes.Ldc_I4_1) && instructions[i + 2].OpCode == OpCodes.Callvirt)
			{
				object operand = instructions[i + 2].Operand;
				MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null);
				if (val != null && ((MemberReference)val).Name == "set_enabled")
				{
					instructions[i].OpCode = OpCodes.Pop;
					instructions[i].Operand = null;
					instructions[i + 2].OpCode = OpCodes.Pop;
					instructions[i + 2].Operand = null;
					num++;
				}
			}
		}
		return num;
	}
}