Decompiled source of Valheim ItemInfiniteStack v1.0.1

Valheim.ItemInfiniteStack.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;
using Valheim.ItemInfiniteStack.Configuration;
using Valheim.ItemInfiniteStack.Core;
using Valheim.ItemInfiniteStack.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Valheim.ItemInfiniteStack")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+77677a2a29189ae285a5e4b860f4687c514ee127")]
[assembly: AssemblyProduct("Valheim.ItemInfiniteStack")]
[assembly: AssemblyTitle("Valheim.ItemInfiniteStack")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
namespace Valheim.ItemInfiniteStack
{
	[BepInPlugin("ckforgame.ItemInfiniteStack", "Valheim.ItemInfiniteStack", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "ckforgame.ItemInfiniteStack";

		public const string ModName = "Valheim.ItemInfiniteStack";

		public const string ModVersion = "1.0.1";

		private Harmony _harmony;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PluginConfig.Initialize(((BaseUnityPlugin)this).Config);
			PluginConfig.OnConfigChanged += HandleConfigChanged;
			_harmony = new Harmony("ckforgame.ItemInfiniteStack");
			_harmony.PatchAll();
			Log.LogInfo((object)string.Format("{0} v{1} loaded successfully! InfiniteStackLimit: {2}", "Valheim.ItemInfiniteStack", "1.0.1", PluginConfig.InfiniteStackLimit.Value));
		}

		private void OnDestroy()
		{
			PluginConfig.OnConfigChanged -= HandleConfigChanged;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
		}

		private void Update()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (PluginConfig.ModEnabled.Value && InventoryGui.IsVisible() && !TextInput.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()))
			{
				KeyboardShortcut value = PluginConfig.ConsolidateHotkey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					InventoryConsolidator.ConsolidateActiveInventories();
				}
			}
		}

		private static void HandleConfigChanged()
		{
			if (PluginConfig.LoggingEnabled.Value)
			{
				Log.LogInfo((object)"Configuration changed, refreshing ObjectDB and inventories...");
			}
			StackManager.PatchObjectDB();
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				StackManager.PatchPlayerInventory(Player.m_localPlayer);
			}
		}
	}
}
namespace Valheim.ItemInfiniteStack.UI
{
	public static class ItemStackFormatter
	{
		private static readonly string[] SmallNumberCache;

		static ItemStackFormatter()
		{
			SmallNumberCache = new string[1001];
			for (int i = 0; i <= 1000; i++)
			{
				SmallNumberCache[i] = i.ToString();
			}
		}

		public static string FastIntToString(int value)
		{
			if (value >= 0 && value <= 1000)
			{
				return SmallNumberCache[value];
			}
			return value.ToString();
		}

		public static string FormatCompact(int value)
		{
			if (value < 0)
			{
				return "0";
			}
			if (value < 1000)
			{
				return FastIntToString(value);
			}
			if (value < 1000000)
			{
				float num = (float)value / 1000f;
				if (value % 1000 != 0 && !(num >= 100f))
				{
					return $"{num:0.#}k";
				}
				return $"{Mathf_Floor(num)}k";
			}
			if (value < 1000000000)
			{
				float num2 = (float)value / 1000000f;
				if (value % 1000000 != 0 && !(num2 >= 100f))
				{
					return $"{num2:0.#}M";
				}
				return $"{Mathf_Floor(num2)}M";
			}
			float num3 = (float)value / 1E+09f;
			return $"{num3:0.#}B";
		}

		private static int Mathf_Floor(float val)
		{
			return (int)val;
		}

		public static void FormatElementAmount(InventoryElement element, ItemData item)
		{
			if ((Object)(object)element?.m_amount == (Object)null || item?.m_shared == null)
			{
				return;
			}
			if (item.m_shared.m_maxStackSize <= 1)
			{
				((Behaviour)element.m_amount).enabled = false;
				return;
			}
			if (item.m_stack <= 1 && PluginConfig.HideSingleItemCount.Value)
			{
				((Behaviour)element.m_amount).enabled = false;
				return;
			}
			((Behaviour)element.m_amount).enabled = true;
			switch (PluginConfig.DisplayFormat.Value)
			{
			default:
				if (item.m_stack >= PluginConfig.CompactThreshold.Value)
				{
					element.m_amount.text = FormatCompact(item.m_stack);
				}
				else
				{
					element.m_amount.text = FastIntToString(item.m_stack);
				}
				break;
			case StackDisplayMode.Compact:
				if (item.m_shared.m_maxStackSize >= 1000)
				{
					element.m_amount.text = FormatCompact(item.m_stack);
				}
				else
				{
					element.m_amount.text = FormatCompact(item.m_stack) + "/" + FormatCompact(item.m_shared.m_maxStackSize);
				}
				break;
			case StackDisplayMode.StandardRatio:
				if (item.m_shared.m_maxStackSize >= 1000)
				{
					element.m_amount.text = FastIntToString(item.m_stack) + "/" + FormatCompact(item.m_shared.m_maxStackSize);
				}
				else
				{
					element.m_amount.text = FastIntToString(item.m_stack) + "/" + FastIntToString(item.m_shared.m_maxStackSize);
				}
				break;
			}
		}
	}
}
namespace Valheim.ItemInfiniteStack.Patches
{
	[HarmonyPatch(typeof(InventoryGrid))]
	public static class InventoryGridPatches
	{
		[HarmonyPatch("UpdateInventory")]
		[HarmonyPrefix]
		public static void UpdateInventory_Prefix(Inventory inventory)
		{
			if (PluginConfig.ModEnabled.Value && inventory != null)
			{
				StackManager.PatchInventory(inventory);
			}
		}

