Decompiled source of PierceBugFix v1.2.1

BepInEx/plugins/PierceBugFix/PierceBugFix.dll

Decompiled 4 months ago
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Gear;
using HarmonyLib;
using Iced.Intel;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Runtime;
using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class;
using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo;
using Microsoft.CodeAnalysis;
using PierceBugFix.Imports;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("PierceBugFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c7b2b13c68d5e8a5a8d5c100e6f6591f27dd62f8")]
[assembly: AssemblyProduct("PierceBugFix")]
[assembly: AssemblyTitle("PierceBugFix")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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;
		}
	}
}
namespace PierceBugFix
{
	internal static class Logger
	{
		private static ManualLogSource m_LogSource;

		public static void SetupFromInit(ManualLogSource logSource)
		{
			m_LogSource = logSource;
		}

		private static string Format(object data)
		{
			return data.ToString();
		}

		public static void Debug(object msg)
		{
			m_LogSource.LogDebug((object)Format(msg));
		}

		public static void Info(object msg)
		{
			m_LogSource.LogInfo((object)Format(msg));
		}

		public static void Warn(object msg)
		{
			m_LogSource.LogWarning((object)Format(msg));
		}

		public static void Error(object msg)
		{
			m_LogSource.LogError((object)Format(msg));
		}

		public static void Fatal(object msg)
		{
			m_LogSource.LogFatal((object)Format(msg));
		}
	}
	internal struct MemoryProtectionCookie : IDisposable
	{
		private Kernel32.MemoryProtectionConstant m_OldProtection;

		private readonly IntPtr m_Address;

		private readonly IntPtr m_Size;

		public MemoryProtectionCookie(IntPtr address, Kernel32.MemoryProtectionConstant newProtection, IntPtr size)
		{
			m_Address = address;
			m_Size = size;
			if (!Kernel32.VirtualProtect(m_Address, m_Size, newProtection, out m_OldProtection))
			{
				Environment.FailFast($"Failed to protect address 0x{m_Address:X2} with protection {newProtection}");
			}
			Logger.Debug($"MemoryProtectionCookie: Modified protection of address 0x{m_Address:X2} from {m_OldProtection} to {newProtection}");
		}

