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();
}
}
}