		[HarmonyPatch("UpdateGui")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void UpdateGui_Postfix(InventoryGrid __instance)
		{
			if (!PluginConfig.ModEnabled.Value || (Object)(object)__instance == (Object)null || __instance.m_inventory == null)
			{
				return;
			}
			int width = __instance.m_inventory.GetWidth();
			List<ItemData> allItems = __instance.m_inventory.GetAllItems();
			if (allItems == null)
			{
				return;
			}
			foreach (ItemData item in allItems)
			{
				if (item?.m_shared == null)
				{
					continue;
				}
				InventoryElement element = __instance.GetElement(item.m_gridPos.x, item.m_gridPos.y, width);
				if (!((Object)(object)element == (Object)null) && !((Object)(object)element.m_amount == (Object)null))
				{
					try
					{
						ItemStackFormatter.FormatElementAmount(element, item);
					}
					catch
					{
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(Inventory))]
	public static class InventoryPatches
	{
		[HarmonyPatch("CanAddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int)
		})]
		[HarmonyPrefix]
		public static bool CanAddItem_Prefix(Inventory __instance, ItemData item, int stack, ref bool __result)
		{
			if (!PluginConfig.ModEnabled.Value || item?.m_shared == null || __instance?.m_inventory == null)
			{
				return true;
			}
			string prefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
			StackManager.ApplyStackModifications(item.m_shared, prefabName);
			if (!ItemClassifier.Classify(item.m_shared, prefabName).IsStackable)
			{
				return true;
			}
			if (stack <= 0)
			{
				stack = item.m_stack;
			}
			long num = 0L;
			foreach (ItemData item2 in __instance.m_inventory)
			{
				if (item2?.m_shared != null && item2.m_shared.m_name == item.m_shared.m_name && Mathf.Approximately((float)item2.m_worldLevel, (float)item.m_worldLevel))
				{
					StackManager.ApplyStackModifications(item2.m_shared, ((Object)(object)item2.m_dropPrefab != (Object)null) ? ((Object)item2.m_dropPrefab).name : null);
					if (item2.m_stack < item2.m_shared.m_maxStackSize)
					{
						num += item2.m_shared.m_maxStackSize - item2.m_stack;
					}
				}
			}
			if (num >= stack)
			{
				__result = true;
				return false;
			}
			int num2 = __instance.m_width * __instance.m_height - __instance.m_inventory.Count;
			if (num2 > 0)
			{
				long num3 = (long)num2 * (long)item.m_shared.m_maxStackSize;
				long num4 = num + num3;
				__result = num4 >= stack;
				return false;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch("FindFreeStackSpace", new Type[]
		{
			typeof(string),
			typeof(float)
		})]
		[HarmonyPrefix]
		public static bool FindFreeStackSpace_Prefix(Inventory __instance, string name, float worldLevel, ref int __result)
		{
			if (!PluginConfig.ModEnabled.Value || __instance == null || __instance.m_inventory == null)
			{
				return true;
			}
			long num = 0L;
			foreach (ItemData item in __instance.m_inventory)
			{
				if (item?.m_shared != null && item.m_shared.m_name == name && Mathf.Approximately((float)item.m_worldLevel, worldLevel))
				{
					StackManager.ApplyStackModifications(item.m_shared, ((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
					if (item.m_stack < item.m_shared.m_maxStackSize)
					{
						num += item.m_shared.m_maxStackSize - item.m_stack;
					}
				}
			}
			__result = (int)Math.Min(num, 999999999L);
			return false;
		}

		[HarmonyPatch("FindFreeStackItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(float)
		})]
		[HarmonyPrefix]
		public static bool FindFreeStackItem_Prefix(Inventory __instance, string name, int quality, float worldLevel, ref ItemData __result)
		{
			if (!PluginConfig.ModEnabled.Value || __instance == null || __instance.m_inventory == null)
			{
				return true;
			}
			foreach (ItemData item in __instance.m_inventory)
			{
				if (item?.m_shared != null && item.m_shared.m_name == name && item.m_quality == quality && Mathf.Approximately((float)item.m_worldLevel, worldLevel))
				{
					StackManager.ApplyStackModifications(item.m_shared, ((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
					if (item.m_stack < item.m_shared.m_maxStackSize)
					{
						__result = item;
						return false;
					}
				}
			}
			__result = null;
			return false;
		}

		[HarmonyPatch("AddItem", new Type[] { typeof(ItemData) })]
		[HarmonyPrefix]
		public static bool AddItem_Prefix(Inventory __instance, ItemData item, ref bool __result)
		{
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: 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_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.ModEnabled.Value || item?.m_shared == null || __instance?.m_inventory == null)
			{
				return true;
			}
			string prefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
			StackManager.ApplyStackModifications(item.m_shared, prefabName);
			if (!ItemClassifier.Classify(item.m_shared, prefabName).IsStackable || item.m_shared.m_maxStackSize <= 1)
			{
				return true;
			}
			bool flag = true;
			bool flag2 = item.m_cheated && Achievements.IsCheatedAtAll();
			int num = item.m_stack;
			while (num > 0)
			{
				ItemData val = __instance.FindFreeStackItem(item.m_shared.m_name, item.m_quality, (float)item.m_worldLevel);
				if (val != null)
				{
					int num2 = val.m_shared.m_maxStackSize - val.m_stack;
					if (num2 <= 0)
					{
						break;
					}
					int num3 = Math.Min(num2, num);
					val.m_stack += num3;
					num -= num3;
					if (flag2 && !PlayerProfile.s_bypassCheatChecks)
					{
						val.m_cheated = true;
					}
				}
				else
				{
					item.m_stack = num;
					Vector2i val2 = __instance.FindEmptySlot(__instance.TopFirst(item));
					if (val2.x < 0)
					{
						flag = false;
						ZLog.LogWarning((object)$"Trying to add item to occupied slot {val2.x}, {val2.y}");
						break;
					}
					item.m_gridPos = val2;
					__instance.m_inventory.Add(item);
					num = 0;
				}
			}
			if (num == 0 && !__instance.m_inventory.Contains(item))
			{
				item.m_stack = 0;
			}
			else if (num > 0)
			{
				item.m_stack = num;
			}
			__instance.Changed(flag, flag2);
			__result = flag;
			return false;
		}

		[HarmonyPatch("AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(Vector2i)
		})]
		[HarmonyPrefix]
		public static bool AddItemPos_Prefix(Inventory __instance, ItemData item, Vector2i pos, ref bool __result)
		{
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: 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_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: 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_016b: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.ModEnabled.Value || item?.m_shared == null || __instance?.m_inventory == null)
			{
				return true;
			}
			string prefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
			StackManager.ApplyStackModifications(item.m_shared, prefabName);
			if (!ItemClassifier.Classify(item.m_shared, prefabName).IsStackable || item.m_shared.m_maxStackSize <= 1)
			{
				return true;
			}
			bool flag = true;
			bool flag2 = item.m_cheated && Achievements.IsCheatedAtAll();
			int num = item.m_stack;
			while (num > 0)
			{
				ItemData val = __instance.FindFreeStackItem(item.m_shared.m_name, item.m_quality, (float)item.m_worldLevel);
				if (val != null)
				{
					int num2 = val.m_shared.m_maxStackSize - val.m_stack;
					if (num2 <= 0)
					{
						break;
					}
					int num3 = Math.Min(num2, num);
					val.m_stack += num3;
					num -= num3;
					if (flag2 && !PlayerProfile.s_bypassCheatChecks)
					{
						val.m_cheated = true;
					}
					continue;
				}
				item.m_stack = num;
				if (__instance.GetItemAt(pos.x, pos.y) == null)
				{
					item.m_gridPos = pos;
					__instance.m_inventory.Add(item);
					num = 0;
					continue;
				}
				Vector2i val2 = __instance.FindEmptySlot(__instance.TopFirst(item));
				if (val2.x >= 0)
				{
					item.m_gridPos = val2;
					__instance.m_inventory.Add(item);
					num = 0;
					continue;
				}
				flag = false;
				ZLog.LogWarning((object)$"Trying to add item to occupied slot {pos.x}, {pos.y}");
				break;
			}
			if (num == 0 && !__instance.m_inventory.Contains(item))
			{
				item.m_stack = 0;
			}
			else if (num > 0)
			{
				item.m_stack = num;
			}
			__instance.Changed(flag, flag2);
			__result = flag;
			return false;
		}

		[HarmonyPatch("AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		[HarmonyPrefix]
		public static void AddItemSlot_Prefix(Inventory __instance, ItemData item, int x, int y)
		{
			if (PluginConfig.ModEnabled.Value && item?.m_shared != null && __instance != null)
			{
				StackManager.ApplyStackModifications(item.m_shared, ((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
				ItemData itemAt = __instance.GetItemAt(x, y);
				if (itemAt?.m_shared != null)
				{
					StackManager.ApplyStackModifications(itemAt.m_shared, ((Object)(object)itemAt.m_dropPrefab != (Object)null) ? ((Object)itemAt.m_dropPrefab).name : null);
				}
			}
		}

		[HarmonyPatch("Load", new Type[] { typeof(ZPackage) })]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void Load_Postfix(Inventory __instance)
		{
			if (PluginConfig.ModEnabled.Value && __instance != null)
			{
				StackManager.PatchInventory(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Player))]
	public static class PlayerPatches
	{
		[HarmonyPatch("Load")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void Load_Postfix(Player __instance)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)__instance == (Object)null))
			{
				StackManager.PatchPlayerInventory(__instance);
			}
		}

		[HarmonyPatch("OnSpawned", new Type[] { typeof(bool) })]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void OnSpawned_Postfix(Player __instance)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)__instance == (Object)null))
			{
				StackManager.PatchObjectDB();
				StackManager.PatchPlayerInventory(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Container))]
	public static class ContainerPatches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void Awake_Postfix(Container __instance)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)__instance == (Object)null))
			{
				StackManager.PatchInventory(__instance.GetInventory());
			}
		}

		[HarmonyPatch("CheckForChanges")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void CheckForChanges_Postfix(Container __instance)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)__instance == (Object)null))
			{
				StackManager.PatchInventory(__instance.GetInventory());
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui))]
	public static class InventoryGuiPatches
	{
		[HarmonyPatch("Show")]
		[HarmonyPostfix]
		public static void Show_Postfix(Container container)
		{
			if (PluginConfig.ModEnabled.Value)
			{
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					StackManager.PatchPlayerInventory(Player.m_localPlayer);
				}
				if ((Object)(object)container != (Object)null)
				{
					StackManager.PatchInventory(container.GetInventory());
				}
				else if ((Object)(object)InventoryGui.instance != (Object)null && (Object)(object)InventoryGui.instance.m_currentContainer != (Object)null)
				{
					StackManager.PatchInventory(InventoryGui.instance.m_currentContainer.GetInventory());
				}
				if (PluginConfig.EnableAutoConsolidateOnOpen.Value)
				{
					InventoryConsolidator.ConsolidateActiveInventories(showMessage: false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ItemDrop))]
	public static class ItemDropPatches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void Awake_Postfix(ItemDrop __instance)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)__instance == (Object)null) && __instance.m_itemData?.m_shared != null)
			{
				string prefabName = (((Object)(object)((Component)__instance).gameObject != (Object)null) ? ((Object)((Component)__instance).gameObject).name : null);
				StackManager.ApplyStackModifications(__instance.m_itemData.m_shared, prefabName);
			}
		}
	}
	[HarmonyPatch]
	public static class ItemDropOnCreateNewPatch
	{
		[HarmonyTargetMethods]
		public static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(ItemDrop), "OnCreateNew", new Type[2]
			{
				typeof(ItemDrop),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo != null)
			{
				yield return methodInfo;
				yield break;
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ItemDrop), "OnCreateNew", new Type[1] { typeof(ItemDrop) }, (Type[])null);
			if (methodInfo2 != null)
			{
				yield return methodInfo2;
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void Postfix(ItemDrop item)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)item == (Object)null) && item.m_itemData?.m_shared != null)
			{
				string prefabName = (((Object)(object)((Component)item).gameObject != (Object)null) ? ((Object)((Component)item).gameObject).name : null);
				StackManager.ApplyStackModifications(item.m_itemData.m_shared, prefabName);
			}
		}
	}
	[HarmonyPatch(typeof(ObjectDB))]
	public static class ObjectDBPatches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void Awake_Postfix(ObjectDB __instance)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)__instance == (Object)null))
			{
				StackManager.PatchObjectDB();
			}
		}

		[HarmonyPatch("CopyOtherDB")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void CopyOtherDB_Postfix(ObjectDB __instance)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)__instance == (Object)null))
			{
				StackManager.PatchObjectDB();
			}
		}

		[HarmonyPatch("UpdateRegisters")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		public static void UpdateRegisters_Postfix(ObjectDB __instance)
		{
			if (PluginConfig.ModEnabled.Value && !((Object)(object)__instance == (Object)null))
			{
				StackManager.PatchObjectDB();
			}
		}
	}
	[HarmonyPatch(typeof(SplitDialog))]
	public static class SplitDialogPatches
	{
		[HarmonyPatch("SliderChanged")]
		[HarmonyPostfix]
		public static void SliderChanged_Postfix(SplitDialog __instance, float value)
		{
			if (!PluginConfig.ModEnabled.Value || (Object)(object)__instance == (Object)null || (Object)(object)__instance.m_splitAmount == (Object)null || (Object)(object)__instance.m_splitSlider == (Object)null)
			{
				return;
			}
			int num = (int)value;
			int num2 = (int)__instance.m_splitSlider.maxValue;
			if (num2 >= 10000)
			{
				if (num < 10000)
				{
					__instance.m_splitAmount.text = $"{num} / {ItemStackFormatter.FormatCompact(num2)}";
				}
				else
				{
					__instance.m_splitAmount.text = ItemStackFormatter.FormatCompact(num) + " / " + ItemStackFormatter.FormatCompact(num2);
				}
			}
			else
			{
				__instance.m_splitAmount.text = $"{num} / {num2}";
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui))]
	public static class InventoryGuiSplitPatches
	{
		[HarmonyPatch("UpdateSplitDialog")]
		[HarmonyPostfix]
		public static void UpdateSplitDialog_Postfix(InventoryGui __instance)
		{
			if (!PluginConfig.ModEnabled.Value || !PluginConfig.EnableSplitDialogEnhancements.Value || (Object)(object)__instance == (Object)null || (Object)(object)__instance.m_splitDialog == (Object)null || !__instance.m_splitDialog.IsActive)
			{
				return;
			}
			Slider splitSlider = __instance.m_splitDialog.m_splitSlider;
			if ((Object)(object)splitSlider == (Object)null)
			{
				return;
			}
			float axis = Input.GetAxis("Mouse ScrollWheel");
			if (Mathf.Abs(axis) > 0.0001f)
			{
				int num = 1;
				if (Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303))
				{
					num *= 10;
				}
				if (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305))
				{
					num *= 100;
				}
				int num2 = PluginConfig.SplitWheelStep.Value * num;
				if (axis > 0f)
				{
					__instance.m_splitDialog.SliderValue = Mathf.Min(splitSlider.maxValue, splitSlider.value + (float)num2);
				}
				else
				{
					__instance.m_splitDialog.SliderValue = Mathf.Max(splitSlider.minValue, splitSlider.value - (float)num2);
				}
			}
			bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			bool flag2 = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
			if (flag || flag2)
			{
				int num3 = ((flag && flag2) ? 1000 : (flag2 ? 100 : 10));
				if (ZInput.GetKeyDown((KeyCode)276, true))
				{
					__instance.m_splitDialog.SliderValue = Mathf.Max(splitSlider.minValue, splitSlider.value - (float)num3);
				}
				else if (ZInput.GetKeyDown((KeyCode)275, true))
				{
					__instance.m_splitDialog.SliderValue = Mathf.Min(splitSlider.maxValue, splitSlider.value + (float)num3);
				}
			}
			if (ZInput.GetKeyDown((KeyCode)278, true))
			{
				__instance.m_splitDialog.SliderValue = splitSlider.minValue;
			}
			else if (ZInput.GetKeyDown((KeyCode)279, true))
			{
				__instance.m_splitDialog.SliderValue = splitSlider.maxValue;
			}
			else if (ZInput.GetKeyDown((KeyCode)104, true) || ZInput.GetKeyDown((KeyCode)32, true))
			{
				__instance.m_splitDialog.SliderValue = Mathf.CeilToInt(splitSlider.maxValue / 2f);
			}
		}
	}
}
namespace Valheim.ItemInfiniteStack.Core
{
	public static class DefaultRegistry
	{
		private static readonly Dictionary<string, int> _originalStackSizes = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> _originalWeights = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly object _lock = new object();

