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