Decompiled source of CatosInventorySorter v0.2.0

CatosInventorySorter.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.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("Catosaur")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Sorts the local player's Valheim inventory using native inventory change callbacks.")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+494c17d12b074258215f2d254cb2e06f563f61b8")]
[assembly: AssemblyProduct("CatosInventorySorter")]
[assembly: AssemblyTitle("CatosInventorySorter")]
[assembly: AssemblyVersion("0.2.0.0")]
[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 CatosInventorySorter
{
	internal static class ConfigHotReload
	{
		private static ConfigFile _config;

		private static DateTime _loadedWriteTime;

		private static DateTime _candidateWriteTime;

		private static float _candidateSince;

		private static float _nextPoll;

		private static float _nextWarning;

		private static bool _pendingChange;

		internal static void Initialize(ConfigFile config)
		{
			_config = config;
			_loadedWriteTime = ReadWriteTime();
			_candidateWriteTime = _loadedWriteTime;
		}

		internal static void Update()
		{
			if (_config == null || Time.unscaledTime < _nextPoll)
			{
				return;
			}
			_nextPoll = Time.unscaledTime + 0.4f;
			try
			{
				DateTime dateTime = ReadWriteTime();
				if (dateTime == _loadedWriteTime)
				{
					_candidateWriteTime = dateTime;
					_candidateSince = 0f;
					_pendingChange = false;
				}
				else if (dateTime != _candidateWriteTime)
				{
					_candidateWriteTime = dateTime;
					_candidateSince = Time.unscaledTime;
					_pendingChange = true;
				}
				else if (_pendingChange && !(Time.unscaledTime - _candidateSince < 0.8f))
				{
					_config.Reload();
					_loadedWriteTime = ReadWriteTime();
					_candidateWriteTime = _loadedWriteTime;
					_candidateSince = 0f;
					_pendingChange = false;
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)"Configuration file changed; settings reloaded.");
					}
				}
			}
			catch (Exception ex)
			{
				if (Time.unscaledTime >= _nextWarning)
				{
					_nextWarning = Time.unscaledTime + 5f;
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("Could not hot-reload configuration: " + ex.Message));
					}
				}
			}
		}

		private static DateTime ReadWriteTime()
		{
			string configFilePath = _config.ConfigFilePath;
			if (!File.Exists(configFilePath))
			{
				return DateTime.MinValue;
			}
			return File.GetLastWriteTimeUtc(configFilePath);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Awake")]
	internal static class InventoryGuiPatches
	{
		private static void Postfix(InventoryGui __instance)
		{
			try
			{
				InventorySortButton.Attach(__instance);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not create inventory sort button: " + ex.Message));
				}
			}
		}
	}
	internal static class InventorySortButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__SortPlayerInventory;
		}

		private static Button _button;

		private static TMP_Text _label;

		private static ConfigEntry<KeyboardShortcut> _sortShortcut;

		private static readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4];

		internal static void Attach(InventoryGui gui)
		{
			//IL_00a6: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: 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_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)gui) || Object.op_Implicit((Object)(object)_button))
			{
				return;
			}
			object? obj = AccessTools.Field(typeof(InventoryGui), "m_player")?.GetValue(gui);
			Transform val = (Transform)((obj is Transform) ? obj : null);
			object? obj2 = AccessTools.Field(typeof(InventoryGui), "m_inventoryRoot")?.GetValue(gui);
			Transform val2 = (Transform)((obj2 is Transform) ? obj2 : null);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2))
			{
				GameObject val3 = new GameObject("CatosInventorySorterButton", new Type[3]
				{
					typeof(RectTransform),
					typeof(Image),
					typeof(Button)
				});
				val3.transform.SetParent(val2, false);
				_button = val3.GetComponent<Button>();
				Image component = val3.GetComponent<Image>();
				((Graphic)component).color = new Color(0.16f, 0.23f, 0.17f, 0.98f);
				((Selectable)_button).targetGraphic = (Graphic)(object)component;
				((Selectable)_button).transition = (Transition)1;
				ColorBlock colors = ((Selectable)_button).colors;
				((ColorBlock)(ref colors)).normalColor = new Color(0.82f, 0.92f, 0.82f, 1f);
				((ColorBlock)(ref colors)).highlightedColor = Color.white;
				((ColorBlock)(ref colors)).pressedColor = new Color(0.6f, 0.8f, 0.6f, 1f);
				((ColorBlock)(ref colors)).selectedColor = Color.white;
				((Selectable)_button).colors = colors;
				Button button = _button;
				Navigation navigation = default(Navigation);
				((Navigation)(ref navigation)).mode = (Mode)0;
				((Selectable)button).navigation = navigation;
				ButtonClickedEvent onClick = _button.onClick;
				object obj3 = <>O.<0>__SortPlayerInventory;
				if (obj3 == null)
				{
					UnityAction val4 = SortPlayerInventory;
					<>O.<0>__SortPlayerInventory = val4;
					obj3 = (object)val4;
				}
				((UnityEvent)onClick).AddListener((UnityAction)obj3);
				Transform transform = ((Component)_button).transform;
				RectTransform val5 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (Object.op_Implicit((Object)(object)val5))
				{
					val5.anchorMin = new Vector2(0.5f, 0.5f);
					val5.anchorMax = new Vector2(0.5f, 0.5f);
					val5.pivot = new Vector2(0f, 1f);
					val5.sizeDelta = new Vector2(180f, 34f);
					((Transform)val5).localScale = Vector3.one;
				}
				((Component)_button).transform.SetAsLastSibling();
				GameObject val6 = new GameObject("Label", new Type[2]
				{
					typeof(RectTransform),
					typeof(TextMeshProUGUI)
				});
				val6.transform.SetParent(((Component)_button).transform, false);
				Transform transform2 = val6.transform;
				Transform obj4 = ((transform2 is RectTransform) ? transform2 : null);
				((RectTransform)obj4).anchorMin = Vector2.zero;
				((RectTransform)obj4).anchorMax = Vector2.one;
				((RectTransform)obj4).offsetMin = Vector2.zero;
				((RectTransform)obj4).offsetMax = Vector2.zero;
				_label = (TMP_Text)(object)val6.GetComponent<TextMeshProUGUI>();
				object? obj5 = AccessTools.Field(typeof(InventoryGui), "m_containerName")?.GetValue(gui);
				TMP_Text val7 = (TMP_Text)((obj5 is TMP_Text) ? obj5 : null);
				if (Object.op_Implicit((Object)(object)val7))
				{
					_label.font = val7.font;
					_label.fontSharedMaterial = val7.fontSharedMaterial;
					_label.fontSize = val7.fontSize;
				}
				else
				{
					_label.font = TMP_Settings.defaultFontAsset;
					_label.fontSize = 16f;
				}
				_label.alignment = (TextAlignmentOptions)514;
				((Graphic)_label).color = Color.white;
				_label.textWrappingMode = (TextWrappingModes)0;
				_label.overflowMode = (TextOverflowModes)1;
				_label.enableAutoSizing = true;
				_label.fontSizeMin = 11f;
				_label.fontSizeMax = 18f;
				_label.text = FormatLabel(InventorySorter.GetCriterion());
				PositionBesideInventory(gui, val, (RectTransform)(object)((val2 is RectTransform) ? val2 : null), val5);
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Inventory sort button attached beside the player inventory grid.");
				}
			}
		}

		internal static void Configure(ConfigFile config)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			_sortShortcut = config.Bind<KeyboardShortcut>("Controls", "SortShortcut", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Sort the local player's inventory while the inventory screen is open.");
		}

		internal static void Update()
		{
			//IL_00b0: 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)
			InventoryGui instance = InventoryGui.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)_button))
			{
				Attach(instance);
			}
			if (Object.op_Implicit((Object)(object)_button))
			{
				object? obj = AccessTools.Field(typeof(InventoryGui), "m_player")?.GetValue(instance);
				Transform playerPanel = (Transform)((obj is Transform) ? obj : null);
				object? obj2 = AccessTools.Field(typeof(InventoryGui), "m_inventoryRoot")?.GetValue(instance);
				Transform val = (Transform)((obj2 is Transform) ? obj2 : null);
				Transform overlayRoot = ((val is RectTransform) ? val : null);
				Transform transform = ((Component)_button).transform;
				PositionBesideInventory(instance, playerPanel, (RectTransform)(object)overlayRoot, (RectTransform)(object)((transform is RectTransform) ? transform : null));
				RefreshLabel();
			}
			if (InventoryGui.IsVisible() && _sortShortcut != null)
			{
				KeyboardShortcut value = _sortShortcut.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					SortPlayerInventory();
				}
			}
		}

		internal static void Destroy()
		{
			if (Object.op_Implicit((Object)(object)_button))
			{
				Object.Destroy((Object)(object)((Component)_button).gameObject);
			}
			_button = null;
			_label = null;
		}

		private static void SortPlayerInventory()
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if (Object.op_Implicit((Object)(object)localPlayer))
				{
					SortCriterion criterion = InventorySorter.GetCriterion();
					bool flag = ((criterion == SortCriterion.Weight) ? ModConfig.WeightDescending.Value : ModConfig.QuantityDescending.Value);
					bool flag2 = InventorySorter.Sort(((Humanoid)localPlayer).GetInventory(), localPlayer, criterion, flag);
					if (Object.op_Implicit((Object)(object)_label))
					{
						_label.text = FormatLabel(criterion);
					}
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogDebug((object)(flag2 ? $"Sorted player inventory by {criterion} (descending={flag})." : "Player inventory already matches the selected sort order."));
					}
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogError((object)$"Inventory sort failed safely: {arg}");
				}
			}
		}

		private static void RefreshLabel()
		{
			if (Object.op_Implicit((Object)(object)_label))
			{
				string text = FormatLabel(InventorySorter.GetCriterion());
				if (_label.text != text)
				{
					_label.text = text;
				}
			}
		}

		private static string FormatLabel(SortCriterion criterion)
		{
			return "Sort: " + criterion;
		}

		private static void PositionBesideInventory(InventoryGui gui, Transform playerPanel, RectTransform overlayRoot, RectTransform buttonRect)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)gui) || !Object.op_Implicit((Object)(object)playerPanel) || !Object.op_Implicit((Object)(object)overlayRoot) || !Object.op_Implicit((Object)(object)buttonRect))
			{
				return;
			}
			object? obj = AccessTools.Field(typeof(InventoryGui), "m_playerGrid")?.GetValue(gui);
			InventoryGrid val = (InventoryGrid)((obj is InventoryGrid) ? obj : null);
			RectTransform val2 = (RectTransform)((val != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				val2 = (RectTransform)(object)((playerPanel is RectTransform) ? playerPanel : null);
			}
			if (Object.op_Implicit((Object)(object)val2))
			{
				val2.GetWorldCorners(_corners);
				Vector3 val3 = ((Transform)overlayRoot).InverseTransformPoint(_corners[2]);
				Rect rect = overlayRoot.rect;
				Rect rect2 = buttonRect.rect;
				float num;
				if (!(((Rect)(ref rect2)).width > 0f))
				{
					num = 180f;
				}
				else
				{
					rect2 = buttonRect.rect;
					num = ((Rect)(ref rect2)).width;
				}
				float num2 = num;
				rect2 = buttonRect.rect;
				float num3;
				if (!(((Rect)(ref rect2)).height > 0f))
				{
					num3 = 34f;
				}
				else
				{
					rect2 = buttonRect.rect;
					num3 = ((Rect)(ref rect2)).height;
				}
				float num4 = num3;
				float num5 = Mathf.Clamp(val3.x + 24f, ((Rect)(ref rect)).xMin + 8f, ((Rect)(ref rect)).xMax - num2 - 8f);
				float num6 = Mathf.Clamp(val3.y + 24f, ((Rect)(ref rect)).yMin + num4 + 8f, ((Rect)(ref rect)).yMax - 8f);
				((Transform)buttonRect).localPosition = new Vector3(num5, num6, 0f);
			}
		}
	}
	internal enum SortCriterion
	{
		Weight,
		Quantity
	}
	internal enum SpecialItemsMode
	{
		Ignore,
		SortFirst
	}
	internal enum SpecialItemCategory
	{
		Other,
		Armor,
		Food,
		Arrows
	}
	internal static class InventorySorter
	{
		private sealed class Slot
		{
			internal int X;

			internal int Y;

			internal Slot(int x, int y)
			{
				X = x;
				Y = y;
			}
		}

		private static readonly MethodInfo ChangedMethod = AccessTools.Method(typeof(Inventory), "Changed", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null);

		internal static bool Sort(Inventory inventory, Player player, SortCriterion criterion, bool descending)
		{
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			if (inventory == null || (Object)(object)player == (Object)null || ChangedMethod == null)
			{
				return false;
			}
			int width = inventory.GetWidth();
			int height = inventory.GetHeight();
			if (width <= 0 || height <= 1)
			{
				return false;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null || allItems.Count < 2)
			{
				return false;
			}
			HashSet<ItemData> hashSet = new HashSet<ItemData>();
			List<ItemData> list = new List<ItemData>();
			inventory.GetBoundItems(list);
			foreach (ItemData item2 in list)
			{
				if (item2 != null)
				{
					hashSet.Add(item2);
				}
			}
			List<ItemData> list2 = new List<ItemData>();
			HashSet<long> hashSet2 = new HashSet<long>();
			HashSet<long> hashSet3 = new HashSet<long>();
			SpecialItemsMode specialItemsMode = GetSpecialItemsMode();
			foreach (ItemData item3 in allItems)
			{
				if (item3 == null || item3.m_shared == null)
				{
					return false;
				}
				int x = item3.m_gridPos.x;
				int y = item3.m_gridPos.y;
				if (x < 0 || y < 0 || x >= width || y >= height)
				{
					return false;
				}
				long item = PositionKey(x, y);
				if (!hashSet3.Add(item))
				{
					return false;
				}
				if (y == 0 || hashSet.Contains(item3) || ((Humanoid)player).IsItemEquiped(item3) || (specialItemsMode == SpecialItemsMode.Ignore && IsSpecialItem(item3)))
				{
					hashSet2.Add(item);
				}
				else
				{
					list2.Add(item3);
				}
			}
			list2.Sort((ItemData a, ItemData b) => Compare(a, b, criterion, descending, specialItemsMode));
			List<Slot> list3 = new List<Slot>();
			for (int num = 1; num < height; num++)
			{
				for (int num2 = 0; num2 < width; num2++)
				{
					if (!hashSet2.Contains(PositionKey(num2, num)))
					{
						list3.Add(new Slot(num2, num));
					}
				}
			}
			if (list3.Count < list2.Count)
			{
				return false;
			}
			bool flag = false;
			for (int num3 = 0; num3 < list2.Count; num3++)
			{
				ItemData val = list2[num3];
				Slot slot = list3[num3];
				if (val.m_gridPos.x != slot.X || val.m_gridPos.y != slot.Y)
				{
					val.m_gridPos = new Vector2i(slot.X, slot.Y);
					flag = true;
				}
			}
			if (flag)
			{
				try
				{
					ChangedMethod.Invoke(inventory, new object[2] { true, false });
				}
				catch (Exception arg)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogError((object)$"Valheim inventory change notification failed: {arg}");
					}
					return false;
				}
			}
			return flag;
		}

		private static int Compare(ItemData left, ItemData right, SortCriterion criterion, bool descending, SpecialItemsMode specialItemsMode)
		{
			if (specialItemsMode == SpecialItemsMode.SortFirst)
			{
				int num = GetSpecialItemRank(left).CompareTo(GetSpecialItemRank(right));
				if (num != 0)
				{
					return num;
				}
			}
			int num2 = ((criterion != SortCriterion.Weight) ? left.m_stack.CompareTo(right.m_stack) : TotalWeight(left).CompareTo(TotalWeight(right)));
			if (descending)
			{
				num2 = -num2;
			}
			if (num2 != 0)
			{
				return num2;
			}
			if (left.m_gridPos.y == right.m_gridPos.y)
			{
				return left.m_gridPos.x.CompareTo(right.m_gridPos.x);
			}
			return left.m_gridPos.y.CompareTo(right.m_gridPos.y);
		}

		private static float TotalWeight(ItemData item)
		{
			return item.m_shared.m_weight * (float)item.m_stack;
		}

		private static long PositionKey(int x, int y)
		{
			return ((long)y << 32) | (uint)x;
		}

		private static bool IsSpecialItem(ItemData item)
		{
			return GetSpecialItemCategory(item) != SpecialItemCategory.Other;
		}

		private static SpecialItemCategory GetSpecialItemCategory(ItemData item)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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)
			//IL_003d: Expected I4, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return SpecialItemCategory.Other;
			}
			ItemType itemType = item.m_shared.m_itemType;
			switch (itemType - 6)
			{
			default:
				if ((int)itemType != 17)
				{
					break;
				}
				goto case 0;
			case 3:
				return SpecialItemCategory.Arrows;
			case 0:
			case 1:
			case 5:
			case 6:
				return SpecialItemCategory.Armor;
			case 2:
			case 4:
				break;
			}
			if (!(item.m_shared.m_food > 0f))
			{
				return SpecialItemCategory.Other;
			}
			return SpecialItemCategory.Food;
		}

		private static int GetSpecialItemRank(ItemData item)
		{
			SpecialItemCategory specialItemCategory = GetSpecialItemCategory(item);
			string[] array = ((ModConfig.SpecialItemsOrder != null) ? ModConfig.SpecialItemsOrder.Value : "Armor,Food,Arrows").Split(new char[1] { ',' });
			int num = 0;
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				if (Enum.TryParse<SpecialItemCategory>(array2[i].Trim(), ignoreCase: true, out var result) && result != SpecialItemCategory.Other)
				{
					if (result == specialItemCategory)
					{
						return num;
					}
					num++;
				}
			}
			if (specialItemCategory != SpecialItemCategory.Other)
			{
				return num;
			}
			return int.MaxValue;
		}

		private static SpecialItemsMode GetSpecialItemsMode()
		{
			if (ModConfig.SpecialItemsMode == null || !Enum.TryParse<SpecialItemsMode>(ModConfig.SpecialItemsMode.Value, ignoreCase: true, out var result))
			{
				return SpecialItemsMode.Ignore;
			}
			return result;
		}

		internal static SortCriterion GetCriterion()
		{
			if (!Enum.TryParse<SortCriterion>(ModConfig.SortMode.Value, ignoreCase: true, out var result))
			{
				return SortCriterion.Weight;
			}
			return result;
		}
	}
	internal static class ModConfig
	{
		internal static ConfigEntry<string> SortMode;

		internal static ConfigEntry<bool> WeightDescending;

		internal static ConfigEntry<bool> QuantityDescending;

		internal static ConfigEntry<string> SpecialItemsMode;

		internal static ConfigEntry<string> SpecialItemsOrder;

		internal static void Bind(ConfigFile config)
		{
			SortMode = config.Bind<string>("Sorting", "SortMode", "Weight", "Sort criterion: Weight or Quantity. Changes are picked up while the game is running.");
			WeightDescending = config.Bind<bool>("Sorting", "WeightDescending", true, "Sort heaviest total stacks first by default. Set false for lightest first. Changes are picked up while the game is running.");
			QuantityDescending = config.Bind<bool>("Sorting", "QuantityDescending", true, "Sort highest stack quantities first by default. Set false for lowest first. Changes are picked up while the game is running.");
			SpecialItemsMode = config.Bind<string>("Sorting", "SpecialItemsMode", "SortFirst", "How to handle arrows, armor, and food: SortFirst sorts them before other items; Ignore keeps them in place. Changes are picked up while the game is running.");
			SpecialItemsOrder = config.Bind<string>("Sorting", "SpecialItemsOrder", "Armor,Food,Arrows", "Order for special items when SpecialItemsMode is SortFirst. Use a comma-separated list containing Armor, Food, and Arrows. Changes are picked up while the game is running.");
			InventorySortButton.Configure(config);
		}
	}
	[BepInPlugin("com.catosaur.catosinventorysorter", "Catos Inventory Sorter", "0.1.0")]
	[BepInProcess("valheim.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.catosaur.catosinventorysorter";

		public const string Name = "Catos Inventory Sorter";

		public const string Version = "0.1.0";

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			ConfigHotReload.Initialize(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("com.catosaur.catosinventorysorter");
			_harmony.PatchAll(typeof(InventoryGuiPatches));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Catos Inventory Sorter 0.1.0 client-only loaded.");
		}

		private void OnDestroy()
		{
			InventorySortButton.Destroy();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchAll("com.catosaur.catosinventorysorter");
			}
		}

		private void Update()
		{
			ConfigHotReload.Update();
			InventorySortButton.Update();
		}
	}
}