		public static string CleanPrefabName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return name;
			}
			int num = name.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase);
			if (num >= 0)
			{
				name = name.Substring(0, num);
			}
			return name.Trim();
		}

		public static void RegisterDefault(string prefabName, string sharedName, int maxStackSize, float weight)
		{
			string text = CleanPrefabName(prefabName);
			if ((string.IsNullOrEmpty(text) && string.IsNullOrEmpty(sharedName)) || maxStackSize >= 999999999 || maxStackSize >= 1000 || (PluginConfig.InfiniteStackLimit != null && maxStackSize >= PluginConfig.InfiniteStackLimit.Value && maxStackSize > 100))
			{
				return;
			}
			lock (_lock)
			{
				if (!string.IsNullOrEmpty(text))
				{
					if (!_originalStackSizes.ContainsKey(text))
					{
						_originalStackSizes[text] = maxStackSize;
					}
					if (!_originalWeights.ContainsKey(text))
					{
						_originalWeights[text] = weight;
					}
				}
				if (!string.IsNullOrEmpty(sharedName))
				{
					if (!_originalStackSizes.ContainsKey(sharedName))
					{
						_originalStackSizes[sharedName] = maxStackSize;
					}
					if (!_originalWeights.ContainsKey(sharedName))
					{
						_originalWeights[sharedName] = weight;
					}
				}
			}
		}

		public static int GetOriginalStack(string identifier, int fallback)
		{
			if (string.IsNullOrEmpty(identifier))
			{
				return fallback;
			}
			string text = CleanPrefabName(identifier);
			lock (_lock)
			{
				if (_originalStackSizes.TryGetValue(text, out var value))
				{
					return value;
				}
				if (!string.Equals(text, identifier, StringComparison.OrdinalIgnoreCase) && _originalStackSizes.TryGetValue(identifier, out var value2))
				{
					return value2;
				}
				return fallback;
			}
		}

		public static float GetOriginalWeight(string identifier, float fallback)
		{
			if (string.IsNullOrEmpty(identifier))
			{
				return fallback;
			}
			string text = CleanPrefabName(identifier);
			lock (_lock)
			{
				if (_originalWeights.TryGetValue(text, out var value))
				{
					return value;
				}
				if (!string.Equals(text, identifier, StringComparison.OrdinalIgnoreCase) && _originalWeights.TryGetValue(identifier, out var value2))
				{
					return value2;
				}
				return fallback;
			}
		}

		public static bool HasDefault(string identifier)
		{
			if (string.IsNullOrEmpty(identifier))
			{
				return false;
			}
			string key = CleanPrefabName(identifier);
			lock (_lock)
			{
				return _originalStackSizes.ContainsKey(key) || _originalStackSizes.ContainsKey(identifier);
			}
		}

		public static void Clear()
		{
			lock (_lock)
			{
				_originalStackSizes.Clear();
				_originalWeights.Clear();
			}
		}
	}
	public static class InventoryConsolidator
	{
		public static int Consolidate(Inventory inventory)
		{
			if (inventory == null || inventory.m_inventory == null || inventory.m_inventory.Count <= 1)
			{
				return 0;
			}
			StackManager.PatchInventory(inventory);
			int num = 0;
			List<ItemData> list = new List<ItemData>();
			Dictionary<string, ItemData> dictionary = new Dictionary<string, ItemData>(StringComparer.Ordinal);
			for (int i = 0; i < inventory.m_inventory.Count; i++)
			{
				ItemData val = inventory.m_inventory[i];
				if (val?.m_shared == null || val.m_equipped || val.m_shared.m_maxStackSize <= 1 || val.m_shared.m_useDurability || (val.m_customData != null && val.m_customData.Count > 0))
				{
					continue;
				}
				string key = $"{val.m_shared.m_name}:{val.m_quality}:{val.m_variant}:{val.m_crafterID}:{val.m_worldLevel}";
				if (!dictionary.TryGetValue(key, out var value))
				{
					dictionary[key] = val;
					continue;
				}
				int num2 = value.m_shared.m_maxStackSize - value.m_stack;
				if (num2 > 0)
				{
					int num3 = Math.Min(num2, val.m_stack);
					ItemData obj = value;
					obj.m_stack += num3;
					val.m_stack -= num3;
					num++;
					if (val.m_stack <= 0)
					{
						list.Add(val);
					}
				}
				else
				{
					dictionary[key] = val;
				}
			}
			if (list.Count > 0)
			{
				foreach (ItemData item in list)
				{
					inventory.m_inventory.Remove(item);
				}
				inventory.Changed(false, false);
			}
			return num;
		}

		public static void ConsolidateActiveInventories(bool showMessage = true)
		{
			int num = 0;
			if ((Object)(object)InventoryGui.instance != (Object)null && InventoryGui.instance.IsContainerOpen() && (Object)(object)InventoryGui.instance.m_currentContainer != (Object)null)
			{
				Inventory inventory = InventoryGui.instance.m_currentContainer.GetInventory();
				if (inventory != null)
				{
					num += Consolidate(inventory);
				}
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				Inventory inventory2 = ((Humanoid)Player.m_localPlayer).GetInventory();
				if (inventory2 != null)
				{
					num += Consolidate(inventory2);
				}
			}
			if (showMessage && num > 0 && (Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)1, $"Consolidated {num} duplicate item stack(s).", 0, (Sprite)null, false, true);
			}
		}
	}
	public enum ExclusionReason
	{
		None,
		Blacklisted,
		DurabilityItem,
		QualityUpgradable,
		EquipmentType,
		VanillaOneStack,
		Whitelisted
	}
	public readonly struct ClassificationDecision
	{
		public bool IsStackable { get; }

		public ExclusionReason Reason { get; }

		public string Details { get; }

		public ClassificationDecision(bool isStackable, ExclusionReason reason, string details = "")
		{
			IsStackable = isStackable;
			Reason = reason;
			Details = details;
		}
	}
	public static class ItemClassifier
	{
		private static readonly HashSet<ItemType> EquipmentItemTypes = new HashSet<ItemType>
		{
			(ItemType)3,
			(ItemType)14,
			(ItemType)22,
			(ItemType)4,
			(ItemType)5,
			(ItemType)6,
			(ItemType)7,
			(ItemType)11,
			(ItemType)17,
			(ItemType)18,
			(ItemType)19,
			(ItemType)15,
			(ItemType)10,
			(ItemType)24,
			(ItemType)20
		};

		public static ClassificationDecision Classify(SharedData shared, string prefabName)
		{
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Invalid comparison between Unknown and I4
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Invalid comparison between Unknown and I4
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Invalid comparison between Unknown and I4
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Invalid comparison between Unknown and I4
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Invalid comparison between Unknown and I4
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Invalid comparison between Unknown and I4
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Invalid comparison between Unknown and I4
			if (shared == null)
			{
				return new ClassificationDecision(isStackable: false, ExclusionReason.None, "Null shared data");
			}
			string text = ((!string.IsNullOrEmpty(prefabName)) ? prefabName : shared.m_name);
			if (PluginConfig.IsBlacklisted(prefabName, shared.m_name))
			{
				return new ClassificationDecision(isStackable: false, ExclusionReason.Blacklisted, "User blacklisted: '" + text + "'");
			}
			if (PluginConfig.IsWhitelisted(prefabName, shared.m_name))
			{
				return new ClassificationDecision(isStackable: true, ExclusionReason.Whitelisted, "User whitelisted: '" + text + "'");
			}
			if (PluginConfig.ProtectDurabilityItems.Value && shared.m_useDurability)
			{
				return new ClassificationDecision(isStackable: false, ExclusionReason.DurabilityItem, $"Durability item (max: {shared.m_maxDurability})");
			}
			if (PluginConfig.ProtectQualityItems.Value && shared.m_maxQuality > 1 && (int)shared.m_itemType != 21)
			{
				return new ClassificationDecision(isStackable: false, ExclusionReason.QualityUpgradable, $"Quality upgradable (max: {shared.m_maxQuality})");
			}
			if (PluginConfig.ProtectEquipment.Value && EquipmentItemTypes.Contains(shared.m_itemType))
			{
				return new ClassificationDecision(isStackable: false, ExclusionReason.EquipmentType, $"Equipment type: {shared.m_itemType}");
			}
			if ((int)shared.m_itemType != 1 && (int)shared.m_itemType != 2 && (int)shared.m_itemType != 9 && (int)shared.m_itemType != 23 && (int)shared.m_itemType != 13 && (int)shared.m_itemType != 21)
			{
				int originalStack = DefaultRegistry.GetOriginalStack(text, shared.m_maxStackSize);
				if (PluginConfig.PreserveVanillaOneStack.Value && originalStack <= 1)
				{
					return new ClassificationDecision(isStackable: false, ExclusionReason.VanillaOneStack, $"Vanilla default stack is {originalStack}");
				}
			}
			return new ClassificationDecision(isStackable: true, ExclusionReason.None, "Stackable material/resource");
		}

		public static bool IsEquipment(ItemType itemType)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return EquipmentItemTypes.Contains(itemType);
		}
	}
	public static class StackManager
	{
		public const int SafeMaxStackLimit = 999999999;

		public static bool ApplyStackModifications(SharedData shared, string prefabName)
		{
			if (shared == null)
			{
				return false;
			}
			string text = DefaultRegistry.CleanPrefabName(prefabName);
			string text2 = ((!string.IsNullOrEmpty(text)) ? text : shared.m_name);
			DefaultRegistry.RegisterDefault(text, shared.m_name, shared.m_maxStackSize, shared.m_weight);
			int originalStack = DefaultRegistry.GetOriginalStack(text2, shared.m_maxStackSize);
			float originalWeight = DefaultRegistry.GetOriginalWeight(text2, shared.m_weight);
			if (!PluginConfig.ModEnabled.Value)
			{
				bool num = shared.m_maxStackSize != originalStack;
				bool flag = Math.Abs(shared.m_weight - originalWeight) > 0.0001f;
				shared.m_maxStackSize = originalStack;
				shared.m_weight = originalWeight;
				return num || flag;
			}
			ClassificationDecision classificationDecision = ItemClassifier.Classify(shared, text);
			if (!classificationDecision.IsStackable)
			{
				bool flag2 = false;
				if (shared.m_maxStackSize != originalStack)
				{
					shared.m_maxStackSize = originalStack;
					flag2 = true;
				}
				if (Math.Abs(shared.m_weight - originalWeight) > 0.0001f)
				{
					shared.m_weight = originalWeight;
					flag2 = true;
				}
				if (PluginConfig.LoggingEnabled.Value && flag2)
				{
					Plugin.Log.LogDebug((object)$"[Preserved 1-Per-Slot] {text2} -> Stack: {shared.m_maxStackSize} (Reason: {classificationDecision.Reason})");
				}
				return flag2;
			}
			int num2 = ((!(PluginConfig.GlobalStackMultiplier.Value > 0f)) ? PluginConfig.InfiniteStackLimit.Value : Mathf.CeilToInt((float)originalStack * PluginConfig.GlobalStackMultiplier.Value));
			num2 = Mathf.Clamp(num2, 1, 999999999);
			float num3 = originalWeight;
			if (PluginConfig.EnableWeightScaling.Value)
			{
				float num4 = Mathf.Max(0f, PluginConfig.StackableWeightMultiplier.Value);
				num3 = originalWeight * num4;
			}
			bool flag3 = shared.m_maxStackSize != num2 || Math.Abs(shared.m_weight - num3) > 0.0001f;
			shared.m_maxStackSize = num2;
			shared.m_weight = num3;
			if (PluginConfig.LoggingEnabled.Value && flag3)
			{
				Plugin.Log.LogDebug((object)$"[Infinite Stack Applied] {text2} -> Stack: {num2}, Weight: {num3:F2}");
			}
			return flag3;
		}

		public static int PatchObjectDB()
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_items == null)
			{
				return 0;
			}
			int num = 0;
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				if (!((Object)(object)item == (Object)null))
				{
					ItemDrop component = item.GetComponent<ItemDrop>();
					if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null && ApplyStackModifications(component.m_itemData.m_shared, ((Object)item).name))
					{
						num++;
					}
				}
			}
			if (PluginConfig.LoggingEnabled.Value)
			{
				Plugin.Log.LogInfo((object)$"Patched {num} items in ObjectDB.");
			}
			return num;
		}

		public static bool PatchInventory(Inventory inventory)
		{
			if (inventory == null || inventory.m_inventory == null)
			{
				return false;
			}
			bool flag = false;
			foreach (ItemData item in inventory.m_inventory)
			{
				if (item?.m_shared != null && ApplyStackModifications(item.m_shared, ((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null))
				{
					flag = true;
				}
			}
			if (flag)
			{
				inventory.Changed(false, false);
			}
			return flag;
		}

		public static bool PatchPlayerInventory(Player player)
		{
			if ((Object)(object)player != (Object)null)
			{
				return PatchInventory(((Humanoid)player).GetInventory());
			}
			return false;
		}
	}
}
namespace Valheim.ItemInfiniteStack.Configuration
{
	public enum StackDisplayMode
	{
		AmountOnly,
		Compact,
		StandardRatio
	}
	public static class PluginConfig
	{
		private static HashSet<string> _parsedBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static HashSet<string> _parsedWhitelist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static ConfigEntry<bool> ModEnabled { get; private set; }

