Decompiled source of SortContainers v1.0.0

SortContainers.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
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: AssemblyVersion("0.0.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 SortContainers
{
	internal static class InventorySorter
	{
		private static readonly FieldRef<Inventory, List<ItemData>> ItemsRef = AccessTools.FieldRefAccess<Inventory, List<ItemData>>("m_inventory");

		private static readonly Action<Inventory, bool, bool> Changed = AccessTools.MethodDelegate<Action<Inventory, bool, bool>>(AccessTools.Method(typeof(Inventory), "Changed", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null), (object)null, true);

		public static void Sort(Inventory inventory, bool mergeStacks)
		{
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (inventory == null)
			{
				return;
			}
			List<ItemData> list = ItemsRef.Invoke(inventory);
			if (list != null && list.Count != 0)
			{
				if (mergeStacks)
				{
					MergeStacks(list);
				}
				List<ItemData> list2 = list.OrderBy<ItemData, string>(DisplayName, StringComparer.CurrentCultureIgnoreCase).ThenBy<ItemData, string>((ItemData i) => i.m_shared.m_name, StringComparer.Ordinal).ThenByDescending((ItemData i) => i.m_quality)
					.ThenBy((ItemData i) => i.m_variant)
					.ThenByDescending((ItemData i) => i.m_stack)
					.ToList();
				int num = Math.Max(1, inventory.GetWidth());
				int num2 = Math.Max(1, inventory.GetHeight());
				int num3 = num * num2;
				for (int num4 = 0; num4 < list2.Count && num4 < num3; num4++)
				{
					list2[num4].m_gridPos = new Vector2i(num4 % num, num4 / num);
				}
				NotifyChanged(inventory);
			}
		}

		private static void MergeStacks(List<ItemData> items)
		{
			IEnumerable<IGrouping<string, ItemData>> enumerable = items.Where((ItemData i) => i.m_shared.m_maxStackSize > 1 && i.m_customData.Count == 0).GroupBy(StackKey);
			List<ItemData> list = new List<ItemData>();
			foreach (IGrouping<string, ItemData> item in enumerable)
			{
				List<ItemData> list2 = item.OrderByDescending((ItemData i) => i.m_stack).ToList();
				int maxStackSize = list2[0].m_shared.m_maxStackSize;
				int num = 0;
				for (int num2 = list2.Count - 1; num2 > num; num2--)
				{
					ItemData val = list2[num2];
					while (val.m_stack > 0 && num < num2)
					{
						ItemData val2 = list2[num];
						int num3 = maxStackSize - val2.m_stack;
						if (num3 <= 0)
						{
							num++;
							continue;
						}
						int num4 = Math.Min(num3, val.m_stack);
						val2.m_stack += num4;
						val.m_stack -= num4;
					}
					if (val.m_stack <= 0)
					{
						list.Add(val);
					}
				}
			}
			foreach (ItemData item2 in list)
			{
				items.Remove(item2);
			}
		}

		private static string StackKey(ItemData item)
		{
			return item.m_shared.m_name + "|" + item.m_quality + "|" + item.m_variant + "|" + item.m_worldLevel;
		}

		private static string DisplayName(ItemData item)
		{
			string name = item.m_shared.m_name;
			Localization instance = Localization.instance;
			if (instance == null || string.IsNullOrEmpty(name))
			{
				return name ?? string.Empty;
			}
			string text = instance.Localize(name);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return name;
		}

		private static void NotifyChanged(Inventory inventory)
		{
			if (Changed != null)
			{
				Changed(inventory, arg2: false, arg3: false);
			}
			else
			{
				inventory.m_onChanged?.Invoke();
			}
		}
	}
	[BepInPlugin("dsoltyka.SortContainers", "SortContainers", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "dsoltyka.SortContainers";

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		internal static Settings Settings { get; private set; }

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = new Settings(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("dsoltyka.SortContainers");
			_harmony.PatchAll(typeof(SortButton));
			Log.LogInfo((object)"SortContainers 1.0.0 loaded");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal sealed class Settings
	{
		public ConfigEntry<bool> MergeStacks { get; }

		public ConfigEntry<float> ButtonGap { get; }

		public ConfigEntry<float> ButtonSize { get; }

		public ConfigEntry<float> IconPadding { get; }

		public Settings(ConfigFile config)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			MergeStacks = config.Bind<bool>("1 - General", "MergeStacks", true, "Also combine partial stacks of the same item while sorting.");
			ButtonGap = config.Bind<float>("2 - Layout", "ButtonGap", 4f, new ConfigDescription("Gap between the vanilla 'Place stacks' button and the sort button, in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ButtonSize = config.Bind<float>("2 - Layout", "ButtonSize", 0f, new ConfigDescription("Sort button size in pixels. 0 matches the height of the 'Place stacks' button.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 128f), Array.Empty<object>()));
			IconPadding = config.Bind<float>("2 - Layout", "IconPadding", 6f, new ConfigDescription("Inset of the icon inside the button, in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 32f), Array.Empty<object>()));
		}
	}
	[HarmonyPatch]
	internal static class SortButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OnClicked;
		}

		private const string ButtonName = "SortContainers.SortButton";

		private static readonly FieldRef<InventoryGui, Container> CurrentContainerRef = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer");

		private static readonly Action<InventoryGui, ItemData, Inventory, int> SetupDragItem = AccessTools.MethodDelegate<Action<InventoryGui, ItemData, Inventory, int>>(AccessTools.Method(typeof(InventoryGui), "SetupDragItem", (Type[])null, (Type[])null), (object)null, true);

		private static Button s_button;

		private static Sprite s_icon;

		private static bool s_iconLoadAttempted;

		private static Settings S => Plugin.Settings;

		[HarmonyPatch(typeof(InventoryGui), "Show")]
		[HarmonyPostfix]
		private static void Show_Postfix(InventoryGui __instance)
		{
			try
			{
				Ensure(__instance);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to create the sort button: {arg}");
			}
		}

		private static void Ensure(InventoryGui gui)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00d9: 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_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_011b: 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_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: 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_0168: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s_button != (Object)null)
			{
				return;
			}
			Button stackAllButton = gui.m_stackAllButton;
			if ((Object)(object)stackAllButton == (Object)null)
			{
				Plugin.Log.LogWarning((object)"InventoryGui has no 'Place stacks' button to attach to; sort button not created.");
				return;
			}
			GameObject obj = Object.Instantiate<GameObject>(((Component)stackAllButton).gameObject, ((Component)stackAllButton).transform.parent);
			((Object)obj).name = "SortContainers.SortButton";
			Button component = obj.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			ButtonClickedEvent onClick = component.onClick;
			object obj2 = <>O.<0>__OnClicked;
			if (obj2 == null)
			{
				UnityAction val = OnClicked;
				<>O.<0>__OnClicked = val;
				obj2 = (object)val;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj2);
			((Selectable)component).interactable = true;
			UITooltip component2 = obj.GetComponent<UITooltip>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_text = "Sort";
			}
			RectTransform val2 = (RectTransform)((Component)stackAllButton).transform;
			RectTransform val3 = (RectTransform)obj.transform;
			Rect rect;
			float num;
			if (!(S.ButtonSize.Value > 0f))
			{
				rect = val2.rect;
				num = ((Rect)(ref rect)).height;
			}
			else
			{
				num = S.ButtonSize.Value;
			}
			float num2 = num;
			val3.sizeDelta = new Vector2(num2, num2);
			float x = val2.anchoredPosition.x;
			float num3 = 1f - val2.pivot.x;
			rect = val2.rect;
			float num4 = x + num3 * ((Rect)(ref rect)).width;
			val3.anchoredPosition = new Vector2(num4 + S.ButtonGap.Value + val3.pivot.x * num2, val2.anchoredPosition.y);
			TMP_Text componentInChildren = obj.GetComponentInChildren<TMP_Text>(true);
			Sprite val4 = LoadIcon();
			if ((Object)(object)val4 != (Object)null)
			{
				if ((Object)(object)componentInChildren != (Object)null)
				{
					((Component)componentInChildren).gameObject.SetActive(false);
				}
				CreateIcon(val3, val4, componentInChildren);
			}
			else if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = "Sort";
			}
			s_button = component;
		}

		private static void CreateIcon(RectTransform parent, Sprite sprite, TMP_Text styleSource)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Icon", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent((Transform)(object)parent, false);
			float value = S.IconPadding.Value;
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = new Vector2(value, value);
			val2.offsetMax = new Vector2(0f - value, 0f - value);
			Image component = val.GetComponent<Image>();
			component.sprite = sprite;
			component.preserveAspect = true;
			((Graphic)component).raycastTarget = false;
			((Graphic)component).color = (((Object)(object)styleSource != (Object)null) ? ((Graphic)styleSource).color : Color.white);
		}

		private static void OnClicked()
		{
			InventoryGui instance = InventoryGui.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting())
			{
				return;
			}
			Container val = CurrentContainerRef.Invoke(instance);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (!val.IsOwner())
			{
				Plugin.Log.LogInfo((object)"Container is not owned by this client yet; ignoring sort.");
				return;
			}
			try
			{
				SetupDragItem?.Invoke(instance, null, null, 1);
				InventorySorter.Sort(val.GetInventory(), S.MergeStacks.Value);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Sort failed: {arg}");
			}
		}

		private static Sprite LoadIcon()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if (s_iconLoadAttempted)
			{
				return s_icon;
			}
			s_iconLoadAttempted = true;
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("sort.png");
				if (stream == null)
				{
					Plugin.Log.LogWarning((object)"Embedded sort.png not found; using a text label instead.");
					return null;
				}
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				Texture2D val = AssetUtils.LoadImage(memoryStream.ToArray());
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Embedded sort.png could not be decoded; using a text label instead.");
					return null;
				}
				((Texture)val).filterMode = (FilterMode)1;
				s_icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
				((Object)s_icon).name = "SortContainers.Icon";
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not load the sort icon: " + ex.Message));
			}
			return s_icon;
		}
	}
	internal static class BuildInfo
	{
		public const string Name = "SortContainers";

		public const string Version = "1.0.0";
	}
}