Decompiled source of VStacks v1.0.1

BepInEx/plugins/VStack/VStack.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
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.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BepInEx.Unity.IL2CPP.Hook;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("VStack")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+06f3890683ddfc10d0abc9a09202c5cd19cba0c5")]
[assembly: AssemblyProduct("VStack")]
[assembly: AssemblyTitle("VStack")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 VStack
{
	[BepInPlugin("com.originera.vstack", "VStack", "1.0.1")]
	public sealed class Plugin : BasePlugin
	{
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate ushort SettingsClampHalfDelegate(float value, float min, float max, IntPtr fieldName);

		private sealed class ExternalSettingOverride
		{
			public string OwnerGuid { get; }

			public string SettingName { get; }

			public Func<float> MultiplierProvider { get; }

			public bool LoggedFirstIntercept { get; set; }

			public bool LoggedProviderFailure { get; set; }

			public bool LoggedInvalidValue { get; set; }

			public ExternalSettingOverride(string ownerGuid, string settingName, Func<float> multiplierProvider)
			{
				OwnerGuid = ownerGuid;
				SettingName = settingName;
				MultiplierProvider = multiplierProvider;
			}
		}

		private enum BurstPatchRole
		{
			None,
			Client,
			Server
		}

		private sealed class BurstMemoryPatch
		{
			public IntPtr Address { get; }

			public byte[] OriginalBytes { get; }

			public byte[] ReplacementBytes { get; }

			public string Description { get; }

			private BurstMemoryPatch(IntPtr address, byte[] originalBytes, byte[] replacementBytes, string description)
			{
				Address = address;
				OriginalBytes = originalBytes;
				ReplacementBytes = replacementBytes;
				Description = description;
			}

			public static BurstMemoryPatch Int32(IntPtr address, int originalValue, int replacementValue, string description)
			{
				return new BurstMemoryPatch(address, BitConverter.GetBytes(originalValue), BitConverter.GetBytes(replacementValue), description);
			}

			public static BurstMemoryPatch Byte(IntPtr address, byte originalValue, byte replacementValue, string description)
			{
				return new BurstMemoryPatch(address, new byte[1] { originalValue }, new byte[1] { replacementValue }, description);
			}
		}

		public const string PluginGuid = "com.originera.vstack";

		public const string PluginName = "VStack";

		public const string PluginVersion = "1.0.1";

		public const float DefaultMultiplier = 1000f;

		public const float MinimumMultiplier = 0.01f;

		public const float MaximumMultiplier = 65504f;

		private const string TargetSettingName = "InventoryStacksModifier";

		private static readonly object ExternalOverrideLock = new object();

		private static ExternalSettingOverride[] _externalSettingOverrides = Array.Empty<ExternalSettingOverride>();

		private const uint ImageScnMemExecute = 536870912u;

		private const uint PageExecuteReadWrite = 64u;

		private const int VanillaInventoryWireMaximum = 4095;

		private const int ExtendedInventoryWireMaximum = int.MaxValue;

		private static readonly byte[] Pattern = new byte[15]
		{
			64, 87, 72, 131, 236, 96, 128, 61, 0, 0,
			0, 0, 0, 73, 139
		};

		private static readonly bool[] PatternMask = new bool[15]
		{
			true, true, true, true, true, true, true, true, false, false,
			false, false, false, true, true
		};

		private const int VanillaInventoryBitWidth = 12;

		private const int ExtendedInventoryBitWidth = 31;

		private static readonly byte[] BurstServerClampPatternA = new byte[24]
		{
			129, 249, 255, 15, 0, 0, 184, 255, 15, 0,
			0, 15, 77, 200, 133, 201, 184, 0, 0, 0,
			0, 15, 78, 200
		};

		private static readonly byte[] BurstServerClampPatternB = new byte[28]
		{
			65, 129, 255, 255, 15, 0, 0, 184, 255, 15,
			0, 0, 68, 15, 77, 248, 69, 133, 255, 184,
			0, 0, 0, 0, 68, 15, 78, 248
		};

		private static readonly bool[] BurstServerClampPatternAMask = CreateAllTrueMask(BurstServerClampPatternA.Length);

		private static readonly bool[] BurstServerClampPatternBMask = CreateAllTrueMask(BurstServerClampPatternB.Length);

		private static readonly byte[] BurstClientFirstReadPattern = new byte[29]
		{
			72, 139, 15, 139, 87, 8, 72, 131, 236, 32,
			65, 184, 12, 0, 0, 0, 232, 0, 0, 0,
			0, 72, 131, 196, 32, 131, 71, 20, 12
		};

		private static readonly bool[] BurstClientFirstReadPatternMask = new bool[29]
		{
			true, true, true, true, true, true, true, true, true, true,
			true, true, true, true, true, true, true, false, false, false,
			false, true, true, true, true, true, true, true, true
		};

		private static readonly byte[] BurstClientSecondReadPattern = new byte[31]
		{
			72, 131, 236, 32, 65, 184, 12, 0, 0, 0,
			232, 0, 0, 0, 0, 72, 131, 196, 32, 68,
			139, 79, 20, 65, 131, 193, 12, 68, 137, 79,
			20
		};

		private static readonly bool[] BurstClientSecondReadPatternMask = new bool[31]
		{
			true, true, true, true, true, true, true, true, true, true,
			true, false, false, false, false, true, true, true, true, true,
			true, true, true, true, true, true, true, true, true, true,
			true
		};

		private static readonly byte[] BurstBitWidthMovEdx12 = new byte[5] { 186, 12, 0, 0, 0 };

		private static readonly bool[] BurstBitWidthMovEdx12Mask = CreateAllTrueMask(BurstBitWidthMovEdx12.Length);

		private const int ExpectedBurstServerClampMatchesPerShape = 6;

		private const int ExpectedBurstClientDecoderPairs = 6;

		private const int BurstClientFirstReadWidthImmediateOffset = 12;

		private const int BurstClientFirstReadBitPositionImmediateOffset = 28;

		private const int BurstClientSecondReadWidthImmediateOffset = 6;

		private const int BurstClientSecondReadBitPositionImmediateOffset = 26;

		private const int BurstClientFirstReadSearchBack = 512;

		private ConfigFile? _vStackConfig;

		private ConfigEntry<float>? _multiplier;

		private INativeDetour? _detour;

		private SettingsClampHalfDelegate? _original;

		private SettingsClampHalfDelegate? _detourDelegate;

		private bool _loggedFirstIntercept;

		private bool _loggedInvalidConfig;

		private readonly List<BurstMemoryPatch> _burstWirePatches = new List<BurstMemoryPatch>();

		private BurstPatchRole _burstPatchRole;

		[DllImport("kernel32.dll", SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool VirtualProtect(IntPtr address, UIntPtr size, uint newProtect, out uint oldProtect);

		[DllImport("kernel32.dll", SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool FlushInstructionCache(IntPtr process, IntPtr baseAddress, UIntPtr size);

		[DllImport("kernel32.dll")]
		private static extern IntPtr GetCurrentProcess();

		public override void Load()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Expected O, but got Unknown
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Expected O, but got Unknown
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Expected O, but got Unknown
			_vStackConfig = new ConfigFile(Path.Combine(Paths.ConfigPath, "VStack.cfg"), true);
			_multiplier = _vStackConfig.Bind<float>("Stacks", "Multiplier", 1000f, "Inventory stack multiplier. Default: 1000. Examples: 10, 100, 500, 1000, 5000. Valid positive range: 0.01 to 65504. Restart the game/server after editing this text file.");
			bool flag = default(bool);
			try
			{
				IntPtr intPtr = FindSettingsClampHalf();
				if (intPtr == IntPtr.Zero)
				{
					((BasePlugin)this).Log.LogError((object)"SettingsClamp::Half signature was not found. The game may have updated; stack multiplier hook was not applied.");
				}
				else
				{
					SettingsClampHalfDelegate settingsClampHalfDelegate = (_detourDelegate = SettingsClampHalfDetour);
					SettingsClampHalfDelegate original = default(SettingsClampHalfDelegate);
					_detour = INativeDetour.CreateAndApply<SettingsClampHalfDelegate>(intPtr, settingsClampHalfDelegate, ref original);
					_original = original;
					ManualLogSource log = ((BasePlugin)this).Log;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(37, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("VStack");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("1.0.1");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded (managed-only BepInEx hook).");
					}
					log.LogInfo(val);
					ManualLogSource log2 = ((BasePlugin)this).Log;
					val = new BepInExInfoLogInterpolatedStringHandler(33, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Hooked SettingsClamp::Half at 0x");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<long>(intPtr.ToInt64(), "X");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
					}
					log2.LogInfo(val);
					ManualLogSource log3 = ((BasePlugin)this).Log;
					val = new BepInExInfoLogInterpolatedStringHandler(43, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Target setting: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("InventoryStacksModifier");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; configured multiplier: x");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(GetMultiplier(), "0.###");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
					}
					log3.LogInfo(val);
					ManualLogSource log4 = ((BasePlugin)this).Log;
					val = new BepInExInfoLogInterpolatedStringHandler(13, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Config file: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_vStackConfig.ConfigFilePath);
					}
					log4.LogInfo(val);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log5 = ((BasePlugin)this).Log;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(46, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Failed to install SettingsClamp::Half detour: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				log5.LogError(val2);
			}
			try
			{
				_burstPatchRole = InstallBurstInventoryWirePatch();
				ManualLogSource log6 = ((BasePlugin)this).Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(67, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Burst InventoryBuffer extended-count patch enabled as ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<BurstPatchRole>(_burstPatchRole);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("(");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(12);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" -> ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(31);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" bits).");
				}
				log6.LogInfo(val);
			}
			catch (Exception value)
			{
				try
				{
					RestoreBurstInventoryWirePatch();
				}
				catch (Exception ex2)
				{
					ManualLogSource log7 = ((BasePlugin)this).Log;
					BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(48, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Error while rolling back Burst inventory patch: ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex2);
					}
					log7.LogWarning(val3);
				}
				_burstPatchRole = BurstPatchRole.None;
				((BasePlugin)this).Log.LogError((object)("Failed to install the Burst InventoryBuffer extended-count patch. The stack multiplier hook can still work, but inventory counts above 4095 may remain visually capped. " + $"Details: {value}"));
			}
		}

		public static bool RegisterSettingOverride(string ownerGuid, string settingName, Func<float> multiplierProvider)
		{
			if (string.IsNullOrWhiteSpace(ownerGuid) || string.IsNullOrWhiteSpace(settingName) || multiplierProvider == null || string.Equals(settingName, "InventoryStacksModifier", StringComparison.Ordinal))
			{
				return false;
			}
			lock (ExternalOverrideLock)
			{
				List<ExternalSettingOverride> list = new List<ExternalSettingOverride>(_externalSettingOverrides.Length + 1);
				ExternalSettingOverride[] externalSettingOverrides = _externalSettingOverrides;
				foreach (ExternalSettingOverride externalSettingOverride in externalSettingOverrides)
				{
					if (!string.Equals(externalSettingOverride.OwnerGuid, ownerGuid, StringComparison.Ordinal) || !string.Equals(externalSettingOverride.SettingName, settingName, StringComparison.Ordinal))
					{
						list.Add(externalSettingOverride);
					}
				}
				list.Add(new ExternalSettingOverride(ownerGuid, settingName, multiplierProvider));
				_externalSettingOverrides = list.ToArray();
			}
			return true;
		}

		public static int UnregisterSettingOverrides(string ownerGuid)
		{
			if (string.IsNullOrWhiteSpace(ownerGuid))
			{
				return 0;
			}
			lock (ExternalOverrideLock)
			{
				List<ExternalSettingOverride> list = new List<ExternalSettingOverride>(_externalSettingOverrides.Length);
				int num = 0;
				ExternalSettingOverride[] externalSettingOverrides = _externalSettingOverrides;
				foreach (ExternalSettingOverride externalSettingOverride in externalSettingOverrides)
				{
					if (string.Equals(externalSettingOverride.OwnerGuid, ownerGuid, StringComparison.Ordinal))
					{
						num++;
					}
					else
					{
						list.Add(externalSettingOverride);
					}
				}
				if (num != 0)
				{
					_externalSettingOverrides = list.ToArray();
				}
				return num;
			}
		}

		public override bool Unload()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			bool flag = default(bool);
			try
			{
				RestoreBurstInventoryWirePatch();
				_burstPatchRole = BurstPatchRole.None;
			}
			catch (Exception ex)
			{
				ManualLogSource log = ((BasePlugin)this).Log;
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(56, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Error while restoring Burst InventoryBuffer wire patch: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogWarning(val);
			}
			try
			{
				lock (ExternalOverrideLock)
				{
					_externalSettingOverrides = Array.Empty<ExternalSettingOverride>();
				}
				((IDisposable)_detour)?.Dispose();
				_detour = null;
				_original = null;
				_detourDelegate = null;
				_multiplier = null;
				_vStackConfig = null;
			}
			catch (Exception ex2)
			{
				ManualLogSource log2 = ((BasePlugin)this).Log;
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(36, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Error while removing native detour: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex2);
				}
				log2.LogWarning(val);
			}
			return true;
		}

		private ushort SettingsClampHalfDetour(float value, float min, float max, IntPtr fieldName)
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Expected O, but got Unknown
			SettingsClampHalfDelegate original = _original;
			if (original == null)
			{
				return 0;
			}
			bool flag = default(bool);
			if (IsIl2CppStringEqual(fieldName, "InventoryStacksModifier"))
			{
				float multiplier = GetMultiplier();
				value = multiplier;
				min = 0f;
				max = multiplier;
				if (!_loggedFirstIntercept)
				{
					_loggedFirstIntercept = true;
					ManualLogSource log = ((BasePlugin)this).Log;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(29, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("InventoryStacksModifier");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" intercepted and forced to x");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(multiplier, "0.###");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
					}
					log.LogInfo(val);
				}
			}
			else
			{
				ExternalSettingOverride externalSettingOverride = FindExternalSettingOverride(fieldName);
				if (externalSettingOverride != null)
				{
					float num;
					try
					{
						num = externalSettingOverride.MultiplierProvider();
					}
					catch (Exception ex)
					{
						if (!externalSettingOverride.LoggedProviderFailure)
						{
							externalSettingOverride.LoggedProviderFailure = true;
							ManualLogSource log2 = ((BasePlugin)this).Log;
							BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(80, 3, ref flag);
							if (flag)
							{
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("External setting provider '");
								((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(externalSettingOverride.OwnerGuid);
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' for ");
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("'");
								((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(externalSettingOverride.SettingName);
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' failed: ");
								((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(". Passing the vanilla value through.");
							}
							log2.LogWarning(val2);
						}
						return original(value, min, max, fieldName);
					}
					if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f)
					{
						if (!externalSettingOverride.LoggedInvalidValue)
						{
							externalSettingOverride.LoggedInvalidValue = true;
							ManualLogSource log3 = ((BasePlugin)this).Log;
							BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(102, 3, ref flag);
							if (flag)
							{
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("External setting provider '");
								((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(externalSettingOverride.OwnerGuid);
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' returned invalid multiplier ");
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("'");
								((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(num);
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' for '");
								((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(externalSettingOverride.SettingName);
								((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("'. Passing the vanilla value through.");
							}
							log3.LogWarning(val2);
						}
						return original(value, min, max, fieldName);
					}
					num = Math.Min(num, 65504f);
					value = num;
					min = 0f;
					max = num;
					if (!externalSettingOverride.LoggedFirstIntercept)
					{
						externalSettingOverride.LoggedFirstIntercept = true;
						ManualLogSource log4 = ((BasePlugin)this).Log;
						BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(35, 3, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("External setting override [");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(externalSettingOverride.OwnerGuid);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(externalSettingOverride.SettingName);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" -> x");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(num, "0.###");
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
						}
						log4.LogInfo(val);
					}
				}
			}
			return original(value, min, max, fieldName);
		}

		private BurstPatchRole InstallBurstInventoryWirePatch()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			RestoreBurstInventoryWirePatch();
			ProcessModule burstGeneratedModule = GetBurstGeneratedModule();
			List<IntPtr> list = FindExecutablePatternMatches(burstGeneratedModule, BurstServerClampPatternA, BurstServerClampPatternAMask);
			List<IntPtr> list2 = FindExecutablePatternMatches(burstGeneratedModule, BurstServerClampPatternB, BurstServerClampPatternBMask);
			List<IntPtr> list3 = FindExecutablePatternMatches(burstGeneratedModule, BurstClientSecondReadPattern, BurstClientSecondReadPatternMask);
			bool flag = list.Count == 6 && list2.Count == 6;
			bool flag2 = list.Count == 0 && list2.Count == 0 && list3.Count == 6;
			bool flag3 = default(bool);
			if (flag)
			{
				InstallBurstServerSerializerPatch(list, list2);
				ManualLogSource log = ((BasePlugin)this).Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(86, 2, ref flag3);
				if (flag3)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Burst server inventory serializer patch verified: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(list.Count + list2.Count);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" inventory fields, ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_burstWirePatches.Count);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" immediate edits.");
				}
				log.LogInfo(val);
				return BurstPatchRole.Server;
			}
			if (flag2)
			{
				InstallBurstClientDeserializerPatch(list3);
				ManualLogSource log2 = ((BasePlugin)this).Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(88, 2, ref flag3);
				if (flag3)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Burst client inventory deserializer patch verified: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(list3.Count * 2);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" inventory fields, ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_burstWirePatches.Count);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" immediate edits.");
				}
				log2.LogInfo(val);
				return BurstPatchRole.Client;
			}
			throw new InvalidOperationException($"Unrecognized lib_burst_generated.dll layout. Server clamp A={list.Count}, server clamp B={list2.Count}, client second-read={list3.Count}. Refusing to patch.");
		}

		private void InstallBurstServerSerializerPatch(List<IntPtr> clampShapeA, List<IntPtr> clampShapeB)
		{
			List<BurstMemoryPatch> list = new List<BurstMemoryPatch>(60);
			foreach (IntPtr item in clampShapeA)
			{
				AddBurstServerFieldPatch(list, item, 2, 7, "shape-A");
			}
			foreach (IntPtr item2 in clampShapeB)
			{
				AddBurstServerFieldPatch(list, item2, 3, 8, "shape-B");
			}
			if (list.Count != 60)
			{
				throw new InvalidOperationException($"Expected 60 Burst server immediate edits, generated {list.Count}. Refusing to patch.");
			}
			ValidateAndApplyBurstPatches(list);
		}

		private void AddBurstServerFieldPatch(List<BurstMemoryPatch> candidates, IntPtr clamp, int firstMaximumImmediateOffset, int secondMaximumImmediateOffset, string shapeName)
		{
			List<IntPtr> list = FindPatternMatchesInRange(IntPtr.Add(clamp, -48), 48, BurstBitWidthMovEdx12, BurstBitWidthMovEdx12Mask);
			if (list.Count != 1)
			{
				throw new InvalidOperationException($"Burst server {shapeName} field at 0x{clamp.ToInt64():X}: expected exactly one pre-write 12-bit width, found {list.Count}.");
			}
			List<IntPtr> list2 = FindPatternMatchesInRange(clamp, 112, BurstBitWidthMovEdx12, BurstBitWidthMovEdx12Mask);
			if (list2.Count != 1)
			{
				throw new InvalidOperationException($"Burst server {shapeName} field at 0x{clamp.ToInt64():X}: expected exactly one writer 12-bit width, found {list2.Count}.");
			}
			IntPtr address = FindBurstServerBitPositionImmediate(clamp);
			candidates.Add(BurstMemoryPatch.Int32(IntPtr.Add(list[0], 1), 12, 31, "Burst server " + shapeName + " pre-write bit width"));
			candidates.Add(BurstMemoryPatch.Int32(IntPtr.Add(clamp, firstMaximumImmediateOffset), 4095, int.MaxValue, "Burst server " + shapeName + " compare maximum"));
			candidates.Add(BurstMemoryPatch.Int32(IntPtr.Add(clamp, secondMaximumImmediateOffset), 4095, int.MaxValue, "Burst server " + shapeName + " clamp maximum"));
			candidates.Add(BurstMemoryPatch.Int32(IntPtr.Add(list2[0], 1), 12, 31, "Burst server " + shapeName + " writer bit width"));
			candidates.Add(BurstMemoryPatch.Byte(address, 12, 31, "Burst server " + shapeName + " bit-position advance"));
		}

		private static IntPtr FindBurstServerBitPositionImmediate(IntPtr clamp)
		{
			byte[][] array = new byte[3][]
			{
				new byte[5] { 65, 131, 70, 24, 12 },
				new byte[4] { 131, 70, 24, 12 },
				new byte[4] { 131, 71, 24, 12 }
			};
			HashSet<IntPtr> hashSet = new HashSet<IntPtr>();
			byte[][] array2 = array;
			foreach (byte[] array3 in array2)
			{
				bool[] mask = CreateAllTrueMask(array3.Length);
				List<IntPtr> list = FindPatternMatchesInRange(clamp, 112, array3, mask);
				foreach (IntPtr item in list)
				{
					int offset = array3.Length - 1;
					hashSet.Add(IntPtr.Add(item, offset));
				}
			}
			if (hashSet.Count != 1)
			{
				throw new InvalidOperationException($"Burst server field at 0x{clamp.ToInt64():X}: expected exactly one 12-bit bit-position advance, found {hashSet.Count}.");
			}
			using (HashSet<IntPtr>.Enumerator enumerator2 = hashSet.GetEnumerator())
			{
				if (enumerator2.MoveNext())
				{
					return enumerator2.Current;
				}
			}
			throw new InvalidOperationException("Unreachable Burst bit-position lookup state.");
		}

		private void InstallBurstClientDeserializerPatch(List<IntPtr> secondReads)
		{
			List<BurstMemoryPatch> list = new List<BurstMemoryPatch>(24);
			secondReads.Sort((IntPtr left, IntPtr right) => left.ToInt64().CompareTo(right.ToInt64()));
			for (int num = 0; num < secondReads.Count; num++)
			{
				IntPtr pointer = secondReads[num];
				List<IntPtr> list2 = FindPatternMatchesInRange(IntPtr.Add(pointer, -512), 512, BurstClientFirstReadPattern, BurstClientFirstReadPatternMask);
				if (list2.Count != 1)
				{
					throw new InvalidOperationException($"Burst client decoder pair #{num + 1}: expected exactly one matching first 12-bit read within 0x{512:X} bytes, found {list2.Count}.");
				}
				IntPtr pointer2 = list2[0];
				long num2 = pointer.ToInt64() - pointer2.ToInt64();
				if (num2 < 224 || num2 > 240)
				{
					throw new InvalidOperationException($"Burst client decoder pair #{num + 1}: unexpected field spacing 0x{num2:X}. Refusing to patch.");
				}
				list.Add(BurstMemoryPatch.Int32(IntPtr.Add(pointer2, 12), 12, 31, $"Burst client pair #{num + 1} first-field reader width"));
				list.Add(BurstMemoryPatch.Byte(IntPtr.Add(pointer2, 28), 12, 31, $"Burst client pair #{num + 1} first-field bit-position advance"));
				list.Add(BurstMemoryPatch.Int32(IntPtr.Add(pointer, 6), 12, 31, $"Burst client pair #{num + 1} second-field reader width"));
				list.Add(BurstMemoryPatch.Byte(IntPtr.Add(pointer, 26), 12, 31, $"Burst client pair #{num + 1} second-field bit-position advance"));
			}
			if (list.Count != 24)
			{
				throw new InvalidOperationException($"Expected 24 Burst client immediate edits, generated {list.Count}. Refusing to patch.");
			}
			ValidateAndApplyBurstPatches(list);
		}

		private void ValidateAndApplyBurstPatches(List<BurstMemoryPatch> candidates)
		{
			HashSet<IntPtr> hashSet = new HashSet<IntPtr>();
			foreach (BurstMemoryPatch candidate in candidates)
			{
				if (!hashSet.Add(candidate.Address))
				{
					throw new InvalidOperationException($"Duplicate Burst patch address 0x{candidate.Address.ToInt64():X} detected. Refusing to patch.");
				}
				byte[] left = ReadBytes(candidate.Address, candidate.OriginalBytes.Length);
				if (!BytesEqual(left, candidate.OriginalBytes))
				{
					throw new InvalidOperationException($"Unexpected bytes at 0x{candidate.Address.ToInt64():X} for {candidate.Description}. Refusing to patch.");
				}
			}
			_burstWirePatches.AddRange(candidates);
			try
			{
				foreach (BurstMemoryPatch candidate2 in candidates)
				{
					WriteProtectedBytes(candidate2.Address, candidate2.ReplacementBytes);
				}
			}
			catch
			{
				RestoreBurstInventoryWirePatch();
				throw;
			}
		}

		private void RestoreBurstInventoryWirePatch()
		{
			if (_burstWirePatches.Count == 0)
			{
				return;
			}
			Exception ex = null;
			for (int num = _burstWirePatches.Count - 1; num >= 0; num--)
			{
				BurstMemoryPatch burstMemoryPatch = _burstWirePatches[num];
				try
				{
					byte[] left = ReadBytes(burstMemoryPatch.Address, burstMemoryPatch.ReplacementBytes.Length);
					if (BytesEqual(left, burstMemoryPatch.ReplacementBytes))
					{
						WriteProtectedBytes(burstMemoryPatch.Address, burstMemoryPatch.OriginalBytes);
					}
					else if (!BytesEqual(left, burstMemoryPatch.OriginalBytes))
					{
						((BasePlugin)this).Log.LogWarning((object)($"Not restoring Burst patch '{burstMemoryPatch.Description}' at 0x{burstMemoryPatch.Address.ToInt64():X}; " + "current bytes are neither VStack's replacement nor the original bytes."));
					}
				}
				catch (Exception ex2)
				{
					if (ex == null)
					{
						ex = ex2;
					}
				}
			}
			if (ex == null)
			{
				_burstWirePatches.Clear();
			}
			if (ex == null)
			{
				return;
			}
			throw ex;
		}

		private static ProcessModule GetBurstGeneratedModule()
		{
			foreach (ProcessModule module in Process.GetCurrentProcess().Modules)
			{
				if (string.Equals(module.ModuleName, "lib_burst_generated.dll", StringComparison.OrdinalIgnoreCase))
				{
					return module;
				}
			}
			throw new InvalidOperationException("lib_burst_generated.dll is not loaded.");
		}

		private unsafe List<IntPtr> FindExecutablePatternMatches(ProcessModule module, byte[] pattern, bool[] mask)
		{
			if (pattern.Length == 0 || pattern.Length != mask.Length)
			{
				throw new ArgumentException("Pattern and mask must be non-empty and the same length.");
			}
			byte* ptr = (byte*)(void*)module.BaseAddress;
			GetPeSectionTable(ptr, out var numberOfSections, out var sectionTable);
			List<IntPtr> list = new List<IntPtr>();
			int num = 0;
			while (num < numberOfSections)
			{
				uint num2 = ((uint*)sectionTable)[2];
				uint num3 = ((uint*)sectionTable)[3];
				uint num4 = ((uint*)sectionTable)[9];
				if ((num4 & 0x20000000) != 0 && num2 >= pattern.Length)
				{
					byte* ptr2 = ptr + num3;
					int num5 = checked((int)num2);
					for (int i = 0; i <= num5 - pattern.Length; i++)
					{
						if (MatchesPattern(ptr2 + i, pattern, mask))
						{
							list.Add((IntPtr)(ptr2 + i));
						}
					}
				}
				num++;
				sectionTable += 40;
			}
			return list;
		}

		private static bool[] CreateAllTrueMask(int length)
		{
			bool[] array = new bool[length];
			for (int i = 0; i < length; i++)
			{
				array[i] = true;
			}
			return array;
		}

		private static byte[] ReadBytes(IntPtr address, int length)
		{
			byte[] array = new byte[length];
			Marshal.Copy(address, array, 0, length);
			return array;
		}

		private static bool BytesEqual(byte[] left, byte[] right)
		{
			if (left.Length != right.Length)
			{
				return false;
			}
			for (int i = 0; i < left.Length; i++)
			{
				if (left[i] != right[i])
				{
					return false;
				}
			}
			return true;
		}

		private static void WriteProtectedBytes(IntPtr address, byte[] bytes)
		{
			UIntPtr size = new UIntPtr((uint)bytes.Length);
			if (!VirtualProtect(address, size, 64u, out var oldProtect))
			{
				throw new InvalidOperationException($"VirtualProtect(RWX) failed at 0x{address.ToInt64():X}; Win32 error {Marshal.GetLastWin32Error()}.");
			}
			Exception ex = null;
			try
			{
				Marshal.Copy(bytes, 0, address, bytes.Length);
				byte[] left = ReadBytes(address, bytes.Length);
				if (!BytesEqual(left, bytes))
				{
					throw new InvalidOperationException($"Burst memory verification failed at 0x{address.ToInt64():X}.");
				}
				if (!FlushInstructionCache(GetCurrentProcess(), address, size))
				{
					throw new InvalidOperationException($"FlushInstructionCache failed at 0x{address.ToInt64():X}; Win32 error {Marshal.GetLastWin32Error()}.");
				}
			}
			catch (Exception ex2)
			{
				ex = ex2;
			}
			finally
			{
				if (!VirtualProtect(address, size, oldProtect, out var _) && ex == null)
				{
					ex = new InvalidOperationException($"VirtualProtect(restore) failed at 0x{address.ToInt64():X}; Win32 error {Marshal.GetLastWin32Error()}.");
				}
			}
			if (ex != null)
			{
				throw ex;
			}
		}

		private static ExternalSettingOverride? FindExternalSettingOverride(IntPtr fieldName)
		{
			ExternalSettingOverride[] externalSettingOverrides = _externalSettingOverrides;
			foreach (ExternalSettingOverride externalSettingOverride in externalSettingOverrides)
			{
				if (IsIl2CppStringEqual(fieldName, externalSettingOverride.SettingName))
				{
					return externalSettingOverride;
				}
			}
			return null;
		}

		private float GetMultiplier()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			float num = _multiplier?.Value ?? 1000f;
			bool flag = default(bool);
			if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f)
			{
				if (!_loggedInvalidConfig)
				{
					_loggedInvalidConfig = true;
					ManualLogSource log = ((BasePlugin)this).Log;
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(39, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Invalid Multiplier '");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(num);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'. Using default x");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(1000f, "0.###");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
					}
					log.LogWarning(val);
				}
				return 1000f;
			}
			if (num > 65504f)
			{
				if (!_loggedInvalidConfig)
				{
					_loggedInvalidConfig = true;
					ManualLogSource log2 = ((BasePlugin)this).Log;
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(55, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Multiplier x");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(num, "0.###");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" exceeds the half-precision limit. Using x");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(65504f, "0.###");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
					}
					log2.LogWarning(val);
				}
				return 65504f;
			}
			return Math.Max(num, 0.01f);
		}

		private unsafe static bool IsIl2CppStringEqual(IntPtr stringObject, string expected)
		{
			if (stringObject == IntPtr.Zero)
			{
				return false;
			}
			byte* ptr = (byte*)(void*)stringObject;
			int num = ((int*)ptr)[4];
			if (num != expected.Length || num < 0 || num > 128)
			{
				return false;
			}
			char* ptr2 = (char*)ptr + 10;
			for (int i = 0; i < num; i++)
			{
				if (ptr2[i] != expected[i])
				{
					return false;
				}
			}
			return true;
		}

		private unsafe static List<IntPtr> FindPatternMatchesInRange(IntPtr rangeStart, int rangeLength, byte[] pattern, bool[] mask)
		{
			if (rangeStart == IntPtr.Zero)
			{
				throw new ArgumentNullException("rangeStart");
			}
			if (rangeLength < pattern.Length)
			{
				throw new ArgumentOutOfRangeException("rangeLength");
			}
			if (pattern.Length == 0 || pattern.Length != mask.Length)
			{
				throw new ArgumentException("Pattern and mask must be non-empty and the same length.");
			}
			List<IntPtr> list = new List<IntPtr>();
			byte* ptr = (byte*)(void*)rangeStart;
			for (int i = 0; i <= rangeLength - pattern.Length; i++)
			{
				if (MatchesPattern(ptr + i, pattern, mask))
				{
					list.Add((IntPtr)(ptr + i));
				}
			}
			return list;
		}

		private unsafe static bool MatchesPattern(byte* address, byte[] pattern, bool[] mask)
		{
			for (int i = 0; i < pattern.Length; i++)
			{
				if (mask[i] && address[i] != pattern[i])
				{
					return false;
				}
			}
			return true;
		}

		private unsafe static void GetPeSectionTable(byte* imageBase, out ushort numberOfSections, out byte* sectionTable)
		{
			if (*(ushort*)imageBase != 23117)
			{
				throw new InvalidOperationException("Loaded native module has an invalid DOS header.");
			}
			int num = ((int*)imageBase)[15];
			byte* ptr = imageBase + num;
			if (*(uint*)ptr != 17744)
			{
				throw new InvalidOperationException("Loaded native module has an invalid PE header.");
			}
			numberOfSections = ((ushort*)ptr)[3];
			ushort num2 = ((ushort*)ptr)[10];
			sectionTable = ptr + 24 + (int)num2;
		}

		private unsafe IntPtr FindSettingsClampHalf()
		{
			ProcessModule processModule = null;
			foreach (ProcessModule module in Process.GetCurrentProcess().Modules)
			{
				if (string.Equals(module.ModuleName, "GameAssembly.dll", StringComparison.OrdinalIgnoreCase))
				{
					processModule = module;
					break;
				}
			}
			if (processModule == null)
			{
				throw new InvalidOperationException("GameAssembly.dll is not loaded.");
			}
			byte* ptr = (byte*)(void*)processModule.BaseAddress;
			if (*(ushort*)ptr != 23117)
			{
				throw new InvalidOperationException("Loaded native module has an invalid DOS header.");
			}
			int num = ((int*)ptr)[15];
			byte* ptr2 = ptr + num;
			if (*(uint*)ptr2 != 17744)
			{
				throw new InvalidOperationException("Loaded native module has an invalid PE header.");
			}
			ushort num2 = ((ushort*)ptr2)[3];
			ushort num3 = ((ushort*)ptr2)[10];
			byte* ptr3 = ptr2 + 24 + (int)num3;
			IntPtr intPtr = IntPtr.Zero;
			int num4 = 0;
			int num5 = 0;
			while (num5 < num2)
			{
				uint num6 = ((uint*)ptr3)[2];
				uint num7 = ((uint*)ptr3)[3];
				uint num8 = ((uint*)ptr3)[9];
				if ((num8 & 0x20000000) != 0 && num6 >= Pattern.Length)
				{
					byte* ptr4 = ptr + num7;
					int num9 = checked((int)num6);
					for (int i = 0; i <= num9 - Pattern.Length; i++)
					{
						if (Matches(ptr4 + i))
						{
							num4++;
							intPtr = (IntPtr)(ptr4 + i);
						}
					}
				}
				num5++;
				ptr3 += 40;
			}
			return num4 switch
			{
				0 => IntPtr.Zero, 
				1 => intPtr, 
				_ => throw new InvalidOperationException($"SettingsClamp::Half signature was not unique ({num4} matches). Refusing to hook."), 
			};
		}

		private unsafe static bool Matches(byte* address)
		{
			for (int i = 0; i < Pattern.Length; i++)
			{
				if (PatternMask[i] && address[i] != Pattern[i])
				{
					return false;
				}
			}
			return true;
		}
	}
}