		public static ConfigEntry<bool> LoggingEnabled { get; private set; }

		public static ConfigEntry<int> InfiniteStackLimit { get; private set; }

		public static ConfigEntry<float> GlobalStackMultiplier { get; private set; }

		public static ConfigEntry<bool> ProtectDurabilityItems { get; private set; }

		public static ConfigEntry<bool> ProtectQualityItems { get; private set; }

		public static ConfigEntry<bool> ProtectEquipment { get; private set; }

		public static ConfigEntry<bool> PreserveVanillaOneStack { get; private set; }

		public static ConfigEntry<string> CustomBlacklist { get; private set; }

		public static ConfigEntry<string> CustomWhitelist { get; private set; }

		public static ConfigEntry<bool> EnableWeightScaling { get; private set; }

		public static ConfigEntry<float> StackableWeightMultiplier { get; private set; }

		public static ConfigEntry<StackDisplayMode> DisplayFormat { get; private set; }

		public static ConfigEntry<int> CompactThreshold { get; private set; }

		public static ConfigEntry<bool> HideSingleItemCount { get; private set; }

		public static ConfigEntry<bool> EnableSplitDialogEnhancements { get; private set; }

		public static ConfigEntry<int> SplitWheelStep { get; private set; }

		public static ConfigEntry<bool> EnableAutoConsolidateOnOpen { get; private set; }

