Decompiled source of InventoryExpansion v1.5.1

InventoryExpansion.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Bifrost.Cooked;
using DooDesch;
using HarmonyLib;
using InventoryExpansion;
using InventoryExpansion.Config;
using InventoryExpansion.Patches;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using Mimic;
using Mimic.Actors;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "InventoryExpansion", "1.5.1", "DooDesch", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+22647a04014be1fce044a4b382452bf0748afc9b")]
[assembly: AssemblyProduct("InventoryExpansion")]
[assembly: AssemblyTitle("InventoryExpansion")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.1.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 DooDesch
{
	internal sealed class HostSettings
	{
		[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyCreated")]
		private static class CreatedPatch
		{
			private static void Postfix(LobbyCreated_t callback)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				if ((int)callback.m_eResult != 1)
				{
					return;
				}
				foreach (HostSettings instance in Instances)
				{
					instance.Begin(new CSteamID(callback.m_ulSteamIDLobby), host: true);
				}
			}
		}

		[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyEntered")]
		private static class EnteredPatch
		{
			private static void Prefix(LobbyEnter_t callback)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				if (callback.m_EChatRoomEnterResponse != 1)
				{
					return;
				}
				CSteamID val = default(CSteamID);
				((CSteamID)(ref val))..ctor(callback.m_ulSteamIDLobby);
				foreach (HostSettings instance in Instances)
				{
					if (!instance.isHost || !(instance.lobby == val))
					{
						instance.Begin(val, host: false);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SteamMatchmaking), "LeaveLobby")]
		private static class LeavePatch
		{
			private static void Prefix(CSteamID steamIDLobby)
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				foreach (HostSettings instance in Instances)
				{
					if (instance.lobby == steamIDLobby)
					{
						instance.Reset();
					}
				}
			}
		}

		[HarmonyPatch(typeof(MainMenu), "Start")]
		private static class MenuPatch
		{
			private static void Prefix()
			{
				foreach (HostSettings instance in Instances)
				{
					instance.Reset();
				}
			}
		}

		private static readonly List<HostSettings> Instances = new List<HostSettings>();

		private static Callback<LobbyDataUpdate_t> dataCallback;

		private static float nextPoll;

		private readonly string modId;

		private readonly string lobbyKey;

		private readonly int schemaVersion;

		private readonly List<ISetting> settings = new List<ISetting>();

		private readonly string modernKey;

		private string modernPayload;

		private string lastModernPayload;

		private long modernRevision;

		private bool legacyHost;

		private Dictionary<string, object> snapshot;

		private CSteamID lobby;

		private CSteamID owner;

		private bool isHost;

		private bool hostCreated;

		private bool authorityValid;

		private bool ownerChanged;

		private bool published;

		private string hostPayload;

		private string warningReason;

		private int warningRepeats;

		internal bool IsInSession => lobby != CSteamID.Nil;

		internal bool IsHost
		{
			get
			{
				if (isHost)
				{
					return authorityValid;
				}
				return false;
			}
		}

		internal bool UsingLegacyHost
		{
			get
			{
				if (!isHost && authorityValid && legacyHost)
				{
					return modernRevision == 0;
				}
				return false;
			}
		}

		internal event Action EffectiveValuesChanged;

		internal event Action LiveValuesChanged;

		internal HostSettings(string modId, int schemaVersion = 1)
		{
			if (string.IsNullOrWhiteSpace(modId))
			{
				throw new ArgumentException("A mod ID is required.", "modId");
			}
			if (schemaVersion < 1)
			{
				throw new ArgumentOutOfRangeException("schemaVersion");
			}
			this.modId = modId;
			this.schemaVersion = schemaVersion;
			lobbyKey = "doodesch." + modId.ToLowerInvariant() + ".settings.v" + schemaVersion;
			modernKey = lobbyKey + ".extended";
			if (Encoding.UTF8.GetByteCount(modernKey) >= 255)
			{
				throw new ArgumentException("The mod ID exceeds the Steam lobby key limit.", "modId");
			}
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobbyKey == lobbyKey)
				{
					throw new ArgumentException("The mod already has a settings snapshot.", "modId");
				}
			}
			Instances.Add(this);
		}

		internal HostSetting<T> Register<T>(MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate = null, bool live = false, bool legacy = true, Func<T> localValue = null)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			if (string.IsNullOrWhiteSpace(((MelonPreferences_Entry)entry).Identifier))
			{
				throw new ArgumentException("A setting identifier is required.", "entry");
			}
			if (((MelonPreferences_Entry)entry).Identifier.IndexOfAny(new char[3] { '\n', '\r', '\t' }) >= 0)
			{
				throw new ArgumentException("Setting identifiers cannot contain wire delimiters.", "entry");
			}
			if (lobby != CSteamID.Nil)
			{
				throw new InvalidOperationException("Register settings before entering a lobby.");
			}
			if (typeof(T) != typeof(bool) && typeof(T) != typeof(int) && typeof(T) != typeof(float) && typeof(T) != typeof(string))
			{
				throw new NotSupportedException("Host settings support bool, int, float and string values.");
			}
			if (legacy && typeof(T) == typeof(string))
			{
				throw new ArgumentException("String settings must use the extended snapshot.", "legacy");
			}
			foreach (ISetting setting in settings)
			{
				if (setting.Key == ((MelonPreferences_Entry)entry).Identifier)
				{
					throw new ArgumentException("Duplicate setting: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
				}
			}
			HostSetting<T> hostSetting = new HostSetting<T>(this, entry, vanillaValue, validate, live, legacy, localValue);
			if (!hostSetting.IsValid(vanillaValue))
			{
				throw new ArgumentException("The vanilla value is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "vanillaValue");
			}
			if (!hostSetting.IsValid(entry.DefaultValue))
			{
				throw new ArgumentException("The configured default is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
			}
			settings.Add(hostSetting);
			return hostSetting;
		}

		internal T GetValue<T>(string key, T localValue, T vanillaValue, bool live)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (lobby == CSteamID.Nil)
			{
				return localValue;
			}
			if (live && !authorityValid)
			{
				return vanillaValue;
			}
			if (snapshot == null || !snapshot.TryGetValue(key, out var value))
			{
				return vanillaValue;
			}
			return (T)value;
		}

		internal static void Update()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (Time.realtimeSinceStartup < nextPoll)
			{
				return;
			}
			nextPoll = Time.realtimeSinceStartup + 1f;
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobby != CSteamID.Nil)
				{
					instance.Refresh();
				}
			}
		}

		private void Begin(CSteamID newLobby, bool host)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (settings.Count == 0 || (lobby == newLobby && (!host || hostCreated)))
			{
				return;
			}
			if (lobby != newLobby)
			{
				Reset();
			}
			lobby = newLobby;
			isHost = false;
			hostCreated = host;
			authorityValid = false;
			ownerChanged = false;
			snapshot = null;
			published = false;
			hostPayload = null;
			owner = CSteamID.Nil;
			try
			{
				owner = SteamMatchmaking.GetLobbyOwner(lobby);
				CSteamID steamID = SteamUser.GetSteamID();
				if (owner == CSteamID.Nil || (host ? (owner != steamID) : (owner == steamID)))
				{
					owner = CSteamID.Nil;
					Warn("Lobby owner does not identify the expected host; using vanilla settings.");
					Changed();
					return;
				}
				isHost = host;
				authorityValid = true;
				if (host)
				{
					CaptureHostSnapshot();
				}
				else
				{
					CaptureClientSnapshot();
				}
			}
			catch (Exception ex)
			{
				Warn("Initial lobby settings failed; vanilla settings are frozen for this session: " + ex.GetType().Name + ": " + ex.Message);
			}
			Refresh();
			Changed();
		}

		private static void EnsureCallback()
		{
			if (dataCallback == null)
			{
				dataCallback = Callback<LobbyDataUpdate_t>.Create((DispatchDelegate<LobbyDataUpdate_t>)OnLobbyDataUpdated);
			}
		}

		private static void OnLobbyDataUpdated(LobbyDataUpdate_t callback)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			foreach (HostSettings instance in Instances)
			{
				if (instance.lobby.m_SteamID == callback.m_ulSteamIDLobby && callback.m_ulSteamIDMember == callback.m_ulSteamIDLobby)
				{
					if (callback.m_bSuccess == 0)
					{
						instance.Warn("Steam lobby data update failed; frozen session settings are retained.");
					}
					else
					{
						instance.Refresh();
					}
				}
			}
		}

		private void CaptureHostSnapshot()
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			StringBuilder stringBuilder = new StringBuilder(schemaVersion.ToString(CultureInfo.InvariantCulture));
			for (int i = 0; i < settings.Count; i++)
			{
				ISetting setting = settings[i];
				object localValue = setting.LocalValue;
				dictionary.Add(setting.Key, localValue);
				if (setting.Legacy)
				{
					stringBuilder.Append('\n').Append(setting.Key).Append('\t')
						.Append(Format(localValue));
				}
			}
			string s = stringBuilder.ToString();
			if (Encoding.UTF8.GetByteCount(s) + 1 > 8192)
			{
				throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
			}
			hostPayload = s;
			snapshot = dictionary;
			modernRevision = 1L;
			modernPayload = BuildModern(dictionary, modernRevision);
		}

		private unsafe void CaptureClientSnapshot()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
			if (!string.IsNullOrEmpty(lobbyData))
			{
				snapshot = ParseModern(lobbyData, out modernRevision);
				lastModernPayload = lobbyData;
				MelonLogger.Msg("[" + modId + "] Host settings captured; live values follow the host, lobby values remain fixed.");
				return;
			}
			string lobbyData2 = SteamMatchmaking.GetLobbyData(lobby, lobbyKey);
			if (string.IsNullOrEmpty(lobbyData2))
			{
				Warn("Host snapshot is absent; vanilla settings are frozen for this session.");
				return;
			}
			snapshot = ParseSnapshot(lobbyData2);
			legacyHost = true;
			string[] obj = new string[5] { "[", modId, "] Host settings snapshot frozen for lobby ", null, null };
			CSteamID val = lobby;
			obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
			obj[4] = ".";
			MelonLogger.Msg(string.Concat(obj));
		}

		private void Refresh()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			try
			{
				EnsureCallback();
				CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(lobby);
				if (ownerChanged || owner == CSteamID.Nil || lobbyOwner != owner)
				{
					authorityValid = false;
					if (lobbyOwner != CSteamID.Nil && lobbyOwner != owner)
					{
						ownerChanged = true;
					}
					Warn("Lobby owner changed or is unavailable; authority revoked and frozen session settings retained.");
					return;
				}
				bool num = !authorityValid;
				authorityValid = true;
				flag = true;
				if (num)
				{
					MelonLogger.Msg("[" + modId + "] Pinned lobby authority recovered; frozen settings retained.");
				}
				if (isHost)
				{
					if (hostPayload != null)
					{
						if (published)
						{
							RefreshHostLive();
						}
						else
						{
							Publish();
						}
					}
				}
				else
				{
					RefreshClientLive();
				}
			}
			catch (Exception ex)
			{
				authorityValid = flag;
				Warn("Refresh lobby settings failed; frozen session settings retained: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private unsafe void Publish()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload && !SteamMatchmaking.SetLobbyData(lobby, lobbyKey, hostPayload))
			{
				CSteamID val = lobby;
				Warn("SetLobbyData rejected the settings snapshot for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
			}
			else if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload)
			{
				CSteamID val = lobby;
				Warn("Settings snapshot readback differs for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
			}
			else if (PublishModern(modernPayload))
			{
				published = true;
				Recover();
				string[] obj = new string[5] { "[", modId, "] Settings snapshot confirmed by local lobby readback for ", null, null };
				CSteamID val = lobby;
				obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
				obj[4] = ".";
				MelonLogger.Msg(string.Concat(obj));
			}
		}

		private Dictionary<string, object> ParseSnapshot(string payload)
		{
			if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
			{
				throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
			}
			string[] array = payload.Split('\n');
			if (array[0] != schemaVersion.ToString(CultureInfo.InvariantCulture))
			{
				throw new FormatException("Settings snapshot schema mismatch.");
			}
			if (array.Length != settings.FindAll((ISetting s) => s.Legacy).Count + 1)
			{
				throw new FormatException("Settings snapshot has missing or extra keys.");
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			for (int num = 1; num < array.Length; num++)
			{
				string text = array[num];
				int num2 = text.IndexOf('\t');
				if (num2 <= 0 || num2 != text.LastIndexOf('\t') || text.IndexOf('\r') >= 0)
				{
					throw new FormatException("Settings snapshot has an invalid entry delimiter.");
				}
				string key = text.Substring(0, num2);
				if (dictionary.ContainsKey(key))
				{
					throw new FormatException("Settings snapshot has a duplicate or missing key.");
				}
				ISetting setting = settings.Find((ISetting candidate) => candidate.Key == key && candidate.Legacy);
				if (setting == null)
				{
					throw new FormatException("Settings snapshot has an unknown key: " + key);
				}
				dictionary.Add(key, setting.Parse(text.Substring(num2 + 1)));
			}
			return dictionary;
		}

		private string BuildModern(Dictionary<string, object> values, long revision)
		{
			StringBuilder stringBuilder = new StringBuilder("2\t" + revision.ToString(CultureInfo.InvariantCulture));
			foreach (ISetting setting in settings)
			{
				stringBuilder.Append('\n').Append(setting.Key).Append('\t')
					.Append(Format(values[setting.Key]));
			}
			string text = stringBuilder.ToString();
			if (Encoding.UTF8.GetByteCount(text) + 1 > 8192)
			{
				throw new FormatException("Extended settings exceed the Steam metadata limit.");
			}
			return text;
		}

		private Dictionary<string, object> ParseModern(string payload, out long revision)
		{
			if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
			{
				throw new FormatException("Extended settings exceed the Steam metadata limit.");
			}
			string[] array = payload.Split('\n');
			string[] array2 = array[0].Split('\t');
			if (array2.Length != 2 || array2[0] != "2" || !long.TryParse(array2[1], NumberStyles.None, CultureInfo.InvariantCulture, out revision) || revision < 1)
			{
				throw new FormatException("Invalid extended settings schema or revision.");
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			for (int i = 1; i < array.Length; i++)
			{
				int num = array[i].IndexOf('\t');
				if (num <= 0 || num != array[i].LastIndexOf('\t') || array[i].IndexOf('\r') >= 0)
				{
					throw new FormatException("Invalid extended settings delimiter.");
				}
				string key = array[i].Substring(0, num);
				if (!hashSet.Add(key))
				{
					throw new FormatException("Duplicate extended setting: " + key);
				}
				ISetting setting = settings.Find((ISetting s) => s.Key == key);
				if (setting != null)
				{
					dictionary.Add(key, setting.Parse(array[i].Substring(num + 1)));
				}
			}
			return dictionary;
		}

		private bool PublishModern(string payload)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload && !SteamMatchmaking.SetLobbyData(lobby, modernKey, payload))
			{
				Warn("SetLobbyData rejected the extended settings; last confirmed values retained.");
				return false;
			}
			if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload)
			{
				Warn("Extended settings readback differs; last confirmed values retained.");
				return false;
			}
			return true;
		}

		private void RefreshHostLive()
		{
			if (snapshot == null)
			{
				return;
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(snapshot, StringComparer.Ordinal);
			bool flag = false;
			foreach (ISetting setting in settings)
			{
				if (setting.Live)
				{
					object localValue = setting.LocalValue;
					if (!dictionary.TryGetValue(setting.Key, out var value) || !object.Equals(value, localValue))
					{
						dictionary[setting.Key] = localValue;
						flag = true;
					}
				}
			}
			if (!flag)
			{
				if (PublishModern(modernPayload))
				{
					Recover();
				}
				return;
			}
			long revision = checked(modernRevision + 1);
			string payload = BuildModern(dictionary, revision);
			if (PublishModern(payload))
			{
				snapshot = dictionary;
				modernRevision = revision;
				modernPayload = payload;
				Recover();
				Changed(this.LiveValuesChanged);
			}
		}

		private void RefreshClientLive()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
			if (string.IsNullOrEmpty(lobbyData) || lobbyData == lastModernPayload)
			{
				return;
			}
			long revision;
			Dictionary<string, object> dictionary = ParseModern(lobbyData, out revision);
			if (revision <= modernRevision)
			{
				Warn("Extended settings revision did not advance; last confirmed values retained.");
				return;
			}
			Dictionary<string, object> dictionary2 = ((snapshot == null) ? new Dictionary<string, object>(StringComparer.Ordinal) : new Dictionary<string, object>(snapshot, StringComparer.Ordinal));
			bool flag = false;
			foreach (ISetting setting in settings)
			{
				if (setting.Live && dictionary.TryGetValue(setting.Key, out var value) && (!dictionary2.TryGetValue(setting.Key, out var value2) || !object.Equals(value2, value)))
				{
					dictionary2[setting.Key] = value;
					flag = true;
				}
			}
			snapshot = dictionary2;
			modernRevision = revision;
			lastModernPayload = lobbyData;
			Recover();
			if (flag)
			{
				Changed(this.LiveValuesChanged);
			}
		}

		private static string Format(object value)
		{
			if (value is string s)
			{
				return "s:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s));
			}
			if (value is float num)
			{
				return num.ToString("R", CultureInfo.InvariantCulture);
			}
			if (value is bool)
			{
				if (!(bool)value)
				{
					return "false";
				}
				return "true";
			}
			return ((int)value).ToString(CultureInfo.InvariantCulture);
		}

		private void Warn(string reason)
		{
			if (warningReason == reason)
			{
				warningRepeats++;
				return;
			}
			if (warningRepeats > 0)
			{
				MelonLogger.Warning("[" + modId + "] Previous settings warning repeated " + warningRepeats + " times.");
			}
			warningReason = reason;
			warningRepeats = 0;
			MelonLogger.Warning("[" + modId + "] " + reason);
		}

		private void Recover()
		{
			if (warningReason != null)
			{
				MelonLogger.Msg("[" + modId + "] Lobby settings recovered after " + (warningRepeats + 1) + " warning occurrences.");
				warningReason = null;
				warningRepeats = 0;
			}
		}

		private void Changed()
		{
			Changed(this.EffectiveValuesChanged);
		}

		private void Changed(Action handlers)
		{
			if (handlers == null)
			{
				return;
			}
			Delegate[] invocationList = handlers.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action = (Action)invocationList[i];
				try
				{
					action();
				}
				catch (Exception ex)
				{
					MelonLogger.Error("[" + modId + "] Apply effective settings failed.", ex);
				}
			}
		}

		private void Reset()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			bool num = lobby != CSteamID.Nil;
			lobby = CSteamID.Nil;
			owner = CSteamID.Nil;
			isHost = false;
			hostCreated = false;
			authorityValid = false;
			ownerChanged = false;
			published = false;
			snapshot = null;
			hostPayload = null;
			modernPayload = null;
			lastModernPayload = null;
			modernRevision = 0L;
			legacyHost = false;
			warningReason = null;
			warningRepeats = 0;
			if (num)
			{
				MelonLogger.Msg("[" + modId + "] Session settings cleared; local settings restored.");
				Changed();
			}
		}
	}
	internal interface ISetting
	{
		string Key { get; }

		object LocalValue { get; }

		bool Live { get; }

		bool Legacy { get; }

		bool IsValid(object value);

		object Parse(string value);
	}
	internal sealed class HostSetting<T> : ISetting
	{
		private readonly HostSettings owner;

		private readonly MelonPreferences_Entry<T> entry;

		private readonly T vanillaValue;

		private readonly Func<T, bool> validate;

		private readonly Func<T> localValue;

		private bool invalidLocal;

		public bool Live { get; }

		public bool Legacy { get; }

		internal T Value => owner.GetValue(Key, (T)LocalValue, vanillaValue, Live);

		public string Key => ((MelonPreferences_Entry)entry).Identifier;

		public object LocalValue
		{
			get
			{
				T val = ((localValue == null) ? entry.Value : localValue());
				if (IsValid(val))
				{
					if (invalidLocal)
					{
						MelonLogger.Msg("Local setting recovered: " + Key);
					}
					invalidLocal = false;
					return val;
				}
				if (!invalidLocal)
				{
					MelonLogger.Warning("Invalid local setting " + Key + "; using its configured default without overwriting the file.");
				}
				invalidLocal = true;
				return entry.DefaultValue;
			}
		}

		internal HostSetting(HostSettings owner, MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate, bool live, bool legacy, Func<T> localValue)
		{
			this.owner = owner;
			this.entry = entry;
			this.vanillaValue = vanillaValue;
			this.validate = validate;
			this.localValue = localValue;
			Live = live;
			Legacy = legacy;
		}

		public bool IsValid(object value)
		{
			if (!(value is T arg))
			{
				return false;
			}
			if (value is float f && (float.IsNaN(f) || float.IsInfinity(f)))
			{
				return false;
			}
			if (value is string text && (Encoding.UTF8.GetByteCount(text) > 1024 || text.IndexOf('\0') >= 0))
			{
				return false;
			}
			if (validate != null)
			{
				return validate(arg);
			}
			return true;
		}

		public object Parse(string value)
		{
			object obj;
			int result2;
			float result3;
			if (typeof(T) == typeof(bool) && bool.TryParse(value, out var result))
			{
				obj = result;
			}
			else if (typeof(T) == typeof(int) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
			{
				obj = result2;
			}
			else if (typeof(T) == typeof(float) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result3))
			{
				obj = result3;
			}
			else
			{
				if (!(typeof(T) == typeof(string)) || !value.StartsWith("s:", StringComparison.Ordinal))
				{
					throw new FormatException("Invalid settings value: " + Key);
				}
				obj = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(value.Substring(2)));
			}
			if (!IsValid(obj))
			{
				throw new FormatException("Settings value is out of range: " + Key);
			}
			return obj;
		}
	}
	internal static class ModVersion
	{
		internal const string Current = "1.5.1";
	}
}
namespace InventoryExpansion
{
	public sealed class Core : MelonMod
	{
		public override void OnInitializeMelon()
		{
			InventoryExpansionPreferences.Initialize();
			InventoryExpansionPreferences.EffectiveValuesChanged += ApplyEffectiveSettings;
			ApplyEffectiveSettings();
			MelonLogger.Msg("InventoryExpansion initialized. Enabled={0}", new object[1] { InventoryExpansionPreferences.Enabled });
		}

