Decompiled source of FacelessStalkerFix v1.0.0

BepInEx\patchers\FacelessStalkerFix\FacelessStalkerFix.dll

Decompiled a week 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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("v0id")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Preloader compatibility patch fixing MissingMethodException crashes in Sparble's FacelessStalker for modern Lethal Company versions.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FacelessStalkerFix")]
[assembly: AssemblyTitle("FacelessStalkerFix")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 FacelessStalkerFix
{
	public static class Patcher
	{
		private static readonly ManualLogSource Logger = Logger.CreateLogSource("FacelessStalkerFix");

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

		public static void Initialize()
		{
			Logger.LogInfo((object)"Preloader initialized. Scanning for SlendermanMod.dll...");
			try
			{
				string text = null;
				try
				{
					text = Paths.PluginPath;
				}
				catch
				{
				}
				if (!string.IsNullOrEmpty(text) && Directory.Exists(text))
				{
					string[] files = Directory.GetFiles(text, "SlendermanMod.dll", SearchOption.AllDirectories);
					for (int i = 0; i < files.Length; i++)
					{
						TryPatchFile(files[i]);
					}
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Error during scan/patch: {arg}");
			}
		}

		public static void Patch(AssemblyDefinition assembly)
		{
			if (((AssemblyNameReference)assembly.Name).Name == "SlendermanMod")
			{
				PatchAssembly(assembly, null);
			}
		}

		public static void Finish()
		{
		}

		public static bool TryPatchFile(string filePath)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			try
			{
				string text = null;
				try
				{
					text = Paths.ManagedPath;
				}
				catch
				{
				}
				if (string.IsNullOrEmpty(text) || !Directory.Exists(text))
				{
					string text2 = "S:\\SteamLibrary\\steamapps\\common\\Lethal Company\\Lethal Company_Data\\Managed";
					if (Directory.Exists(text2))
					{
						text = text2;
					}
				}
				DefaultAssemblyResolver val = new DefaultAssemblyResolver();
				if (!string.IsNullOrEmpty(text) && Directory.Exists(text))
				{
					((BaseAssemblyResolver)val).AddSearchDirectory(text);
				}
				try
				{
					if (!string.IsNullOrEmpty(Paths.BepInExAssemblyDirectory) && Directory.Exists(Paths.BepInExAssemblyDirectory))
					{
						((BaseAssemblyResolver)val).AddSearchDirectory(Paths.BepInExAssemblyDirectory);
					}
				}
				catch
				{
				}
				string directoryName = Path.GetDirectoryName(filePath);
				if (!string.IsNullOrEmpty(directoryName))
				{
					((BaseAssemblyResolver)val).AddSearchDirectory(directoryName);
				}
				using MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(filePath));
				ReaderParameters val2 = new ReaderParameters
				{
					AssemblyResolver = (IAssemblyResolver)(object)val
				};
				AssemblyDefinition val3 = AssemblyDefinition.ReadAssembly((Stream)memoryStream, val2);
				try
				{
					if (PatchAssembly(val3, (IAssemblyResolver?)(object)val))
					{
						string text3 = filePath + ".orig";
						if (!File.Exists(text3))
						{
							File.Copy(filePath, text3, overwrite: false);
						}
						using MemoryStream memoryStream2 = new MemoryStream();
						val3.Write((Stream)memoryStream2);
						File.WriteAllBytes(filePath, memoryStream2.ToArray());
						Logger.LogInfo((object)("Successfully patched and updated: " + filePath));
						return true;
					}
					Logger.LogInfo((object)(filePath + " is already compatible / patched."));
				}
				finally
				{
					((IDisposable)val3)?.Dispose();
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to patch {filePath}: {arg}");
			}
			return false;
		}

		private static bool PatchAssembly(AssemblyDefinition asm, IAssemblyResolver? resolver)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: 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_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0364: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0416: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cd: 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)
			string text = null;
			try
			{
				text = Paths.ManagedPath;
			}
			catch
			{
			}
			if (string.IsNullOrEmpty(text) || !Directory.Exists(text))
			{
				string text2 = "S:\\SteamLibrary\\steamapps\\common\\Lethal Company\\Lethal Company_Data\\Managed";
				if (Directory.Exists(text2))
				{
					text = text2;
				}
			}
			string text3 = Path.Combine(text ?? string.Empty, "Assembly-CSharp.dll");
			if (!File.Exists(text3))
			{
				Logger.LogWarning((object)("Assembly-CSharp.dll not found at " + text3));
				return false;
			}
			ReaderParameters val = new ReaderParameters
			{
				AssemblyResolver = resolver
			};
			AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(text3, val);
			try
			{
				ModuleDefinition mainModule = val2.MainModule;
				TypeDefinition type = mainModule.GetType("GameNetcodeStuff.PlayerControllerB");
				MethodDefinition val3 = ((type != null) ? ((IEnumerable<MethodDefinition>)type.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == "HasLineOfSightToPosition" && ((MethodReference)m).Parameters.Count == 5)) : null);
				MethodDefinition val4 = ((type != null) ? ((IEnumerable<MethodDefinition>)type.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == "KillPlayer" && ((MethodReference)m).Parameters.Count == 6)) : null);
				TypeDefinition type2 = mainModule.GetType("EnemyAI");
				MethodDefinition val5 = ((type2 != null) ? ((IEnumerable<MethodDefinition>)type2.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == "EnableEnemyMesh" && ((MethodReference)m).Parameters.Count == 3)) : null);
				MethodDefinition val6 = ((type2 != null) ? ((IEnumerable<MethodDefinition>)type2.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == "PlayerIsTargetable" && ((MethodReference)m).Parameters.Count == 4)) : null);
				MethodDefinition val7 = ((type2 != null) ? ((IEnumerable<MethodDefinition>)type2.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == "ChooseFarthestNodeFromPosition" && ((MethodReference)m).Parameters.Count == 6 && ((MemberReference)((ParameterReference)((MethodReference)m).Parameters[5]).ParameterType).FullName == "System.Int32")) : null);
				TypeDefinition type3 = mainModule.GetType("EntranceTeleport");
				FieldDefinition val8 = ((type3 != null) ? ((IEnumerable<FieldDefinition>)type3.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "entrancePoint")) : null);
				if (val3 == null || val4 == null)
				{
					Logger.LogWarning((object)"Failed to resolve required PlayerControllerB methods (HasLineOfSightToPosition / KillPlayer) from Assembly-CSharp.dll");
					return false;
				}
				ModuleDefinition mainModule2 = asm.MainModule;
				MethodReference operand = mainModule2.ImportReference((MethodReference)(object)val3);
				MethodReference operand2 = mainModule2.ImportReference((MethodReference)(object)val4);
				MethodReference val9 = ((val5 != null) ? mainModule2.ImportReference((MethodReference)(object)val5) : null);
				MethodReference val10 = ((val6 != null) ? mainModule2.ImportReference((MethodReference)(object)val6) : null);
				MethodReference val11 = ((val7 != null) ? mainModule2.ImportReference((MethodReference)(object)val7) : null);
				FieldReference val12 = ((val8 != null) ? mainModule2.ImportReference((FieldReference)(object)val8) : null);
				int num = 0;
				Enumerator<TypeDefinition> enumerator = mainModule2.Types.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						Enumerator<MethodDefinition> enumerator2 = enumerator.Current.Methods.GetEnumerator();
						try
						{
							while (enumerator2.MoveNext())
							{
								MethodDefinition current = enumerator2.Current;
								if (!current.HasBody)
								{
									continue;
								}
								ILProcessor iLProcessor = current.Body.GetILProcessor();
								Instruction[] array = current.Body.Instructions.ToArray();
								for (int num2 = 0; num2 < array.Length; num2++)
								{
									Instruction val13 = array[num2];
									if (val13.Operand == null)
									{
										continue;
									}
									string text4 = val13.Operand.ToString();
									if (text4.Contains("HasLineOfSightToPosition"))
									{
										object operand3 = val13.Operand;
										MethodReference val14 = (MethodReference)((operand3 is MethodReference) ? operand3 : null);
										if (val14 != null && val14.Parameters.Count == 4)
										{
											Instruction val15 = Instruction.Create(OpCodes.Ldc_I4_M1);
											iLProcessor.InsertBefore(val13, val15);
											val13.Operand = operand;
											num++;
										}
									}
									if (text4.Contains("KillPlayer"))
									{
										object operand4 = val13.Operand;
										MethodReference val16 = (MethodReference)((operand4 is MethodReference) ? operand4 : null);
										if (val16 != null && val16.Parameters.Count == 5)
										{
											Instruction val17 = Instruction.Create(OpCodes.Ldc_I4_0);
											iLProcessor.InsertBefore(val13, val17);
											val13.Operand = operand2;
											num++;
										}
									}
									if (val9 != null && text4.Contains("EnableEnemyMesh"))
									{
										object operand5 = val13.Operand;
										MethodReference val18 = (MethodReference)((operand5 is MethodReference) ? operand5 : null);
										if (val18 != null && val18.Parameters.Count == 2)
										{
											Instruction val19 = Instruction.Create(OpCodes.Ldc_I4_0);
											iLProcessor.InsertBefore(val13, val19);
											val13.Operand = val9;
											num++;
										}
									}
									if (val10 != null && text4.Contains("PlayerIsTargetable"))
									{
										object operand6 = val13.Operand;
										MethodReference val20 = (MethodReference)((operand6 is MethodReference) ? operand6 : null);
										if (val20 != null && val20.Parameters.Count == 3)
										{
											Instruction val21 = Instruction.Create(OpCodes.Ldc_I4_0);
											iLProcessor.InsertBefore(val13, val21);
											val13.Operand = val10;
											num++;
										}
									}
									if (val12 != null && text4.Contains("EntranceTeleport::exitPoint"))
									{
										val13.Operand = val12;
										num++;
									}
									if (val11 == null || !text4.Contains("ChooseFarthestNodeFromPosition"))
									{
										continue;
									}
									object operand7 = val13.Operand;
									MethodReference val22 = (MethodReference)((operand7 is MethodReference) ? operand7 : null);
									if (val22 != null && val22.Parameters.Count == 6 && ((MemberReference)((ParameterReference)val22.Parameters[5]).ParameterType).FullName == "System.Boolean")
									{
										if (num2 > 0 && ((object)array[num2 - 1].OpCode/*cast due to .constrained prefix*/).ToString().StartsWith("ldc.i4"))
										{
											array[num2 - 1].OpCode = OpCodes.Ldc_I4_M1;
											array[num2 - 1].Operand = null;
										}
										val13.Operand = val11;
										num++;
									}
								}
							}
						}
						finally
						{
							((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
						}
					}
				}
				finally
				{
					((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
				return num > 0;
			}
			finally
			{
				((IDisposable)val2)?.Dispose();
			}
		}
	}
}