		public void Dispose()
		{
			Kernel32.MemoryProtectionConstant oldProtection = m_OldProtection;
			if (!Kernel32.VirtualProtect(m_Address, m_Size, m_OldProtection, out m_OldProtection))
			{
				Environment.FailFast($"Failed to revert memory protection of address 0x{m_Address:X2}");
			}
			Logger.Debug($"MemoryProtectionCookie: Reverted protection of address 0x{m_Address:X2} to {oldProtection}");
		}
	}
	public class PierceBugDisassembler
	{
		public unsafe static IntPtr FindInc(IntPtr methodPointer, int index, int totalExpected, int widthExpected)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Invalid comparison between Unknown and I4
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Invalid comparison between Unknown and I4
			IntPtr intPtr = IntPtr.Zero;
			StreamCodeReader val = new StreamCodeReader((Stream)new UnmanagedMemoryStream((byte*)(void*)methodPointer, 65536L, 65536L, FileAccess.Read));
			Decoder val2 = Decoder.Create(sizeof(void*) * 8, (CodeReader)(object)val, (DecoderOptions)0);
			val2.IP = (ulong)(long)methodPointer;
			Instruction val3 = default(Instruction);
			val2.Decode(ref val3);
			int num = 0;
			while ((int)((Instruction)(ref val3)).Mnemonic != 1620)
			{
				if ((int)((Instruction)(ref val3)).Mnemonic == 279)
				{
					num++;
					if (num == index)
					{
						intPtr = (IntPtr)(long)((Instruction)(ref val3)).IP;
						if (((Instruction)(ref val3)).NextIP - ((Instruction)(ref val3)).IP != (ulong)widthExpected)
						{
							Logger.Error("PierceBugFix found an instruction with an unexpected width, this probably means the method has changed in some way and we should avoid changing it.");
							Environment.FailFast("PierceBugFix found an instruction with an unexpected width, this probably means the method has changed in some way and we should avoid changing it.");
						}
					}
				}
				val2.Decode(ref val3);
			}
			val.Stream.Dispose();
			if (num != totalExpected)
			{
				Logger.Error("PierceBugFix didn't find the correct number of `Inc` instructions, this probably means the method has changed in some way and we should avoid changing it.");
				Environment.FailFast("PierceBugFix didn't find the correct number of `Inc` instructions, this probably means the method has changed in some way and we should avoid changing it.");
			}
			if (intPtr == IntPtr.Zero)
			{
				Logger.Error("PierceBugFix found a zero instruction int pointer for our `Inc` instruction, something has gone terribly wrong.");
				Environment.FailFast("PierceBugFix found a zero instruction int pointer for our `Inc` instruction, something has gone terribly wrong.");
			}
			return intPtr;
		}
	}
	[HarmonyPatch]
	internal static class PierceSpreadPatch
	{
		[HarmonyTargetMethod]
		private static MethodBase FindWeaponRayFunc(Harmony harmony)
		{
			return AccessTools.Method(typeof(Weapon), "CastWeaponRay", new Type[4]
			{
				typeof(Transform),
				typeof(WeaponHitData).MakeByRefType(),
				typeof(Vector3),
				typeof(int)
			}, (Type[])null);
		}

		[HarmonyPostfix]
		private static void PostRayCallback(ref WeaponHitData weaponRayData, bool __result)
		{
			weaponRayData.randomSpread = 0f;
			weaponRayData.angOffsetX = 0f;
			weaponRayData.angOffsetY = 0f;
			if (!__result)
			{
				weaponRayData.maxRayDist = 0f;
			}
		}
	}
	[BepInPlugin("tru0067.PierceBugFix", "PierceBugFix", "1.0.0")]
	public class Plugin : BasePlugin
	{
		internal const string GUID = "tru0067.PierceBugFix";

		public const int NOP = 144;

		public override void Load()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Logger.SetupFromInit(((BasePlugin)this).Log);
			Logger.Info("PierceBugFix is loading...");
			PatchBulletWeaponFire<BulletWeapon>(3, 4, 3);
			PatchBulletWeaponFire<Shotgun>(3, 5, 3);
			new Harmony("tru0067.PierceBugFix").PatchAll();
			Logger.Info("PierceBugFix is loaded!");
		}

		private unsafe void PatchBulletWeaponFire<T>(int incIndex, int incTotalExpected, int incWidthExpected) where T : BulletWeapon
		{
			string name = typeof(T).Name;
			Logger.Info("Patching `" + name + ".Fire`...");
			INativeClassStruct val = UnityVersionHandler.Wrap((Il2CppClass*)(void*)Il2CppClassPointerStore<T>.NativeClassPtr);
			for (int i = 0; i < val.MethodCount; i++)
			{
				INativeMethodInfoStruct val2 = UnityVersionHandler.Wrap(val.Methods[i]);
				if (!(Marshal.PtrToStringAnsi(val2.Name) == "Fire"))
				{
					continue;
				}
				IntPtr intPtr = PierceBugDisassembler.FindInc(val2.MethodPointer, incIndex, incTotalExpected, incWidthExpected);
				using (new MemoryProtectionCookie(intPtr, Kernel32.MemoryProtectionConstant.ExecuteReadWrite, new IntPtr(16)))
				{
					for (int j = 0; j < incWidthExpected; j++)
					{
						*(sbyte*)((long)intPtr + (long)new IntPtr(j)) = -112;
					}
				}
				Logger.Info("`" + name + ".Fire` is patched!");
				return;
			}
			Logger.Error("PierceBugFix failed to find `" + name + ".Fire`");
			Environment.FailFast("PierceBugFix failed to find `" + name + ".Fire`");
		}
	}
}
namespace PierceBugFix.Imports
{
	internal static class Kernel32
	{
		[Flags]
		public enum MemoryAllocationConstant : uint
		{
			Commit = 0x1000u,
			Reserve = 0x2000u,
			Reset = 0x80000u,
			ResetUndo = 0x1000000u,
			LargePages = 0x20000000u,
			Physical = 0x400000u,
			TopDown = 0x100000u,
			WriteWatch = 0x200000u
		}

		[Flags]
		public enum MemoryProtectionConstant : uint
		{
			NoAccess = 1u,
			ReadOnly = 2u,
			ReadWrite = 4u,
			WriteCopy = 8u,
			Execute = 0x10u,
			ExecuteRead = 0x20u,
			ExecuteReadWrite = 0x40u,
			ExecuteWriteCopy = 0x80u
		}

		[DllImport("kernel32.dll")]
		public static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, MemoryAllocationConstant flAllocationType, MemoryProtectionConstant flProtect);

		[DllImport("kernel32.dll")]
		public static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, MemoryProtectionConstant flNewProtect, out MemoryProtectionConstant lpflOldProtect);
	}
}