		private static void ApplyEffectiveSettings()
		{
			BackpackPanelPatch.ResetForSettingsChange();
			BackpackMovementSpeedPatch.ResetLocalState();
		}

		public override void OnUpdate()
		{
			HostSettings.Update();
		}
	}
}
namespace InventoryExpansion.Patches
{
	internal static class BackpackLayout
	{
		internal static int[] Rows(int slots)
		{
			if (slots < 0)
			{
				throw new ArgumentOutOfRangeException("slots");
			}
			if (slots == 0)
			{
				return Array.Empty<int>();
			}
			int num = (slots + 7) / 8;
			int[] array = new int[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = slots / num + ((i < slots % num) ? 1 : 0);
			}
			return array;
		}
	}
	[HarmonyPatch]
	internal static class BackpackPanelPatch
	{
		private static readonly FieldInfo MainInventoryField = AccessTools.Field(typeof(GameMainBase), "inventoryui");

		private static UIPrefab_Inventory _inventoryUi;

		private static RectTransform _nativeHud;

		private static Vector2 _hudRest;

		private static RectTransform _backpackPanel;

		private static RectTransform _firstNativeSlot;

		private static RectTransform _lastNativeSlot;

		private static GameObject _bodyVisuals;

		private static UIPrefab_KeyIcon _keyBadge;

