Decompiled source of InFalsusNoIotaLimit v1.0.1

BepInEx/plugins/InFalsusNoIotaLimit/InFalsusNoIotaLimit.dll

Decompiled 6 hours ago
using System;
using System.Diagnostics;
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.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Microsoft.CodeAnalysis;
using UnityEngine;
using ifapp.Game.Common;
using ifapp.Game.Data;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace InFalsusNoIotaLimit
{
	internal static class NoIotaLimitCore
	{
		private struct MEMORY_BASIC_INFORMATION
		{
			public IntPtr BaseAddress;

			public IntPtr AllocationBase;

			public uint AllocationProtect;

			public UIntPtr RegionSize;

			public uint State;

			public uint Protect;

			public uint Type;
		}

		internal static Action<string> Info;

		internal static Action<string> Warn;

		internal static Action<string> Error;

		private static IntPtr _fieldPtr;

		private static Type _iotaType;

		private static bool _loggedCap;

		private static bool _loggedError;

		private static bool _resolveFailedLogged;

		private const uint MEM_COMMIT = 4096u;

		private const uint PAGE_EXECUTE = 16u;

		private const uint PAGE_EXECUTE_READ = 32u;

		private const uint PAGE_EXECUTE_READWRITE = 64u;

		private const uint PAGE_EXECUTE_WRITECOPY = 128u;

		internal unsafe static void SetCap()
		{
			try
			{
				if (_fieldPtr == IntPtr.Zero)
				{
					ResolveField();
				}
				if (_fieldPtr == IntPtr.Zero)
				{
					return;
				}
				ushort num = default(ushort);
				IL2CPP.il2cpp_field_static_get_value(_fieldPtr, (void*)(&num));
				if (num != ushort.MaxValue)
				{
					ushort value = ushort.MaxValue;
					IL2CPP.il2cpp_field_static_set_value(_fieldPtr, (void*)(&value));
					if (!_loggedCap)
					{
						_loggedCap = true;
						Info?.Invoke($"MaxTotalIotaCount {num} -> {value}");
					}
				}
			}
			catch (Exception ex)
			{
				if (!_loggedError)
				{
					_loggedError = true;
					Error?.Invoke(ex.ToString());
				}
			}
		}

		private static void ResolveField()
		{
			if (_iotaType == null)
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					_iotaType = assembly.GetType("ifapp.Game.Data.IotaInventory", throwOnError: false) ?? assembly.GetType("Il2Cppifapp.Game.Data.IotaInventory", throwOnError: false);
					if (_iotaType != null)
					{
						break;
					}
				}
				if (_iotaType == null)
				{
					assemblies = AppDomain.CurrentDomain.GetAssemblies();
					foreach (Assembly assembly2 in assemblies)
					{
						Type[] types;
						try
						{
							types = assembly2.GetTypes();
						}
						catch (ReflectionTypeLoadException ex)
						{
							types = ex.Types;
						}
						Type[] array = types;
						foreach (Type type in array)
						{
							if (type != null && type.Name == "IotaInventory")
							{
								_iotaType = type;
								break;
							}
						}
						if (_iotaType != null)
						{
							break;
						}
					}
				}
				if (_iotaType == null)
				{
					return;
				}
			}
			FieldInfo field = _iotaType.GetField("NativeFieldInfoPtr_MaxTotalIotaCount", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				IntPtr intPtr = (IntPtr)field.GetValue(null);
				if (intPtr != IntPtr.Zero)
				{
					_fieldPtr = intPtr;
					return;
				}
			}
			IntPtr nativeClassPointer = Il2CppClassPointerStore.GetNativeClassPointer(_iotaType);
			if (nativeClassPointer != IntPtr.Zero)
			{
				_fieldPtr = IL2CPP.GetIl2CppField(nativeClassPointer, "MaxTotalIotaCount");
				if (_fieldPtr != IntPtr.Zero)
				{
					return;
				}
			}
			if (!_resolveFailedLogged)
			{
				_resolveFailedLogged = true;
				Warn?.Invoke("could not resolve IotaInventory.MaxTotalIotaCount");
			}
		}

		internal static void PatchUiCapCode()
		{
			try
			{
				IntPtr moduleHandle = GetModuleHandle("GameAssembly.dll");
				if (moduleHandle == IntPtr.Zero)
				{
					Warn?.Invoke("GameAssembly.dll not loaded; UI cap not patched");
					return;
				}
				byte[] array = new byte[6] { 129, 251, 15, 39, 0, 0 };
				byte[] array2 = new byte[6] { 129, 251, 255, 255, 0, 0 };
				int num = 0;
				IntPtr lpAddress = moduleHandle;
				UIntPtr dwLength = (UIntPtr)(ulong)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION));
				MEMORY_BASIC_INFORMATION lpBuffer;
				while (VirtualQuery(lpAddress, out lpBuffer, dwLength) != UIntPtr.Zero && !(lpBuffer.AllocationBase != moduleHandle))
				{
					if (lpBuffer.State == 4096 && (lpBuffer.Protect & 0xF0) != 0 && lpBuffer.RegionSize.ToUInt64() != 0)
					{
						int num2 = (int)(uint)lpBuffer.RegionSize;
						byte[] array3 = new byte[num2];
						Marshal.Copy(lpBuffer.BaseAddress, array3, 0, num2);
						for (int i = 0; i <= num2 - array.Length; i++)
						{
							bool flag = true;
							for (int j = 0; j < array.Length; j++)
							{
								if (array3[i + j] != array[j])
								{
									flag = false;
									break;
								}
							}
							if (flag)
							{
								IntPtr intPtr = IntPtr.Add(lpBuffer.BaseAddress, i);
								if (VirtualProtect(intPtr, (UIntPtr)(ulong)array2.Length, 64u, out var lpflOldProtect))
								{
									Marshal.Copy(array2, 0, intPtr, array2.Length);
									VirtualProtect(intPtr, (UIntPtr)(ulong)array2.Length, lpflOldProtect, out var _);
									num++;
								}
							}
						}
					}
					lpAddress = IntPtr.Add(lpBuffer.BaseAddress, (int)(uint)lpBuffer.RegionSize);
				}
				Info?.Invoke($"UI cap patch: {num} site(s)");
			}
			catch (Exception ex)
			{
				Error?.Invoke(ex.ToString());
			}
		}

		[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
		private static extern IntPtr GetModuleHandle(string moduleName);

		[DllImport("kernel32.dll")]
		private static extern UIntPtr VirtualQuery(IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, UIntPtr dwLength);

		[DllImport("kernel32.dll")]
		private static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
	}
	[HarmonyPatch(typeof(IotaInventory), "Add")]
	internal static class InventoryAddPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(IotaInventory __instance, ref IotaInstance __0)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			UnorderedListVal<IotaInstance> allIota = __instance.allIota;
			if (allIota == null)
			{
				return true;
			}
			long num = 0L;
			Il2CppArrayBase<IotaInstance> rawBuffer = allIota.RawBuffer;
			for (int i = 0; i < allIota.Length; i++)
			{
				num += rawBuffer[i].Count;
			}
			long num2 = 65535 - num;
			if (num2 <= 0)
			{
				return false;
			}
			__0.Count = (ushort)Math.Min(__0.Count, num2);
			return __0.Count != 0;
		}
	}
	[BepInPlugin("RedDragon.InFalsusNoIotaLimit", "InFalsusNoIotaLimit", "1.0.1")]
	public class NoIotaLimitPlugin : BasePlugin
	{
		internal static ManualLogSource LogSource;

		internal static ConfigEntry<bool> PatchUiCap;

		private static Harmony _harmony;

		public override void Load()
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			LogSource = ((BasePlugin)this).Log;
			NoIotaLimitCore.Info = delegate(string message)
			{
				LogSource.LogInfo((object)message);
			};
			NoIotaLimitCore.Warn = delegate(string message)
			{
				LogSource.LogWarning((object)message);
			};
			NoIotaLimitCore.Error = delegate(string message)
			{
				LogSource.LogError((object)message);
			};
			PatchUiCap = ((BasePlugin)this).Config.Bind<bool>("General", "PatchUiCap", true, "同时把 UI 里硬编码的 9999 比较改成 65535(粒子数量显示 / 界面元素)。");
			LogSource.LogInfo((object)"loading: removing the 9999 particle cap");
			((BasePlugin)this).AddComponent<NoIotaLimitBehaviour>();
			if (PatchUiCap.Value)
			{
				NoIotaLimitCore.PatchUiCapCode();
			}
			try
			{
				_harmony = new Harmony("RedDragon.InFalsusNoIotaLimit");
				_harmony.PatchAll(typeof(NoIotaLimitPlugin).Assembly);
				LogSource.LogInfo((object)"inventory guard installed");
			}
			catch (Exception ex)
			{
				LogSource.LogError((object)ex.ToString());
			}
		}

		public override bool Unload()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			return true;
		}
	}
	public class NoIotaLimitBehaviour : MonoBehaviour
	{
		private void Update()
		{
			NoIotaLimitCore.SetCap();
		}
	}
}