Decompiled source of HauntedHarpistFix v1.0.1

BepInEx\patchers\HauntedHarpistFix\HauntedHarpistFix.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using 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 for Bob123's Haunted Harpist in modern Lethal Company versions.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("HauntedHarpistFix")]
[assembly: AssemblyTitle("HauntedHarpistFix")]
[assembly: AssemblyVersion("1.0.1.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 HauntedHarpistFix
{
	public static class Patcher
	{
		private static readonly ManualLogSource Logger = Logger.CreateLogSource("HauntedHarpistFix");

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

		public static void Initialize()
		{
			Logger.LogInfo((object)"Preloader initialized. Scanning for LethalCompanyHarpGhost.dll...");
			try
			{
				string pluginPath = Paths.PluginPath;
				if (Directory.Exists(pluginPath))
				{
					string[] files = Directory.GetFiles(pluginPath, "LethalCompanyHarpGhost.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 == "LethalCompanyHarpGhost")
			{
				PatchAssembly(assembly, null);
			}
		}

		public static void Finish()
		{
		}

		public static bool TryPatchFile(string filePath)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_003e: 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_004b: Expected O, but got Unknown
			try
			{
				DefaultAssemblyResolver val = new DefaultAssemblyResolver();
				((BaseAssemblyResolver)val).AddSearchDirectory(Paths.ManagedPath);
				((BaseAssemblyResolver)val).AddSearchDirectory(Paths.BepInExAssemblyDirectory);
				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 text = filePath + ".orig";
						if (!File.Exists(text))
						{
							File.Copy(filePath, text, 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.Combine(Paths.ManagedPath, "Assembly-CSharp.dll");
			if (!File.Exists(text))
			{
				Logger.LogWarning((object)("Assembly-CSharp.dll not found at " + text));
				return false;
			}
			ReaderParameters val = new ReaderParameters
			{
				AssemblyResolver = resolver
			};
			AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(text, 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);
				TypeDefinition type2 = mainModule.GetType("EnemyAI");
				MethodDefinition val4 = ((type2 != null) ? ((IEnumerable<MethodDefinition>)type2.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == "EnableEnemyMesh" && ((MethodReference)m).Parameters.Count == 3)) : null);
				MethodDefinition val5 = ((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 val6 = ((type3 != null) ? ((IEnumerable<FieldDefinition>)type3.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "entrancePoint")) : null);
				if (val3 == null || val4 == null || val5 == null || val6 == null)
				{
					Logger.LogWarning((object)"Failed to resolve required modern target symbols 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 operand3 = mainModule2.ImportReference((MethodReference)(object)val5);
				FieldReference operand4 = mainModule2.ImportReference((FieldReference)(object)val6);
				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 val7 = array[num2];
									if (val7.Operand == null)
									{
										continue;
									}
									string? text2 = val7.Operand.ToString();
									if (text2.Contains("HasLineOfSightToPosition"))
									{
										object operand5 = val7.Operand;
										MethodReference val8 = (MethodReference)((operand5 is MethodReference) ? operand5 : null);
										if (val8 != null && val8.Parameters.Count == 4)
										{
											Instruction val9 = Instruction.Create(OpCodes.Ldc_I4_M1);
											iLProcessor.InsertBefore(val7, val9);
											val7.Operand = operand;
											num++;
										}
									}
									if (text2.Contains("EnableEnemyMesh"))
									{
										object operand6 = val7.Operand;
										MethodReference val10 = (MethodReference)((operand6 is MethodReference) ? operand6 : null);
										if (val10 != null && val10.Parameters.Count == 2)
										{
											Instruction val11 = Instruction.Create(OpCodes.Ldc_I4_0);
											iLProcessor.InsertBefore(val7, val11);
											val7.Operand = operand2;
											num++;
										}
									}
									if (text2.Contains("EntranceTeleport::exitPoint"))
									{
										val7.Operand = operand4;
										num++;
									}
									if (!text2.Contains("ChooseFarthestNodeFromPosition"))
									{
										continue;
									}
									object operand7 = val7.Operand;
									MethodReference val12 = (MethodReference)((operand7 is MethodReference) ? operand7 : null);
									if (val12 != null && val12.Parameters.Count == 6 && ((MemberReference)((ParameterReference)val12.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;
										}
										val7.Operand = operand3;
										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();
			}
		}
	}
}