		private static readonly List<GameObject> Containers = new List<GameObject>();

		private static bool _slotsMoved;

		private static bool _backpackFullyVisible;

		private static bool _targetOpen;

		private static bool _layoutPending;

		private static float _offset;

		private static float _openOffset;

		private static float _nextBindAttempt;

		private static float _nextBadgeAttempt;

		private static bool _badgeMissing;

		private static int _savedStandardSlot;

		private static object _animationCoroutine;

		private static string _lastLayoutError;

		private static bool _waitingForSlotLayout;

		private static FieldInfo _uimanField;

		internal static bool IsBackpackFullyVisible => _backpackFullyVisible;

		internal static bool IsBackpackOpen => _targetOpen;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UIPrefab_Inventory), "Awake")]
		private static void AwakePostfix(UIPrefab_Inventory __instance)
		{
			BackpackSceneChangeHandler.Initialize();
			Relayout(__instance);
		}

		private static UIPrefab_Inventory LocalUi()
		{
			if (!((Object)(object)Hub.Main == (Object)null))
			{
				object? value = MainInventoryField.GetValue(Hub.Main);
				return (UIPrefab_Inventory)((value is UIPrefab_Inventory) ? value : null);
			}
			return null;
		}

		internal static void Relayout(UIPrefab_Inventory ui)
		{
			if (!_layoutPending && InventoryExpansionPreferences.Enabled)
			{
				_layoutPending = true;
				MelonCoroutines.Start(LayoutAfterFrame(ui));
			}
		}

		private static IEnumerator LayoutAfterFrame(UIPrefab_Inventory ui)
		{
			yield return null;
			_layoutPending = false;
			if ((Object)(object)ui == (Object)null || (Object)(object)LocalUi() != (Object)(object)ui || !InventoryExpansionPreferences.Enabled)
			{
				yield break;
			}
			try
			{
				if ((Object)(object)_inventoryUi != (Object)(object)ui || (Object)(object)_backpackPanel == (Object)null)
				{
					Bind(ui);
				}
				if (ArrangeSlots())
				{
					if (_lastLayoutError != null)
					{
						MelonLogger.Msg("[InventoryExpansion] Native backpack layout recovered.");
					}
					_lastLayoutError = null;
				}
			}
			catch (Exception ex)
			{
				string text = ex.GetType().Name + ": " + ex.Message;
				if (_lastLayoutError != text)
				{
					MelonLogger.Error("[InventoryExpansion] Native backpack layout failed: " + ex);
				}
				_lastLayoutError = text;
			}
		}

		private static void Bind(UIPrefab_Inventory ui)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			HideBackpackCompletely();
			if ((Object)(object)_backpackPanel != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_backpackPanel).gameObject);
			}
			Containers.Clear();
			_firstNativeSlot = (_lastNativeSlot = null);
			_keyBadge = null;
			_inventoryUi = ui;
			Transform uE_rootNode = ui.UE_rootNode;
			_nativeHud = (RectTransform)(object)((uE_rootNode is RectTransform) ? uE_rootNode : null);
			if ((Object)(object)_nativeHud == (Object)null)
			{
				throw new InvalidOperationException("Native inventory root is missing.");
			}
			_hudRest = _nativeHud.anchoredPosition;
			_backpackPanel = (RectTransform)new GameObject("InventoryExpansion_BackpackPanel", new Type[1] { typeof(RectTransform) }).transform;
			((Transform)_backpackPanel).SetParent(((Transform)_nativeHud).parent, false);
			RectTransform val = (RectTransform)((Transform)_nativeHud).parent;
			RectTransform backpackPanel = _backpackPanel;
			Vector2 anchorMin = (_backpackPanel.anchorMax = val.pivot);
			backpackPanel.anchorMin = anchorMin;
			_backpackPanel.pivot = new Vector2(0.5f, 0f);
			_bodyVisuals = new GameObject("InventoryExpansion_Group", new Type[1] { typeof(RectTransform) });
			_bodyVisuals.transform.SetParent((Transform)(object)_backpackPanel, false);
			RectTransform val2 = (RectTransform)_bodyVisuals.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			anchorMin = (val2.offsetMax = Vector2.zero);
			val2.offsetMin = anchorMin;
			_slotsMoved = false;
		}

		private static bool ArrangeSlots()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_046f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Expected O, but got Unknown
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Expected O, but got Unknown
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Expected O, but got Unknown
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0322: Unknown result type (might be due to invalid IL or missing references)
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_0385: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_050a: Unknown result type (might be due to invalid IL or missing references)
			//IL_052f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0534: Unknown result type (might be due to invalid IL or missing references)
			List<Slot> list = (List<Slot>)InventoryUiPatches.SlotsField.GetValue(_inventoryUi);
			int num = InvenSlotLayout.FirstExtensionSlotKey - 1;
			int num2 = Math.Min(list.Count, InventoryUiPatches.LogicalSlotCount) - num;
			if (num2 <= 0)
			{
				return true;
			}
			int[] array = BackpackLayout.Rows(num2);
			RectTransform val = (_firstNativeSlot = (RectTransform)list[0].root.transform);
			_lastNativeSlot = (RectTransform)list[3].root.transform;
			Rect rect = val.rect;
			float width = ((Rect)(ref rect)).width;
			rect = val.rect;
			float height = ((Rect)(ref rect)).height;
			if (width <= 0f || height <= 0f)
			{
				if (!_waitingForSlotLayout)
				{
					MelonLogger.Msg("[InventoryExpansion] Waiting for the native inventory slot layout.");
				}
				_waitingForSlotLayout = true;
				return false;
			}
			if (_waitingForSlotLayout)
			{
				MelonLogger.Msg("[InventoryExpansion] Native inventory slot layout is ready.");
			}
			_waitingForSlotLayout = false;
			HorizontalLayoutGroup component = ((Component)((Transform)val).parent).GetComponent<HorizontalLayoutGroup>();
			float num3 = ((component != null) ? ((HorizontalOrVerticalLayoutGroup)component).spacing : 15f);
			float num4 = 8f;
			float num5 = (float)array.Max() * width + (float)(array.Max() - 1) * num3;
			float num6 = (float)array.Length * height + (float)(array.Length - 1) * num4;
			_backpackPanel.sizeDelta = new Vector2(num5 + 24f, num6 + 36f + 24f);
			RectTransform parent = (RectTransform)((Transform)_backpackPanel).parent;
			Vector3[] array2 = (Vector3[])(object)new Vector3[4];
			val.GetWorldCorners(array2);
			float num7 = array2.Min((Vector3 point) => ((Transform)parent).InverseTransformPoint(point).y) - _offset;
			float x = ((Transform)parent).InverseTransformPoint(((Transform)val).position).x;
			float x2 = ((Transform)parent).InverseTransformPoint(list[3].root.transform.position).x;
			rect = parent.rect;
			float num8 = Mathf.Max(((Rect)(ref rect)).yMin + 12f, num7 - 36f - 12f);
			_backpackPanel.anchoredPosition = new Vector2((x + x2) * 0.5f, num8);
			rect = _backpackPanel.rect;
			_openOffset = num8 + ((Rect)(ref rect)).height + 12f - num7;
			int num9 = num;
			Vector2 val5 = default(Vector2);
			Vector2 val7 = default(Vector2);
			for (int num10 = 0; num10 < array.Length; num10++)
			{
				float num11 = (float)array[num10] * width + (float)(array[num10] - 1) * num3;
				int num12 = 0;
				while (num12 < array[num10])
				{
					Slot val2 = list[num9];
					RectTransform val3;
					if (num9 - num < Containers.Count)
					{
						val3 = (RectTransform)Containers[num9 - num].transform;
					}
					else
					{
						GameObject val4 = new GameObject("InventoryExpansion_SlotContainer_" + num9, new Type[1] { typeof(RectTransform) });
						val4.transform.SetParent((Transform)(object)_backpackPanel, false);
						Containers.Add(val4);
						val3 = (RectTransform)val4.transform;
					}
					RectTransform obj = val3;
					RectTransform obj2 = val3;
					((Vector2)(ref val5))..ctor(0.5f, 1f);
					obj2.anchorMax = val5;
					obj.anchorMin = val5;
					val3.pivot = new Vector2(0.5f, 0.5f);
					val3.sizeDelta = new Vector2(width, height);
					val3.anchoredPosition = new Vector2((0f - num11) / 2f + width / 2f + (float)num12 * (width + num3), -12f - height / 2f - (float)num10 * (height + num4));
					RectTransform val6 = (RectTransform)val2.root.transform;
					((Transform)val6).SetParent((Transform)(object)val3, false);
					((Vector2)(ref val7))..ctor(0.5f, 0.5f);
					val6.pivot = val7;
					val5 = (val6.anchorMin = (val6.anchorMax = val7));
					val6.sizeDelta = new Vector2(width, height);
					val6.anchoredPosition = Vector2.zero;
					((Transform)val6).localScale = Vector3.one;
					val2.group = null;
					num12++;
					num9++;
				}
			}
			for (int num13 = _bodyVisuals.transform.childCount - 1; num13 >= 0; num13--)
			{
				Object.Destroy((Object)(object)((Component)_bodyVisuals.transform.GetChild(num13)).gameObject);
			}
			Color color = ((Graphic)_inventoryUi.UE_BG_normal1).color;
			RectImage("Backing", new Vector2(num5 + 20f, num6 + 12f), new Vector2(0f, 48f + num6 / 2f), new Color(color.r * 0.2f, color.g * 0.15f, color.b * 0.1f, 0.07f));
			int[] array3 = new int[2] { -1, 1 };
			foreach (int num15 in array3)
			{
				RectImage("End", new Vector2(2f, 26f), new Vector2((float)num15 * (num5 / 2f + 8f), 48f + num6 / 2f), color);
			}
			_bodyVisuals.transform.SetAsFirstSibling();
			EnsureBadge();
			_slotsMoved = true;
			ApplyOffset(_targetOpen ? _openOffset : 0f);
			ShowContents(_targetOpen);
			return true;
		}

		internal static void FollowNativeHud()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_backpackPanel == (Object)null) && ((Component)_backpackPanel).gameObject.activeInHierarchy && !((Object)(object)_firstNativeSlot == (Object)null) && !((Object)(object)_lastNativeSlot == (Object)null))
			{
				Transform parent = ((Transform)_backpackPanel).parent;
				float x = parent.InverseTransformPoint(((Transform)_firstNativeSlot).position).x;
				float x2 = parent.InverseTransformPoint(((Transform)_lastNativeSlot).position).x;
				_backpackPanel.anchoredPosition = new Vector2((x + x2) / 2f, _backpackPanel.anchoredPosition.y);
			}
		}

		private static Image RectImage(string name, Vector2 size, Vector2 position, Color color)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("InventoryExpansion_" + name, new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent(_bodyVisuals.transform, false);
			RectTransform val2 = (RectTransform)val.transform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.sizeDelta = size;
			val2.anchoredPosition = position;
			Image component = val.GetComponent<Image>();
			((Graphic)component).color = color;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private static void EnsureBadge()
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_keyBadge == (Object)null)
			{
				if (Time.unscaledTime < _nextBadgeAttempt)
				{
					return;
				}
				_nextBadgeAttempt = Time.unscaledTime + 1f;
				UIPrefab_KeyIcon val = ((IEnumerable<UIPrefab_KeyIcon>)Object.FindObjectsByType<UIPrefab_KeyIcon>((FindObjectsInactive)1, (FindObjectsSortMode)0)).FirstOrDefault((Func<UIPrefab_KeyIcon, bool>)((UIPrefab_KeyIcon icon) => !((Object)icon).name.StartsWith("InventoryExpansion_")));
				if ((Object)(object)val == (Object)null)
				{
					if (!_badgeMissing)
					{
						MelonLogger.Warning("[InventoryExpansion] Waiting for the native key-cap prefab.");
					}
					_badgeMissing = true;
					return;
				}
				if (_badgeMissing)
				{
					MelonLogger.Msg("[InventoryExpansion] Native key-cap prefab is available.");
				}
				_badgeMissing = false;
				_keyBadge = Object.Instantiate<UIPrefab_KeyIcon>(val, (Transform)(object)_backpackPanel, false);
				((Object)_keyBadge).name = "InventoryExpansion_KeyHint";
				RectTransform val2 = (RectTransform)((Component)_keyBadge).transform;
				Vector2 val3 = default(Vector2);
				((Vector2)(ref val3))..ctor(0.5f, 0f);
				val2.anchorMax = val3;
				val2.anchorMin = val3;
				val2.pivot = new Vector2(0.5f, 0f);
				val2.anchoredPosition = Vector2.zero;
				val2.sizeDelta = new Vector2(38f, 36f);
				((Transform)val2).localScale = Vector3.one;
			}
			string text = ((object)InventoryExpansionPreferences.BackpackKey/*cast due to .constrained prefix*/).ToString();
			_keyBadge.SetKey("k_" + text.ToLowerInvariant());
			((Component)_keyBadge.UE_Key).gameObject.SetActive(true);
			_keyBadge.UE_UI_KeyText.text = text;
			((Component)_keyBadge).gameObject.SetActive(true);
		}

		private static void ApplyOffset(float offset)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			_offset = offset;
			if ((Object)(object)_nativeHud != (Object)null)
			{
				_nativeHud.anchoredPosition = _hudRest + new Vector2(0f, offset);
			}
		}

		private static void ShowContents(bool visible)
		{
			if ((Object)(object)_bodyVisuals != (Object)null)
			{
				_bodyVisuals.SetActive(visible);
			}
			int num = Math.Max(0, InventoryUiPatches.LogicalSlotCount - InvenSlotLayout.FirstExtensionSlotKey + 1);
			for (int i = 0; i < Containers.Count; i++)
			{
				Containers[i].SetActive(visible && i < num);
			}
		}

		internal static void EnsurePeekIfHidden()
		{
			if (!InventoryExpansionPreferences.Enabled)
			{
				return;
			}
			UIPrefab_Inventory val = LocalUi();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if ((Object)(object)_inventoryUi != (Object)(object)val || !_slotsMoved || (Object)(object)_backpackPanel == (Object)null)
			{
				if (Time.unscaledTime >= _nextBindAttempt)
				{
					_nextBindAttempt = Time.unscaledTime + 1f;
					Relayout(val);
				}
			}
			else
			{
				((Component)_backpackPanel).gameObject.SetActive(true);
				EnsureBadge();
			}
		}

		internal static void ToggleBackpack()
		{
			if (!((Object)(object)_backpackPanel == (Object)null) && !IsLoadingScreenActive() && !IsGamePaused())
			{
				_targetOpen = !_targetOpen;
				if (_animationCoroutine != null)
				{
					MelonCoroutines.Stop(_animationCoroutine);
				}
				((Component)_backpackPanel).gameObject.SetActive(true);
				ShowContents(_targetOpen);
				if (InventoryExpansionPreferences.AnimationSeconds == 0f)
				{
					ApplyOffset(_targetOpen ? _openOffset : 0f);
					_backpackFullyVisible = _targetOpen;
					_animationCoroutine = null;
				}
				else
				{
					_animationCoroutine = MelonCoroutines.Start(Animate(_targetOpen));
				}
			}
		}

		private static IEnumerator Animate(bool open)
		{
			float start = _offset;
			float target = (open ? _openOffset : 0f);
			float elapsed = 0f;
			_backpackFullyVisible = false;
			float duration = InventoryExpansionPreferences.AnimationSeconds;
			while (elapsed < duration && (Object)(object)_nativeHud != (Object)null)
			{
				elapsed += Time.unscaledDeltaTime;
				float num = Mathf.Clamp01(elapsed / duration);
				ApplyOffset(Mathf.Lerp(start, target, 1f - Mathf.Pow(1f - num, 3f)));
				yield return null;
			}
			ApplyOffset(target);
			_backpackFullyVisible = open;
			_animationCoroutine = null;
		}

		internal static void HideBackpackCompletely()
		{
			if (_targetOpen || _backpackFullyVisible)
			{
				GameMainBase main = Hub.Main;
				HandleCursorHandoff((main != null) ? main.GetMyAvatar() : null, opening: false);
			}
			if (_animationCoroutine != null)
			{
				MelonCoroutines.Stop(_animationCoroutine);
			}
			_animationCoroutine = null;
			_targetOpen = (_backpackFullyVisible = false);
			ApplyOffset(0f);
			if ((Object)(object)_backpackPanel != (Object)null)
			{
				((Component)_backpackPanel).gameObject.SetActive(false);
			}
		}

		internal static void ResetForSettingsChange()
		{
			HideBackpackCompletely();
			_savedStandardSlot = 0;
		}

		internal static void HandleCursorHandoff(ProtoActor avatar, bool opening)
		{
			if ((Object)(object)avatar == (Object)null || (opening ? (!InventoryExpansionPreferences.SelectBackpackSlotOnOpen) : (!InventoryExpansionPreferences.RestoreStandardSlotOnClose)))
			{
				return;
			}
			object obj = InventorySelectionHelper.GetActorInventoryField()?.GetValue(avatar);
			if (obj != null)
			{
				int selectedInventorySlotIndex = avatar.GetSelectedInventorySlotIndex();
				int num = InvenSlotLayout.FirstExtensionSlotKey - 1;
				if (opening && selectedInventorySlotIndex < num)
				{
					_savedStandardSlot = selectedInventorySlotIndex;
				}
				int num2 = (opening ? num : (avatar.GetVisibleInventorySlotIndices().Contains(_savedStandardSlot) ? _savedStandardSlot : 0));
				if (!opening || InventoryUiPatches.LogicalSlotCount > num)
				{
					InventorySelectionHelper.GetSelectSlotMethod(obj.GetType()).Invoke(obj, new object[1] { num2 });
				}
			}
		}

		internal static bool IsGamePaused()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Time.timeScale <= 0.01f)
				{
					return true;
				}
				if ((int)Cursor.lockState == 0 && Cursor.visible && IsInGame())
				{
					return true;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsInGame()
		{
			try
			{
				GameMainBase main = Hub.Main;
				ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				return val.AmIAvatar();
			}
			catch (Exception arg)
			{
				MelonLogger.Warning($"[InventoryExpansion][BackpackPanel] IsInGame check failed: {arg}");
				return false;
			}
		}

		private static bool IsInLoadingScreen()
		{
			try
			{
				if ((Object)(object)Hub.s == (Object)null)
				{
					return true;
				}
				GameMainBase main = Hub.Main;
				ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
				if ((Object)(object)val == (Object)null)
				{
					return true;
				}
				return !val.AmIAvatar();
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsLoadingScreenActive()
		{
			try
			{
				Hub s = Hub.s;
				if ((Object)(object)s == (Object)null)
				{
					return false;
				}
				if (_uimanField == null)
				{
					_uimanField = typeof(Hub).GetField("<uiman>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
				}
				object? obj = _uimanField?.GetValue(s);
				UIPrefab_Scene_Loading val = ((UIManager)(((obj is UIManager) ? obj : null)?)).ui_sceneloading;
				return (Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled;
			}
			catch
			{
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(ProtoActor), "Update")]
	internal static class BackpackInputUpdatePatch
	{
		private static bool wasKeyPressedLastFrame;

		[HarmonyPostfix]
		private unsafe static void Update_Postfix(ProtoActor __instance)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!InventoryExpansionPreferences.Enabled || !__instance.AmIAvatar())
				{
					return;
				}
				if (BackpackPanelPatch.IsLoadingScreenActive())
				{
					BackpackPanelPatch.HideBackpackCompletely();
					wasKeyPressedLastFrame = false;
					return;
				}
				if (BackpackPanelPatch.IsGamePaused())
				{
					BackpackPanelPatch.HideBackpackCompletely();
					wasKeyPressedLastFrame = false;
					return;
				}
				BackpackPanelPatch.EnsurePeekIfHidden();
				Keyboard current = Keyboard.current;
				if (current == null)
				{
					wasKeyPressedLastFrame = false;
					return;
				}
				KeyCode backpackKey = InventoryExpansionPreferences.BackpackKey;
				Key val = (Key)0;
				try
				{
					val = (Key)Enum.Parse(typeof(Key), ((object)(*(KeyCode*)(&backpackKey))/*cast due to .constrained prefix*/).ToString());
				}
				catch
				{
					MelonLogger.Warning($"[InventoryExpansion][BackpackPanel] Could not convert KeyCode {backpackKey} to Input System Key");
					return;
				}
				bool isPressed = ((ButtonControl)current[val]).isPressed;
				bool num = isPressed && !wasKeyPressedLastFrame;
				wasKeyPressedLastFrame = isPressed;
				if (num)
				{
					bool flag = !BackpackPanelPatch.IsBackpackOpen;
					if (flag && InventoryExpansionPreferences.BlockLargeItemsInBackpack && LargeItemBackpackPatch.IsLargeHeldItem(__instance))
					{
						LargeItemBackpackPatch.ShowBackpackBlockedMessage("You can't open the backpack while holding a large item.");
						return;
					}
					BackpackPanelPatch.ToggleBackpack();
					BackpackPanelPatch.HandleCursorHandoff(__instance, flag);
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Update postfix failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(ProtoActor))]
	internal static class BackpackMovementSpeedPatch
	{
		private static FieldInfo _netSyncActorDataField;

		private static bool _fieldInitialized;

		private static long _originalMoveSpeedWalk;

		private static long _originalMoveSpeedRun;

		private static bool _speedReduced;

		private static ProtoActor _speedReducedActor;

		internal static void ResetLocalState()
		{
			try
			{
				RestoreMoveSpeed(_speedReducedActor);
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Movement] Failed to restore local movement speed on settings change: {arg}");
			}
		}

		internal static void RestoreForActor(ProtoActor actor)
		{
			if (actor == _speedReducedActor)
			{
				ResetLocalState();
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("Update")]
		private static void Update_Postfix(ProtoActor __instance)
		{
			try
			{
				if (!__instance.AmIAvatar())
				{
					return;
				}
				if (!InventoryExpansionPreferences.Enabled)
				{
					RestoreMoveSpeed(__instance);
					return;
				}
				if (_speedReduced && _speedReducedActor != __instance)
				{
					ResetLocalState();
					if (_speedReduced)
					{
						return;
					}
				}
				if (!_fieldInitialized)
				{
					_netSyncActorDataField = typeof(ProtoActor).GetField("netSyncActorData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					_fieldInitialized = true;
				}
				if (_netSyncActorDataField == null)
				{
					return;
				}
				object value = _netSyncActorDataField.GetValue(__instance);
				if (value == null)
				{
					return;
				}
				FieldInfo field = value.GetType().GetField("MoveSpeedWalk", BindingFlags.Instance | BindingFlags.Public);
				FieldInfo field2 = value.GetType().GetField("MoveSpeedRun", BindingFlags.Instance | BindingFlags.Public);
				if (field == null || field2 == null)
				{
					return;
				}
				if (BackpackPanelPatch.IsBackpackFullyVisible && InventoryExpansionPreferences.ReduceMovementSpeed)
				{
					if (!_speedReduced)
					{
						_originalMoveSpeedWalk = (long)(field.GetValue(value) ?? ((object)350L));
						_originalMoveSpeedRun = (long)(field2.GetValue(value) ?? ((object)700L));
						_speedReduced = true;
						_speedReducedActor = __instance;
					}
					field.SetValue(value, (long)((float)_originalMoveSpeedWalk * InventoryExpansionPreferences.MovementSpeedMultiplier));
					field2.SetValue(value, (long)((float)_originalMoveSpeedRun * InventoryExpansionPreferences.MovementSpeedMultiplier));
				}
				else
				{
					RestoreMoveSpeed(value, field, field2);
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Movement] Movement speed patch failed: {arg}");
			}
		}

		private static void RestoreMoveSpeed(ProtoActor instance)
		{
			if (!_speedReduced || _netSyncActorDataField == null || instance != _speedReducedActor)
			{
				return;
			}
			object value = _netSyncActorDataField.GetValue(instance);
			if (value != null)
			{
				FieldInfo field = value.GetType().GetField("MoveSpeedWalk", BindingFlags.Instance | BindingFlags.Public);
				FieldInfo field2 = value.GetType().GetField("MoveSpeedRun", BindingFlags.Instance | BindingFlags.Public);
				if (field != null && field2 != null)
				{
					RestoreMoveSpeed(value, field, field2);
				}
			}
		}

		private static void RestoreMoveSpeed(object netSyncActorData, FieldInfo moveSpeedWalkField, FieldInfo moveSpeedRunField)
		{
			if (_speedReduced)
			{
				moveSpeedWalkField.SetValue(netSyncActorData, _originalMoveSpeedWalk);
				moveSpeedRunField.SetValue(netSyncActorData, _originalMoveSpeedRun);
				_speedReduced = false;
				_speedReducedActor = null;
				_originalMoveSpeedWalk = 0L;
				_originalMoveSpeedRun = 0L;
			}
		}
	}
	[HarmonyPatch(typeof(ProtoActor), "OnDestroy")]
	internal static class BackpackProtoActorDestroyPatch
	{
		[HarmonyPostfix]
		private static void OnDestroy_Postfix(ProtoActor __instance)
		{
			try
			{
				BackpackMovementSpeedPatch.RestoreForActor(__instance);
				if (__instance.AmIAvatar())
				{
					BackpackSceneChangeHandler.HideBackpack();
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] ProtoActor OnDestroy patch failed: {arg}");
			}
		}
	}
	internal static class BackpackSceneChangeHandler
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction<Scene, Scene> <0>__OnActiveSceneChanged;

			public static WillRenderCanvases <1>__FollowNativeHud;
		}

		private static bool _initialized;

		internal static void Initialize()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if (!_initialized)
			{
				SceneManager.activeSceneChanged += OnActiveSceneChanged;
				object obj = <>O.<1>__FollowNativeHud;
				if (obj == null)
				{
					WillRenderCanvases val = BackpackPanelPatch.FollowNativeHud;
					<>O.<1>__FollowNativeHud = val;
					obj = (object)val;
				}
				Canvas.willRenderCanvases += (WillRenderCanvases)obj;
				_initialized = true;
			}
		}

		private static void OnActiveSceneChanged(Scene previousScene, Scene newScene)
		{
			try
			{
				if (!IsInGame())
				{
					HideBackpack();
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Scene change handler failed: {arg}");
			}
		}

		private static bool IsInGame()
		{
			try
			{
				GameMainBase main = Hub.Main;
				ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				return val.AmIAvatar();
			}
			catch
			{
				return false;
			}
		}

		internal static void HideBackpack()
		{
			BackpackPanelPatch.HideBackpackCompletely();
			BackpackMovementSpeedPatch.ResetLocalState();
		}
	}
	[HarmonyPatch]
	internal static class InventoryControllerPatches
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(InventoryController), "Reset", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(InventoryController), "RecalculateSlotCount", (Type[])null, (Type[])null);
		}

		private static int GetBasicSlotCount(InventoryController inventory)
		{
			return 4 + ((inventory.HasDedicatedSlots && InventoryExpansionPreferences.Enabled) ? InventoryExpansionPreferences.AdditionalSlots : 0);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(InventoryController), "GetDedicatedSlotCount", (Type[])null, (Type[])null);
			for (int i = 0; i + 3 < list.Count; i++)
			{
				if (CodeInstructionExtensions.LoadsConstant(list[i], 4L) && !(list[i + 1].opcode != OpCodes.Ldarg_0) && CodeInstructionExtensions.Calls(list[i + 2], methodInfo) && !(list[i + 3].opcode != OpCodes.Add))
				{
					list[i].opcode = OpCodes.Ldarg_0;
					list[i].operand = null;
					list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(InventoryControllerPatches), "GetBasicSlotCount", (Type[])null, (Type[])null)));
					return list;
				}
			}
			throw new InvalidOperationException("[InventoryExpansion] Native base inventory capacity calculation was not found.");
		}
	}
	[HarmonyPatch(typeof(InventoryController), "FindSlotForAdd")]
	internal static class InventoryPickupPatches
	{
		private static readonly FieldInfo SelfField = AccessTools.Field(typeof(InventoryController), "_self");

		private static readonly FieldInfo SlotsField = AccessTools.Field(typeof(InventoryController), "_inventorySlots");

		private static void Postfix(InventoryController __instance, ref int __result)
		{
			if (!InventoryExpansionPreferences.Enabled || !InventoryExpansionPreferences.FillBackpackFirst || !BackpackPanelPatch.IsBackpackFullyVisible || __result <= 0 || __result > 4)
			{
				return;
			}
			object? value = SelfField.GetValue(__instance);
			VActor val = (VActor)((value is VActor) ? value : null);
			GameMainBase main = Hub.Main;
			ProtoActor val2 = ((main != null) ? main.GetMyAvatar() : null);
			if (val == null || (Object)(object)val2 == (Object)null || val.ObjectID != val2.ActorID)
			{
				return;
			}
			Dictionary<int, ItemElement> obj = (Dictionary<int, ItemElement>)SlotsField.GetValue(__instance);
			int num = 0;
			foreach (KeyValuePair<int, ItemElement> item in obj)
			{
				if (item.Key >= InvenSlotLayout.FirstExtensionSlotKey && item.Value == null && !__instance.IsSlotLocked(item.Key) && (num == 0 || item.Key < num))
				{
					num = item.Key;
				}
			}
			if (num != 0)
			{
				__result = num;
			}
		}
	}
	internal static class InventorySelectionHelper
	{
		internal static Type GetInventoryType()
		{
			return typeof(ProtoActor).GetNestedType("Inventory", BindingFlags.Public | BindingFlags.NonPublic);
		}

		internal static FieldInfo GetSelectedSlotIndexField(Type inventoryType)
		{
			return inventoryType?.GetField("selectedSlotIndex", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		internal static FieldInfo GetSlotSizeField(Type inventoryType)
		{
			return inventoryType?.GetField("slotSize", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		internal static FieldInfo GetActorInventoryField()
		{
			return typeof(ProtoActor).GetField("inventory", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		internal static MethodInfo GetSelectSlotMethod(Type inventoryType)
		{
			return inventoryType?.GetMethod("SelectSlot", BindingFlags.Instance | BindingFlags.NonPublic);
		}
	}
	[HarmonyPatch]
	internal static class InventorySelectionNextPatches
	{
		private static MethodBase TargetMethod()
		{
			return InventorySelectionHelper.GetInventoryType()?.GetMethod("SelectNextSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		[HarmonyPrefix]
		private static bool SelectNextSlot_Prefix(object __instance)
		{
			return InventorySelectionCommon.HandleSlotSelection(__instance, forward: true);
		}
	}
	[HarmonyPatch]
	internal static class InventorySelectionPreviousPatches
	{
		private static MethodBase TargetMethod()
		{
			return InventorySelectionHelper.GetInventoryType()?.GetMethod("SelectPreviousSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		[HarmonyPrefix]
		private static bool SelectPreviousSlot_Prefix(object __instance)
		{
			return InventorySelectionCommon.HandleSlotSelection(__instance, forward: false);
		}
	}
	internal static class InventorySelectionCommon
	{
		internal static bool HandleSlotSelection(object __instance, bool forward)
		{
			try
			{
				if (!InventoryExpansionPreferences.Enabled)
				{
					return true;
				}
				if (__instance == null)
				{
					return true;
				}
				Type type = __instance.GetType();
				FieldInfo selectedSlotIndexField = InventorySelectionHelper.GetSelectedSlotIndexField(type);
				FieldInfo slotSizeField = InventorySelectionHelper.GetSlotSizeField(type);
				if (selectedSlotIndexField == null || slotSizeField == null)
				{
					return true;
				}
				int item = (int)selectedSlotIndexField.GetValue(__instance);
				bool open = BackpackPanelPatch.IsBackpackFullyVisible;
				List<int> list = ((List<int>)type.GetMethod("GetVisibleSlotIndices").Invoke(__instance, null)).FindAll((int num2) => num2 >= InvenSlotLayout.FirstExtensionSlotKey - 1 == open);
				if (list.Count == 0)
				{
					return false;
				}
				int num = list.IndexOf(item);
				int index = ((num >= 0) ? ((num + (forward ? 1 : (-1)) + list.Count) % list.Count) : ((!forward) ? (list.Count - 1) : 0));
				InventorySelectionHelper.GetSelectSlotMethod(type).Invoke(__instance, new object[1] { list[index] });
				return false;
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Selection] Slot selection prefix failed: {arg}");
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(UIPrefab_Inventory))]
	internal static class InventoryUiPatches
	{
		private static UIPrefab_Inventory _currentUi;

		internal static readonly FieldInfo SlotsField = AccessTools.Field(typeof(UIPrefab_Inventory), "inventorySlots");

		internal static int LogicalSlotCount { get; private set; }

		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		private static void AwakePostfix(UIPrefab_Inventory __instance)
		{
			EnsureSlots(__instance, 0);
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateSlot")]
		private static void UpdatePrefix(UIPrefab_Inventory __instance, List<InventoryItem> inventoryItems)
		{
			EnsureSlots(__instance, inventoryItems.Count);
		}

		private static void EnsureSlots(UIPrefab_Inventory ui, int count)
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Expected O, but got Unknown
			if (InventoryExpansionPreferences.Enabled)
			{
				List<Slot> list = (List<Slot>)SlotsField.GetValue(ui);
				int num = InvenSlotLayout.FirstExtensionSlotKey - 1;
				int num2 = Math.Max(count, num + InventoryExpansionPreferences.AdditionalSlots);
				bool flag = (Object)(object)_currentUi != (Object)(object)ui || LogicalSlotCount != num2 || list.Count < num2;
				_currentUi = ui;
				LogicalSlotCount = num2;
				Slot val = list[num] ?? throw new InvalidOperationException("[InventoryExpansion] Native extension slot template is missing.");
				while (list.Count < num2)
				{
					GameObject val2 = Object.Instantiate<GameObject>(val.root, val.root.transform.parent, false);
					((Object)val2).name = "InventoryExpansion_Slot_" + list.Count;
					val2.SetActive(false);
					list.Add(new Slot
					{
						root = val2,
						group = val.group,
						itemGroup = ((Component)CloneNode(val.itemGroup.transform, val.root.transform, val2.transform)).gameObject,
						frame = CloneNode(val.frame, val.root.transform, val2.transform),
						waitEvent = CloneNode(val.waitEvent, val.root.transform, val2.transform),
						image = ((Component)CloneNode(((Component)val.image).transform, val.root.transform, val2.transform)).GetComponent<Image>(),
						stackCount = ((Component)CloneNode(val.stackCount.transform, val.root.transform, val2.transform)).GetComponent<TMP_Text>()
					});
				}
				if (flag)
				{
					BackpackPanelPatch.Relayout(ui);
				}
			}
		}

		private static Transform CloneNode(Transform node, Transform source, Transform target)
		{
			if ((Object)(object)node == (Object)(object)source)
			{
				return target;
			}
			Stack<int> stack = new Stack<int>();
			Transform val = node;
			while ((Object)(object)val != (Object)(object)source)
			{
				if ((Object)(object)val == (Object)null)
				{
					throw new InvalidOperationException("[InventoryExpansion] Native slot reference leaves its root.");
				}
				stack.Push(val.GetSiblingIndex());
				val = val.parent;
			}
			while (stack.Count > 0)
			{
				target = target.GetChild(stack.Pop());
			}
			return target;
		}
	}
	[HarmonyPatch(typeof(LootingLevelObject))]
	internal static class LargeItemBackpackPatch
	{
		private static PropertyInfo _tablemanProp;

		private static FieldInfo _uiprefabsField;

		private static bool _reflectionInitialized;

		private static float _lastMessageTime;

		private static void EnsureReflection()
		{
			if (!_reflectionInitialized)
			{
				_reflectionInitialized = true;
				_tablemanProp = typeof(Hub).GetProperty("tableman", BindingFlags.Instance | BindingFlags.NonPublic);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("TryInteract")]
		private static bool TryInteract_Prefix(LootingLevelObject __instance, ref bool __result)
		{
			try
			{
				if (!InventoryExpansionPreferences.Enabled || !InventoryExpansionPreferences.BlockLargeItemsInBackpack)
				{
					return true;
				}
				if (!BackpackPanelPatch.IsBackpackFullyVisible)
				{
					return true;
				}
				ItemMasterInfo itemMasterInfo = Inventory.GetItemMasterInfo(__instance.itemMasterID);
				if (itemMasterInfo == null || !itemMasterInfo.ForbidChange)
				{
					return true;
				}
				ShowTooLargeMessage();
				__result = false;
				return false;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[InventoryExpansion][LargeItem] TryInteract prefix failed, allowing default grab: " + ex.Message);
				return true;
			}
		}

		internal static bool IsLargeHeldItem(ProtoActor actor)
		{
			try
			{
				int result;
				if (actor == null)
				{
					result = 0;
				}
				else
				{
					InventoryItem selectedInventoryItem = actor.GetSelectedInventoryItem();
					result = ((((selectedInventoryItem == null) ? ((bool?)null) : selectedInventoryItem.MasterInfo?.ForbidChange) == true) ? 1 : 0);
				}
				return (byte)result != 0;
			}
			catch
			{
				return false;
			}
		}

		private static void ShowTooLargeMessage()
		{
			ShowBackpackBlockedMessage("This is too large for your backpack.");
		}

		internal static void ShowBackpackBlockedMessage(string message)
		{
			try
			{
				float unscaledTime = Time.unscaledTime;
				if (unscaledTime - _lastMessageTime < 1f)
				{
					return;
				}
				_lastMessageTime = unscaledTime;
				EnsureReflection();
				object s = Hub.s;
				object obj = ((s == null) ? null : _tablemanProp?.GetValue(s));
				if (obj != null)
				{
					if (_uiprefabsField == null)
					{
						_uiprefabsField = obj.GetType().GetField("uiprefabs", BindingFlags.Instance | BindingFlags.Public);
					}
					object? obj2 = _uiprefabsField?.GetValue(obj);
					MMUIPrefabTable val = (MMUIPrefabTable)((obj2 is MMUIPrefabTable) ? obj2 : null);
					if (val != null)
					{
						val.ShowTimerDialog("ToastSimple", 0f, new object[1] { message });
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[InventoryExpansion][LargeItem] showing message failed: " + ex.Message);
			}
		}
	}
}
namespace InventoryExpansion.Config
{
	internal static class InventoryExpansionPreferences
	{
		private const string CategoryId = "InventoryExpansion";

		private static MelonPreferences_Category _category;

		private static MelonPreferences_Entry<bool> _enabled;

		private static MelonPreferences_Entry<int> _additionalSlots;

		private static MelonPreferences_Entry<string> _backpackKey;

		private static MelonPreferences_Entry<bool> _reduceMovementSpeed;

		private static MelonPreferences_Entry<bool> _selectBackpackSlotOnOpen;

		private static MelonPreferences_Entry<bool> _restoreStandardSlotOnClose;

		private static MelonPreferences_Entry<bool> _fillBackpackFirst;

		private static MelonPreferences_Entry<bool> _blockLargeItemsInBackpack;

		private static HostSetting<bool> _effectiveEnabled;

		private static HostSetting<int> _effectiveAdditionalSlots;

		private static HostSetting<bool> _effectiveReduceMovementSpeed;

		private static HostSetting<bool> _effectiveFillBackpackFirst;

		private static HostSetting<bool> _effectiveBlockLargeItemsInBackpack;

		private static HostSetting<float> _movementSpeedMultiplier;

		private static MelonPreferences_Entry<float> _animationSeconds;

		internal static bool Enabled => _effectiveEnabled?.Value ?? _enabled?.Value ?? true;

		internal static int AdditionalSlots => _effectiveAdditionalSlots?.Value ?? ClampToValidBackpackSize(_additionalSlots?.Value ?? 4);

		internal static KeyCode BackpackKey
		{
			get
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				if (_backpackKey == null)
				{
					return (KeyCode)99;
				}
				string value = _backpackKey.Value;
				if (string.IsNullOrEmpty(value))
				{
					return (KeyCode)99;
				}
				if (Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result))
				{
					return result;
				}
				return (KeyCode)99;
			}
		}

		internal static bool ReduceMovementSpeed => _effectiveReduceMovementSpeed?.Value ?? _reduceMovementSpeed?.Value ?? true;

		internal static float MovementSpeedMultiplier => _movementSpeedMultiplier?.Value ?? 1f;

		internal static float AnimationSeconds
		{
			get
			{
				if (_animationSeconds == null || float.IsNaN(_animationSeconds.Value) || float.IsInfinity(_animationSeconds.Value))
				{
					return 0.25f;
				}
				return Mathf.Clamp(_animationSeconds.Value, 0f, 2f);
			}
		}

		internal static bool SelectBackpackSlotOnOpen => _selectBackpackSlotOnOpen?.Value ?? true;

		internal static bool RestoreStandardSlotOnClose => _restoreStandardSlotOnClose?.Value ?? true;

		internal static bool FillBackpackFirst => _effectiveFillBackpackFirst?.Value ?? _fillBackpackFirst?.Value ?? true;

		internal static bool BlockLargeItemsInBackpack => _effectiveBlockLargeItemsInBackpack?.Value ?? _blockLargeItemsInBackpack?.Value ?? true;

		internal static event Action EffectiveValuesChanged;

		internal static void Initialize()
		{
			if (_category == null)
			{
				_category = MelonPreferences.CreateCategory("InventoryExpansion", "InventoryExpansion");
				_enabled = CreateEntry("Enabled", defaultValue: true, "Enabled", "Enable InventoryExpansion functionality. When disabled, the mod will not modify game behavior.");
				_additionalSlots = CreateEntry("AdditionalSlots", 4, "Additional Inventory Slots", "Number of extra inventory slots to add on top of the game's default inventory size. Any whole number from 4 to 24 is supported. Rows stay balanced with at most eight slots each. Values outside this range are clamped.");
				_backpackKey = CreateEntry("BackpackKey", "C", "Backpack Toggle Key", "Key to toggle backpack visibility. Press to switch between standard inventory and backpack.");
				_reduceMovementSpeed = CreateEntry("ReduceMovementSpeed", defaultValue: true, "Reduce Movement Speed", "Host setting, live. Apply MovementSpeedMultiplier while the backpack is fully open.");
				_selectBackpackSlotOnOpen = CreateEntry("SelectBackpackSlotOnOpen", defaultValue: true, "Select Backpack Slot On Open", "When opening the backpack, move the selected slot to the first backpack slot.");
				_restoreStandardSlotOnClose = CreateEntry("RestoreStandardSlotOnClose", defaultValue: true, "Restore Standard Slot On Close", "When closing the backpack, return the selected slot to the standard inventory (the slot that was selected before opening).");
				_fillBackpackFirst = CreateEntry("FillBackpackFirst", defaultValue: true, "Fill Backpack First", "While the backpack is open, picked-up items fill the backpack slots before the standard inventory. Takes effect only when you are the host or in single-player.");
				_blockLargeItemsInBackpack = CreateEntry("BlockLargeItemsInBackpack", defaultValue: true, "Block Large Items In Backpack", "When enabled, large items (the ones you cannot switch away from while holding them, e.g. two-handed items) cannot be picked up into the backpack while it is open. Trying to do so shows a message instead. Disable to allow large items in the backpack.");
				_additionalSlots.Value = ClampToValidBackpackSize(_additionalSlots.Value);
				_animationSeconds = CreateEntry("AnimationSeconds", 0.25f, "Backpack Animation Time", "Local display setting. HUD animation duration in seconds, 0-2. Zero opens immediately. Default: 0.25");
				HostSettings hostSettings = new HostSettings("InventoryExpansion");
				_effectiveEnabled = hostSettings.Register(_enabled, vanillaValue: false);
				_effectiveAdditionalSlots = hostSettings.Register(_additionalSlots, 0, (int value) => value == 0 || (value >= 4 && value <= 24));
				_effectiveReduceMovementSpeed = hostSettings.Register(_reduceMovementSpeed, vanillaValue: false, null, live: true);
				_effectiveFillBackpackFirst = hostSettings.Register(_fillBackpackFirst, vanillaValue: false, null, live: true);
				_effectiveBlockLargeItemsInBackpack = hostSettings.Register(_blockLargeItemsInBackpack, vanillaValue: false, null, live: true);
				_movementSpeedMultiplier = hostSettings.Register(CreateEntry("MovementSpeedMultiplier", 0.5f, "Backpack Movement Multiplier", "Host setting, live. Walking and running speed while the backpack is open, 0-1. Default: 0.5"), 1f, (float value) => value >= 0f && value <= 1f, live: true, legacy: false);
				hostSettings.EffectiveValuesChanged += delegate
				{
					InventoryExpansionPreferences.EffectiveValuesChanged?.Invoke();
				};
			}
		}

		private static MelonPreferences_Entry<T> CreateEntry<T>(string identifier, T defaultValue, string displayName, string description = null)
		{
			if (_category == null)
			{
				throw new InvalidOperationException("Preference category not initialized.");
			}
			return _category.CreateEntry<T>(identifier, defaultValue, displayName, description, false, false, (ValueValidator)null, (string)null);
		}

		private static int ClampToValidBackpackSize(int value)
		{
			return Math.Clamp(value, 4, 24);
		}
	}
}