		public static ConfigEntry<KeyboardShortcut> ConsolidateHotkey { get; private set; }

		public static event Action OnConfigChanged;

		public static void Initialize(ConfigFile config)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Expected O, but got Unknown
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Expected O, but got Unknown
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Expected O, but got Unknown
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			ModEnabled = config.Bind<bool>("1 - General", "Enabled", true, "Master switch to enable or disable the ItemInfiniteStack mod.");
			LoggingEnabled = config.Bind<bool>("1 - General", "LoggingEnabled", false, "Enable verbose debug logging in the BepInEx console.");
			InfiniteStackLimit = config.Bind<int>("2 - Stack Settings", "InfiniteStackLimit", 999999999, new ConfigDescription("Target maximum stack size for stackable items. Clamped to a safe max of 999,999,999 to prevent 32-bit integer overflow.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 999999999), Array.Empty<object>()));
			GlobalStackMultiplier = config.Bind<float>("2 - Stack Settings", "GlobalStackMultiplier", 0f, new ConfigDescription("Optional stack multiplier. If set above 0, an item's stack size will be originalStack * multiplier instead of InfiniteStackLimit. Set to 0 to use InfiniteStackLimit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>()));
			ProtectDurabilityItems = config.Bind<bool>("3 - Exceptions (1-Per-Slot)", "ProtectDurabilityItems", true, "Strictly preserve 1 item per slot for any item with durability (weapons, armor, tools). Prevents durability corruption.");
			ProtectQualityItems = config.Bind<bool>("3 - Exceptions (1-Per-Slot)", "ProtectQualityItems", true, "Strictly preserve 1 item per slot for upgradable items (maxQuality > 1). Prevents upgrade tier loss.");
			ProtectEquipment = config.Bind<bool>("3 - Exceptions (1-Per-Slot)", "ProtectEquipment", true, "Strictly preserve 1 item per slot for equipment types: Weapons, Shields, Armor, Tools, Torches, Trinkets, Utilities.");
			PreserveVanillaOneStack = config.Bind<bool>("3 - Exceptions (1-Per-Slot)", "PreserveVanillaOneStack", true, "Preserve 1 item per slot for any item whose original vanilla stack size is 1 (unless explicitly included in CustomWhitelist).");
			CustomBlacklist = config.Bind<string>("3 - Exceptions (1-Per-Slot)", "CustomBlacklist", "", "Comma-separated list of item prefab names or localization tokens that MUST remain 1 item per slot (e.g. 'Wishbone, Tankard, Cart').");
			CustomWhitelist = config.Bind<string>("3 - Exceptions (1-Per-Slot)", "CustomWhitelist", "", "Comma-separated list of item prefab names that are forced to have infinite stack even if they would otherwise be excluded.");
			EnableWeightScaling = config.Bind<bool>("4 - Weight Settings", "EnableWeightScaling", false, "If enabled, stackable items will have their weight multiplied by StackableWeightMultiplier.");
			StackableWeightMultiplier = config.Bind<float>("4 - Weight Settings", "StackableWeightMultiplier", 1f, new ConfigDescription("Weight multiplier applied to stackable items. 1.0 = vanilla weight, 0.5 = 50% weight, 0.0 = completely weightless.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			DisplayFormat = config.Bind<StackDisplayMode>("5 - UI & Display", "DisplayFormat", StackDisplayMode.AmountOnly, "How stack count is formatted on inventory slot icons. AmountOnly shows just current count (e.g. 1500). Compact abbreviates large numbers (1.5k, 25M). StandardRatio displays amount/max.");
			CompactThreshold = config.Bind<int>("5 - UI & Display", "CompactThreshold", 1000, new ConfigDescription("Threshold above which Compact format activates (e.g. 1000 turns into 1k).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 1000000), Array.Empty<object>()));
			HideSingleItemCount = config.Bind<bool>("5 - UI & Display", "HideSingleItemCount", true, "Hide stack count text when an item slot contains only 1 item (matches clean vanilla behavior).");
			EnableSplitDialogEnhancements = config.Bind<bool>("6 - Quality of Life", "EnableSplitDialogEnhancements", true, "Enable mouse wheel scrolling and arrow key fine-tuning on the stack split dialog.");
			SplitWheelStep = config.Bind<int>("6 - Quality of Life", "SplitWheelStep", 1, new ConfigDescription("Base number of items adjusted per mouse wheel tick on split dialog. Shift scrolls 10x faster, Ctrl scrolls 100x faster.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			EnableAutoConsolidateOnOpen = config.Bind<bool>("6 - Quality of Life", "EnableAutoConsolidateOnOpen", true, "Automatically consolidate duplicate item stacks when opening containers or player inventory.");
			ConsolidateHotkey = config.Bind<KeyboardShortcut>("6 - Quality of Life", "ConsolidateHotkey", new KeyboardShortcut((KeyCode)127, Array.Empty<KeyCode>()), "Hotkey to immediately consolidate and merge duplicate stacks in the currently opened container or inventory.");
			RebuildLists();
			config.SettingChanged += delegate
			{
				RebuildLists();
				PluginConfig.OnConfigChanged?.Invoke();
			};
		}

		private static void RebuildLists()
		{
			_parsedBlacklist = ParseCommaSeparated(CustomBlacklist?.Value);
			_parsedWhitelist = ParseCommaSeparated(CustomWhitelist?.Value);
		}

		private static HashSet<string> ParseCommaSeparated(string input)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrWhiteSpace(input))
			{
				return hashSet;
			}
			string[] array = input.Split(new char[3] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (!string.IsNullOrEmpty(text))
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}

		public static bool IsBlacklisted(string prefabName, string sharedName)
		{
			if (_parsedBlacklist.Count == 0)
			{
				return false;
			}
			if (!string.IsNullOrEmpty(prefabName) && _parsedBlacklist.Contains(prefabName))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(sharedName) && _parsedBlacklist.Contains(sharedName))
			{
				return true;
			}
			return false;
		}

		public static bool IsWhitelisted(string prefabName, string sharedName)
		{
			if (_parsedWhitelist.Count == 0)
			{
				return false;
			}
			if (!string.IsNullOrEmpty(prefabName) && _parsedWhitelist.Contains(prefabName))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(sharedName) && _parsedWhitelist.Contains(sharedName))
			{
				return true;
			}
			return false;
		}
	}
}