Decompiled source of SCP3166Fix v1.0.1

BepInEx\patchers\SCP3166Fix\SCP3166Fix.dll

Decompiled 10 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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("v0id")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Preloader compatibility patch for ProjectSCP's SCP3166 in modern Lethal Company versions.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("SCP3166Fix")]
[assembly: AssemblyTitle("SCP3166Fix")]
[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 SCP3166Fix
{
	public static class Patcher
	{
		private static readonly ManualLogSource Logger = Logger.CreateLogSource("SCP3166Fix");

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

		public static void Initialize()
		{
			Logger.LogInfo((object)"Preloader initialized. Scanning for ProjectSCP.SCP3166.dll...");
			try
			{
				string pluginPath = Paths.PluginPath;
				if (Directory.Exists(pluginPath))
				{
					string[] files = Directory.GetFiles(pluginPath, "ProjectSCP.SCP3166.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 == "ProjectSCP.SCP3166")
			{
				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_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: 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_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: 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("EnemyAI");
				MethodDefinition val3 = ((type != null) ? ((IEnumerable<MethodDefinition>)type.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == "PlayerIsTargetable" && ((MethodReference)m).Parameters.Count == 4)) : null);
				TypeDefinition type2 = mainModule.GetType("EntranceTeleport");
				FieldDefinition val4 = ((type2 != null) ? ((IEnumerable<FieldDefinition>)type2.Fields).FirstOrDefault((Func<FieldDefinition, bool>)((FieldDefinition f) => ((MemberReference)f).Name == "entrancePoint")) : null);
				if (val3 == null || val4 == 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);
				FieldReference operand2 = mainModule2.ImportReference((FieldReference)(object)val4);
				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();
								foreach (Instruction val5 in array)
								{
									if (val5.Operand == null)
									{
										continue;
									}
									string? text2 = val5.Operand.ToString();
									if (text2.Contains("PlayerIsTargetable"))
									{
										object operand3 = val5.Operand;
										MethodReference val6 = (MethodReference)((operand3 is MethodReference) ? operand3 : null);
										if (val6 != null && val6.Parameters.Count == 3)
										{
											Instruction val7 = Instruction.Create(OpCodes.Ldc_I4_0);
											iLProcessor.InsertBefore(val5, val7);
											val5.Operand = operand;
											num++;
										}
									}
									if (text2.Contains("EntranceTeleport::exitPoint"))
									{
										val5.Operand = operand2;
										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();
			}
		}
	}
}