Decompiled source of ChestSorter v1.0.0

ChestSorter.dll

Decompiled 2 weeks 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 System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using ValheimShared;

[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 ValheimShared
{
	internal static class Perf
	{
		internal readonly struct Scope : IDisposable
		{
			private readonly string _name;

			private readonly long _start;

			internal Scope(string name)
			{
				_name = name;
				_start = Stopwatch.GetTimestamp();
			}

			public void Dispose()
			{
				Add(_name, (double)(Stopwatch.GetTimestamp() - _start) * 1000.0 / (double)Stopwatch.Frequency);
			}
		}

		private static readonly Dictionary<string, (double ms, int calls)> _current = new Dictionary<string, (double, int)>();

		private static readonly List<string> _marks = new List<string>();

		private static int _frame = -1;

		private static Dictionary<string, (double ms, int calls)> _previous = new Dictionary<string, (double, int)>();

		private static List<string> _previousMarks = new List<string>();

		private static int _gcCount;

		internal static bool Enabled = true;

		internal static void BeginFrame(Action<string> log, float thresholdMs)
		{
			Rotate();
			int num = GC.CollectionCount(0);
			int num2 = num - _gcCount;
			_gcCount = num;
			if (!Enabled)
			{
				return;
			}
			float num3 = Time.unscaledDeltaTime * 1000f;
			if (!(num3 < thresholdMs) && Time.frameCount >= 2)
			{
				double num4 = _previous.Values.Sum(((double ms, int calls) v) => v.ms);
				string text = ((_previous.Count == 0) ? "none" : string.Join(", ", from kv in _previous
					orderby kv.Value.ms descending
					select $"{kv.Key} {kv.Value.ms:F1}ms" + ((kv.Value.calls > 1) ? $" x{kv.Value.calls}" : "")));
				string text2 = ((_previousMarks.Count == 0) ? "" : (" | events: " + string.Join(", ", _previousMarks)));
				string text3 = ((num2 > 0) ? $" | GC ran {num2}x (heap {GC.GetTotalMemory(forceFullCollection: false) / 1048576} MB)" : "");
				log($"[perf] slow frame {num3:F0}ms (frame {Time.frameCount - 1}), this plugin {num4:F1}ms: {text}{text2}{text3}");
			}
		}

		internal static void Mark(string evt)
		{
			Rotate();
			if (Enabled)
			{
				_marks.Add(evt);
			}
		}

		internal static Scope Measure(string name)
		{
			return new Scope(name);
		}

		internal static void Add(string name, double ms)
		{
			Rotate();
			if (Enabled)
			{
				_current.TryGetValue(name, out (double, int) value);
				_current[name] = (value.Item1 + ms, value.Item2 + 1);
			}
		}

		private static void Rotate()
		{
			if (_frame != Time.frameCount)
			{
				_frame = Time.frameCount;
				_previous = new Dictionary<string, (double, int)>(_current);
				_previousMarks = new List<string>(_marks);
				_current.Clear();
				_marks.Clear();
			}
		}
	}
	internal static class UiClone
	{
		internal static Button Button(Button template, Transform parent, string name, string label, UnityAction onClick)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, parent, false);
			((Object)obj).name = name;
			StripVanillaBehaviour(obj);
			SetLabel(obj, label);
			Button component = obj.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			if (onClick != null)
			{
				((UnityEvent)component.onClick).AddListener(onClick);
			}
			((Selectable)component).interactable = true;
			IgnoreLayout(obj);
			return component;
		}

		internal static TMP_Text Text(TMP_Text template, Transform parent, string name, string text)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, parent, false);
			((Object)val).name = name;
			StripVanillaBehaviour(val);
			foreach (Transform item in val.transform)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			TMP_Text component = val.GetComponent<TMP_Text>();
			component.text = text;
			IgnoreLayout(val);
			return component;
		}

		internal static void SetLabel(GameObject go, string label)
		{
			TMP_Text componentInChildren = go.GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = label;
				return;
			}
			Text componentInChildren2 = go.GetComponentInChildren<Text>(true);
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				componentInChildren2.text = label;
			}
		}

		internal static TMP_Text LabelOf(GameObject go)
		{
			return go.GetComponentInChildren<TMP_Text>(true);
		}

		internal static void Place(RectTransform rt, float x, float y, float w, float h)
		{
			//IL_0013: 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_002a: 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)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			rt.anchorMax = val;
			rt.anchorMin = val;
			rt.pivot = new Vector2(0f, 1f);
			rt.anchoredPosition = new Vector2(x, 0f - y);
			rt.sizeDelta = new Vector2(w, h);
		}

		internal static void IgnoreLayout(GameObject go)
		{
			LayoutElement val = go.GetComponent<LayoutElement>();
			if ((Object)(object)val == (Object)null)
			{
				val = go.AddComponent<LayoutElement>();
			}
			val.ignoreLayout = true;
		}

		internal static void CopyBackground(RectTransform source, Image target)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			Image val = (((Object)(object)source == (Object)null) ? null : (from i in ((Component)source).GetComponentsInChildren<Image>(true)
				where (Object)(object)i.sprite != (Object)null && (Object)(object)i != (Object)(object)target
				select i).OrderByDescending(delegate(Image i)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: 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_001e: Unknown result type (might be due to invalid IL or missing references)
				Rect rect = ((Graphic)i).rectTransform.rect;
				float width = ((Rect)(ref rect)).width;
				rect = ((Graphic)i).rectTransform.rect;
				return width * ((Rect)(ref rect)).height;
			}).FirstOrDefault());
			if ((Object)(object)val == (Object)null)
			{
				((Graphic)target).color = new Color(0.12f, 0.1f, 0.08f, 0.95f);
				return;
			}
			target.sprite = val.sprite;
			target.type = val.type;
			((Graphic)target).material = ((Graphic)val).material;
			((Graphic)target).color = ((Graphic)val).color;
			target.pixelsPerUnitMultiplier = val.pixelsPerUnitMultiplier;
		}

		internal static RectTransform ScrollArea(Transform parent, string name, float x, float y, float w, float h, Scrollbar scrollbarTemplate, out ScrollRect scroll)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_0067: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: 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_00ed: 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_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Expected O, but got Unknown
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			Place(val2, x, y, w - 14f - 4f, h);
			val.AddComponent<RectMask2D>();
			((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0f);
			GameObject val3 = new GameObject("Content", new Type[1] { typeof(RectTransform) });
			RectTransform val4 = (RectTransform)val3.transform;
			((Transform)val4).SetParent((Transform)(object)val2, false);
			val4.anchorMin = new Vector2(0f, 1f);
			val4.anchorMax = new Vector2(1f, 1f);
			val4.pivot = new Vector2(0.5f, 1f);
			val4.anchoredPosition = Vector2.zero;
			val4.sizeDelta = Vector2.zero;
			VerticalLayoutGroup obj = val3.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 2f;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			val3.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			scroll = val.AddComponent<ScrollRect>();
			scroll.content = val4;
			scroll.viewport = val2;
			scroll.horizontal = false;
			scroll.vertical = true;
			scroll.movementType = (MovementType)2;
			scroll.inertia = false;
			scroll.scrollSensitivity = 30f;
			if ((Object)(object)scrollbarTemplate != (Object)null)
			{
				GameObject obj2 = Object.Instantiate<GameObject>(((Component)scrollbarTemplate).gameObject, parent, false);
				((Object)obj2).name = name + "_Scrollbar";
				IgnoreLayout(obj2);
				Place((RectTransform)obj2.transform, x + w - 14f, y, 14f, h);
				Scrollbar component = obj2.GetComponent<Scrollbar>();
				component.onValueChanged = new ScrollEvent();
				component.direction = (Direction)2;
				scroll.verticalScrollbar = component;
				scroll.verticalScrollbarVisibility = (ScrollbarVisibility)0;
			}
			return val4;
		}

		private static void StripVanillaBehaviour(GameObject go)
		{
			Localize[] componentsInChildren = go.GetComponentsInChildren<Localize>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			UIGamePad[] componentsInChildren2 = go.GetComponentsInChildren<UIGamePad>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
		}
	}
}
namespace ChestSorter
{
	internal static class CategoryConfig
	{
		private static string _path;

		private static DateTime _loadedStampUtc;

		internal static CategoryRules Rules { get; private set; }

		internal static void Init(string configDir)
		{
			_path = Path.Combine(configDir, "ChestSorter.categories.txt");
			try
			{
				if (!File.Exists(_path))
				{
					File.WriteAllText(_path, "# ChestSorter category rules. Edit freely - the file is re-read automatically\n# the next time you press Push after saving it.\n#\n# Line formats (fields separated by |):\n#   version  | <n>\n#   category | <id> | <display name>        id is what gets stored on chests - never rename an id,\n#                                           add an alias line instead\n#   alias    | <old id> | <new id>          chests tagged with <old id> are treated as <new id>\n#   itemtype | <ItemType> | <category id>   fallback for an item type when no rule below matches\n#   override | <scope> | <prefab> | <id>    exact prefab (internal) name\n#   prefix   | <scope> | <text> | <id>      prefab name starts with text\n#   endswith | <scope> | <text> | <id>      prefab name ends with text\n#   contains | <scope> | <text> | <id>      prefab name contains text\n#   suffix   | <scope> | <word> | <id>      LAST PascalCase word of the prefab name equals word\n#   foodcolor | <color> | <id>              food by its icon colour in the game: Health (red),\n#                                           Stamina (yellow), Eitr (blue), Balanced (white)\n#   parent   | <id> | <parent id>           a chest tagged with the parent also takes this category;\n#                                           Push prefers exact-category chests and uses parent-tagged\n#                                           ones as overflow\n# <scope> is an item type (Material, Consumable, Misc, ...) or * for any.\n# Matching order: override, prefix, endswith, contains, suffix, foodcolor, itemtype.\n# Unmatched Material items land in Misc; unmatched items of other types use their item type name.\n\nversion | 4\n\ncategory | Trophy | Trophies\ncategory | Ammo | Ammo\ncategory | SiegeAmmo | Siege Ammo\ncategory | Chest | Armor: Chest\ncategory | Helmet | Armor: Helmets\ncategory | Legs | Armor: Legs\ncategory | Shoulder | Capes\ncategory | Shield | Shields\ncategory | OneHandedWeapon | One-Handed Weapons\ncategory | TwoHandedWeapon | Two-Handed Weapons\ncategory | TwoHandedWeaponLeft | Two-Handed (Left Hand)\ncategory | Bow | Bows & Crossbows\ncategory | Tool | Tools\ncategory | Utility | Utility\ncategory | Torch | Torches & Lanterns\ncategory | Trinket | Trinkets\ncategory | Fish | Fish\ncategory | Food | Food (all kinds)\ncategory | Produce | Food: Produce\ncategory | RawMeat | Food: Raw Meat\ncategory | CookingIngredients | Food: Cooking Ingredients\ncategory | CookedMeat | Food: Cooked Meat\ncategory | MealsHealth | Food: Meals (Health)\ncategory | MealsStamina | Food: Meals (Stamina)\ncategory | MealsEitr | Food: Meals (Eitr)\ncategory | MealsBalanced | Food: Meals (Balanced)\ncategory | Feasts | Food: Feasts\ncategory | Potions | Meads & Potions\ncategory | Customization | Cosmetics\ncategory | HidesAndFur | Hides & Fur\ncategory | BonesAndSkeleton | Bones\ncategory | Organs | Organs\ncategory | OresAndMetals | Ores & Metals\ncategory | BuildingStone | Building Stone\ncategory | Minerals | Minerals\ncategory | Wood | Wood\ncategory | PlantsAndSeeds | Plants & Seeds\ncategory | Textiles | Textiles\ncategory | Feathers | Feathers\ncategory | ResinAndTar | Resin & Tar\ncategory | ChitinAndScales | Chitin & Scales\ncategory | RareCores | Cores & Essences\ncategory | Valuables | Valuables\ncategory | QuestAndBoss | Quest & Boss Items\ncategory | Taming | Taming & Saddles\ncategory | CraftingParts | Crafting Parts\ncategory | MeadBases | Mead Bases\ncategory | UpgradeIdols | Upgrade Idols\ncategory | Spices | Spices\ncategory | Fireworks | Fireworks\ncategory | CastsAndMoulds | Casts & Moulds\ncategory | Misc | Misc\n\n# Old ids from earlier builds.\nalias | StoneAndGems | BuildingStone\nalias | Currency | Valuables\nalias | RawDishes | CookingIngredients\nalias | Consumable | Food\nalias | AmmoNonEquipable | SiegeAmmo\nalias | FeastIngredients | Feasts\n\n# --- Food family: a chest tagged \"Food\" takes all of these.\nparent | Produce | Food\nparent | RawMeat | Food\nparent | CookingIngredients | Food\nparent | CookedMeat | Food\nparent | MealsHealth | Food\nparent | MealsStamina | Food\nparent | MealsEitr | Food\nparent | MealsBalanced | Food\nparent | Feasts | Food\n\n# Prepared food not matched by a name rule below, by its icon colour.\nfoodcolor | Health | MealsHealth\nfoodcolor | Stamina | MealsStamina\nfoodcolor | Eitr | MealsEitr\nfoodcolor | Balanced | MealsBalanced\n\nitemtype | Consumable | Food\nitemtype | AmmoNonEquipable | SiegeAmmo\nitemtype | Material | Misc\nitemtype | Misc | Misc\n\n# --- Exact exceptions: merged-lowercase names the suffix rule can't split\n# (Leatherstraps, Bloodbag...), modifiers after the material word\n# (FlametalNew, OrbFrostFire...), and deliberate regroupings.\noverride | * | BoneFragments | BonesAndSkeleton\noverride | * | Charredskull | BonesAndSkeleton\noverride | * | NeckTail | BonesAndSkeleton\noverride | * | BonemawSerpentScale | ChitinAndScales\noverride | * | Leatherstraps | HidesAndFur\noverride | * | ElakingHairBundle | HidesAndFur\noverride | * | WolfHairBundle | HidesAndFur\noverride | * | Bilebag | Organs\noverride | * | Bloodbag | Organs\noverride | * | Softtissue | Organs\noverride | * | CuredSquirrelHamstring | Organs\noverride | * | Ironpit | OresAndMetals\noverride | * | CharredCogwheel | OresAndMetals\noverride | * | FlametalNew | OresAndMetals\noverride | * | FlametalOreNew | OresAndMetals\noverride | * | Grausten | BuildingStone\noverride | * | PungentPebbles | BuildingStone\noverride | * | Thunderstone | Minerals\noverride | * | SulfurStone | Minerals\noverride | * | SharpeningStone | CraftingParts\noverride | * | CandleWick | CraftingParts\noverride | * | Hook | CraftingParts\noverride | * | BarrelRings | CraftingParts\noverride | * | ScytheHandle | CraftingParts\noverride | * | Blackwood | Wood\noverride | * | Frostwood | Wood\noverride | * | FirConeFrost | PlantsAndSeeds\noverride | * | FragrantBundle | PlantsAndSeeds\noverride | * | Feathers | Feathers\noverride | * | CelestialFeather | Feathers\noverride | * | BreadDough | CookingIngredients\noverride | * | BarleyFlour | CookingIngredients\noverride | * | OatFlour | CookingIngredients\noverride | * | ChickenEgg | Produce\n# Edible as-is, but really crops, forage and cooking ingredients.\noverride | Consumable | Blueberries | Produce\noverride | Consumable | Raspberry | Produce\noverride | Consumable | Cloudberry | Produce\noverride | Consumable | Lingonberry | Produce\noverride | Consumable | Vineberry | Produce\noverride | Consumable | Pukeberries | Produce\noverride | Consumable | Carrot | Produce\noverride | Consumable | Onion | Produce\noverride | Consumable | Kale | Produce\noverride | Consumable | Oat | Produce\noverride | Consumable | Poteitr | Produce\noverride | Consumable | Honey | Produce\noverride | Consumable | RoyalJelly | Produce\noverride | Consumable | Fiddleheadfern | Produce\noverride | Consumable | GlowWorm | Produce\noverride | Consumable | Mushroom | Produce\noverride | Consumable | MushroomBlue | Produce\noverride | Consumable | MushroomBzerker | Produce\noverride | Consumable | MushroomJotunPuffs | Produce\noverride | Consumable | MushroomMagecap | Produce\noverride | Consumable | MushroomSmokePuff | Produce\noverride | Consumable | MushroomYellow | Produce\noverride | Consumable | NeckTailGrilled | CookedMeat\noverride | Consumable | RottenMeat | RawMeat\noverride | * | BarleyWineBase | MeadBases\noverride | * | BarleyWine | Potions\noverride | * | OozeMork | ResinAndTar\noverride | * | ShieldCore | RareCores\noverride | * | DvergrNeedle | RareCores\noverride | * | BlobVial | RareCores\noverride | * | Ectoplasm | RareCores\noverride | * | Voidplasm | RareCores\noverride | * | Wisp | RareCores\noverride | * | FaderEmber | RareCores\noverride | * | OrbFrostFire | RareCores\noverride | * | OrbThunderBlood | RareCores\noverride | * | PowderedDragonEgg | RareCores\noverride | * | HardAntler | QuestAndBoss\noverride | * | QueenDrop | QuestAndBoss\noverride | * | FaderDrop | QuestAndBoss\noverride | * | FrozenKingDrop | QuestAndBoss\noverride | * | DragonTear | QuestAndBoss\noverride | * | YagluthDrop | QuestAndBoss\noverride | * | DragonEgg | QuestAndBoss\noverride | * | AxeHead1 | QuestAndBoss\noverride | * | AxeHead2 | QuestAndBoss\noverride | * | Pot_Shard_Green | QuestAndBoss\noverride | * | Pot_Shard_Red | QuestAndBoss\noverride | * | CryptKey | QuestAndBoss\noverride | * | DvergrKey | QuestAndBoss\noverride | * | BloodGoldKey | QuestAndBoss\noverride | * | HildirKey_forestcrypt | QuestAndBoss\noverride | * | HildirKey_mountaincave | QuestAndBoss\noverride | * | HildirKey_plainsfortress | QuestAndBoss\noverride | * | chest_hildir1 | QuestAndBoss\noverride | * | chest_hildir2 | QuestAndBoss\noverride | * | chest_hildir3 | QuestAndBoss\noverride | * | GoblinTotem | QuestAndBoss\noverride | * | HatefulBlood | QuestAndBoss\noverride | * | Bell | QuestAndBoss\noverride | * | AncientSeed | QuestAndBoss\noverride | * | AsksvinEgg | Taming\noverride | * | VoltureEgg | Taming\noverride | * | SaddleLox | Taming\noverride | * | SaddleAsksvin | Taming\noverride | * | SaddleMoose | Taming\noverride | * | BombSiege | SiegeAmmo\noverride | * | Catapult_ammo | SiegeAmmo\noverride | * | Catapult_Ammo_BloodGold | SiegeAmmo\n\n# --- Name families where the type word is at the front.\nprefix | Material | MeadBase | MeadBases\nprefix | Material | Upgrader | UpgradeIdols\nprefix | Material | Spice | Spices\nprefix | Material | FireworksRocket_ | Fireworks\nprefix | Material | Mold | CastsAndMoulds\nprefix | Material | AncientGemstone | Valuables\nprefix | Material | Gemstone | Valuables\nprefix | Material | Jute | Textiles\nprefix | Material | Feast | Feasts\nprefix | Consumable | Feast | Feasts\nprefix | Consumable | Mead | Potions\nprefix | Consumable | Cooked | CookedMeat\n# \"Cast: ...\" armor and staff moulds are named *Uncooked like food - must win over the Uncooked rule.\nprefix | Material | ArmorGold | CastsAndMoulds\nprefix | Material | Staff | CastsAndMoulds\n\n# \"Cast: ...\" items are named *GoldUncooked - must win over the generic Uncooked rule below.\nendswith | Material | GoldUncooked | CastsAndMoulds\nendswith | Material | MoldUncooked | CastsAndMoulds\nendswith | Consumable | Cooked | CookedMeat\ncontains | Material | Uncooked | CookingIngredients\n\n# --- Last-word rules for Material items.\nsuffix | Material | Hide | HidesAndFur\nsuffix | Material | Pelt | HidesAndFur\nsuffix | Material | Leather | HidesAndFur\nsuffix | Material | Straps | HidesAndFur\nsuffix | Material | Scraps | HidesAndFur\nsuffix | Material | Meat | RawMeat\nsuffix | Material | Blubber | RawMeat\nsuffix | Material | Raw | RawMeat\nsuffix | Material | Bone | BonesAndSkeleton\nsuffix | Material | Bones | BonesAndSkeleton\nsuffix | Material | Skull | BonesAndSkeleton\nsuffix | Material | Ribcage | BonesAndSkeleton\nsuffix | Material | Pelvic | BonesAndSkeleton\nsuffix | Material | Neck | BonesAndSkeleton\nsuffix | Material | Tooth | BonesAndSkeleton\nsuffix | Material | Fang | BonesAndSkeleton\nsuffix | Material | Claw | BonesAndSkeleton\nsuffix | Material | Claws | BonesAndSkeleton\nsuffix | Material | Paw | BonesAndSkeleton\nsuffix | Material | Antler | BonesAndSkeleton\nsuffix | Material | Mandible | BonesAndSkeleton\nsuffix | Material | Bladder | Organs\nsuffix | Material | Bag | Organs\nsuffix | Material | Entrails | Organs\nsuffix | Material | Sinew | Organs\nsuffix | Material | Heart | Organs\nsuffix | Material | Tissue | Organs\nsuffix | Material | Sack | Organs\nsuffix | Material | Eye | Organs\nsuffix | Material | Gland | Organs\nsuffix | Material | Ore | OresAndMetals\nsuffix | Material | Scrap | OresAndMetals\nsuffix | Material | Nails | OresAndMetals\nsuffix | Material | Copper | OresAndMetals\nsuffix | Material | Bronze | OresAndMetals\nsuffix | Material | Iron | OresAndMetals\nsuffix | Material | Silver | OresAndMetals\nsuffix | Material | Tin | OresAndMetals\nsuffix | Material | Gold | OresAndMetals\nsuffix | Material | Flametal | OresAndMetals\nsuffix | Material | Metal | OresAndMetals\nsuffix | Material | Chain | OresAndMetals\nsuffix | Material | Spring | OresAndMetals\nsuffix | Material | Stone | BuildingStone\nsuffix | Material | Rock | BuildingStone\nsuffix | Material | Marble | BuildingStone\nsuffix | Material | Flint | Minerals\nsuffix | Material | Crystal | Minerals\nsuffix | Material | Coal | Minerals\nsuffix | Material | Obsidian | Minerals\nsuffix | Material | Powder | Minerals\nsuffix | Material | Plate | CraftingParts\nsuffix | Material | Wood | Wood\nsuffix | Material | Log | Wood\nsuffix | Material | Bark | Wood\nsuffix | Material | Branch | Wood\nsuffix | Material | Seeds | PlantsAndSeeds\nsuffix | Material | Flax | PlantsAndSeeds\nsuffix | Material | Barley | Produce\nsuffix | Material | Dandelion | PlantsAndSeeds\nsuffix | Material | Thistle | PlantsAndSeeds\nsuffix | Material | Acorn | PlantsAndSeeds\nsuffix | Material | Acorns | PlantsAndSeeds\nsuffix | Material | Cone | PlantsAndSeeds\nsuffix | Material | Root | PlantsAndSeeds\nsuffix | Material | Roots | PlantsAndSeeds\nsuffix | Material | Turnip | Produce\nsuffix | Material | Seaweed | PlantsAndSeeds\nsuffix | Material | Thread | Textiles\nsuffix | Material | Resin | ResinAndTar\nsuffix | Material | Sap | ResinAndTar\nsuffix | Material | Tar | ResinAndTar\nsuffix | Material | Ooze | ResinAndTar\nsuffix | Material | Pulp | ResinAndTar\nsuffix | Material | Guck | ResinAndTar\nsuffix | Material | Ice | ResinAndTar\nsuffix | Material | Fuel | ResinAndTar\nsuffix | Material | Carapace | ChitinAndScales\nsuffix | Material | Chitin | ChitinAndScales\nsuffix | Material | Scale | ChitinAndScales\nsuffix | Material | Needle | ChitinAndScales\nsuffix | Material | Bee | Taming\nsuffix | Material | Core | RareCores\nsuffix | Material | Eitr | RareCores\nsuffix | Material | Remains | RareCores\nsuffix | Material | Orb | RareCores\nsuffix | Material | Fragment | QuestAndBoss\nsuffix | Material | Coin | Valuables\nsuffix | Material | Coins | Valuables\nsuffix | Material | Necklace | Valuables\nsuffix | Material | Amber | Valuables\nsuffix | Material | Pearl | Valuables\nsuffix | Material | Pearls | Valuables\nsuffix | Material | Ruby | Valuables\nsuffix | Material | Jewel | Valuables\n");
					ChestSorterPlugin.WriteLog("Wrote default category rules to " + _path + ".");
				}
				else
				{
					CategoryRules categoryRules = CategoryRules.Parse(File.ReadAllText(_path), new List<string>());
					if (categoryRules.Version < 4)
					{
						string text = _path + $".v{categoryRules.Version}.bak";
						File.Copy(_path, text, overwrite: true);
						File.WriteAllText(_path, "# ChestSorter category rules. Edit freely - the file is re-read automatically\n# the next time you press Push after saving it.\n#\n# Line formats (fields separated by |):\n#   version  | <n>\n#   category | <id> | <display name>        id is what gets stored on chests - never rename an id,\n#                                           add an alias line instead\n#   alias    | <old id> | <new id>          chests tagged with <old id> are treated as <new id>\n#   itemtype | <ItemType> | <category id>   fallback for an item type when no rule below matches\n#   override | <scope> | <prefab> | <id>    exact prefab (internal) name\n#   prefix   | <scope> | <text> | <id>      prefab name starts with text\n#   endswith | <scope> | <text> | <id>      prefab name ends with text\n#   contains | <scope> | <text> | <id>      prefab name contains text\n#   suffix   | <scope> | <word> | <id>      LAST PascalCase word of the prefab name equals word\n#   foodcolor | <color> | <id>              food by its icon colour in the game: Health (red),\n#                                           Stamina (yellow), Eitr (blue), Balanced (white)\n#   parent   | <id> | <parent id>           a chest tagged with the parent also takes this category;\n#                                           Push prefers exact-category chests and uses parent-tagged\n#                                           ones as overflow\n# <scope> is an item type (Material, Consumable, Misc, ...) or * for any.\n# Matching order: override, prefix, endswith, contains, suffix, foodcolor, itemtype.\n# Unmatched Material items land in Misc; unmatched items of other types use their item type name.\n\nversion | 4\n\ncategory | Trophy | Trophies\ncategory | Ammo | Ammo\ncategory | SiegeAmmo | Siege Ammo\ncategory | Chest | Armor: Chest\ncategory | Helmet | Armor: Helmets\ncategory | Legs | Armor: Legs\ncategory | Shoulder | Capes\ncategory | Shield | Shields\ncategory | OneHandedWeapon | One-Handed Weapons\ncategory | TwoHandedWeapon | Two-Handed Weapons\ncategory | TwoHandedWeaponLeft | Two-Handed (Left Hand)\ncategory | Bow | Bows & Crossbows\ncategory | Tool | Tools\ncategory | Utility | Utility\ncategory | Torch | Torches & Lanterns\ncategory | Trinket | Trinkets\ncategory | Fish | Fish\ncategory | Food | Food (all kinds)\ncategory | Produce | Food: Produce\ncategory | RawMeat | Food: Raw Meat\ncategory | CookingIngredients | Food: Cooking Ingredients\ncategory | CookedMeat | Food: Cooked Meat\ncategory | MealsHealth | Food: Meals (Health)\ncategory | MealsStamina | Food: Meals (Stamina)\ncategory | MealsEitr | Food: Meals (Eitr)\ncategory | MealsBalanced | Food: Meals (Balanced)\ncategory | Feasts | Food: Feasts\ncategory | Potions | Meads & Potions\ncategory | Customization | Cosmetics\ncategory | HidesAndFur | Hides & Fur\ncategory | BonesAndSkeleton | Bones\ncategory | Organs | Organs\ncategory | OresAndMetals | Ores & Metals\ncategory | BuildingStone | Building Stone\ncategory | Minerals | Minerals\ncategory | Wood | Wood\ncategory | PlantsAndSeeds | Plants & Seeds\ncategory | Textiles | Textiles\ncategory | Feathers | Feathers\ncategory | ResinAndTar | Resin & Tar\ncategory | ChitinAndScales | Chitin & Scales\ncategory | RareCores | Cores & Essences\ncategory | Valuables | Valuables\ncategory | QuestAndBoss | Quest & Boss Items\ncategory | Taming | Taming & Saddles\ncategory | CraftingParts | Crafting Parts\ncategory | MeadBases | Mead Bases\ncategory | UpgradeIdols | Upgrade Idols\ncategory | Spices | Spices\ncategory | Fireworks | Fireworks\ncategory | CastsAndMoulds | Casts & Moulds\ncategory | Misc | Misc\n\n# Old ids from earlier builds.\nalias | StoneAndGems | BuildingStone\nalias | Currency | Valuables\nalias | RawDishes | CookingIngredients\nalias | Consumable | Food\nalias | AmmoNonEquipable | SiegeAmmo\nalias | FeastIngredients | Feasts\n\n# --- Food family: a chest tagged \"Food\" takes all of these.\nparent | Produce | Food\nparent | RawMeat | Food\nparent | CookingIngredients | Food\nparent | CookedMeat | Food\nparent | MealsHealth | Food\nparent | MealsStamina | Food\nparent | MealsEitr | Food\nparent | MealsBalanced | Food\nparent | Feasts | Food\n\n# Prepared food not matched by a name rule below, by its icon colour.\nfoodcolor | Health | MealsHealth\nfoodcolor | Stamina | MealsStamina\nfoodcolor | Eitr | MealsEitr\nfoodcolor | Balanced | MealsBalanced\n\nitemtype | Consumable | Food\nitemtype | AmmoNonEquipable | SiegeAmmo\nitemtype | Material | Misc\nitemtype | Misc | Misc\n\n# --- Exact exceptions: merged-lowercase names the suffix rule can't split\n# (Leatherstraps, Bloodbag...), modifiers after the material word\n# (FlametalNew, OrbFrostFire...), and deliberate regroupings.\noverride | * | BoneFragments | BonesAndSkeleton\noverride | * | Charredskull | BonesAndSkeleton\noverride | * | NeckTail | BonesAndSkeleton\noverride | * | BonemawSerpentScale | ChitinAndScales\noverride | * | Leatherstraps | HidesAndFur\noverride | * | ElakingHairBundle | HidesAndFur\noverride | * | WolfHairBundle | HidesAndFur\noverride | * | Bilebag | Organs\noverride | * | Bloodbag | Organs\noverride | * | Softtissue | Organs\noverride | * | CuredSquirrelHamstring | Organs\noverride | * | Ironpit | OresAndMetals\noverride | * | CharredCogwheel | OresAndMetals\noverride | * | FlametalNew | OresAndMetals\noverride | * | FlametalOreNew | OresAndMetals\noverride | * | Grausten | BuildingStone\noverride | * | PungentPebbles | BuildingStone\noverride | * | Thunderstone | Minerals\noverride | * | SulfurStone | Minerals\noverride | * | SharpeningStone | CraftingParts\noverride | * | CandleWick | CraftingParts\noverride | * | Hook | CraftingParts\noverride | * | BarrelRings | CraftingParts\noverride | * | ScytheHandle | CraftingParts\noverride | * | Blackwood | Wood\noverride | * | Frostwood | Wood\noverride | * | FirConeFrost | PlantsAndSeeds\noverride | * | FragrantBundle | PlantsAndSeeds\noverride | * | Feathers | Feathers\noverride | * | CelestialFeather | Feathers\noverride | * | BreadDough | CookingIngredients\noverride | * | BarleyFlour | CookingIngredients\noverride | * | OatFlour | CookingIngredients\noverride | * | ChickenEgg | Produce\n# Edible as-is, but really crops, forage and cooking ingredients.\noverride | Consumable | Blueberries | Produce\noverride | Consumable | Raspberry | Produce\noverride | Consumable | Cloudberry | Produce\noverride | Consumable | Lingonberry | Produce\noverride | Consumable | Vineberry | Produce\noverride | Consumable | Pukeberries | Produce\noverride | Consumable | Carrot | Produce\noverride | Consumable | Onion | Produce\noverride | Consumable | Kale | Produce\noverride | Consumable | Oat | Produce\noverride | Consumable | Poteitr | Produce\noverride | Consumable | Honey | Produce\noverride | Consumable | RoyalJelly | Produce\noverride | Consumable | Fiddleheadfern | Produce\noverride | Consumable | GlowWorm | Produce\noverride | Consumable | Mushroom | Produce\noverride | Consumable | MushroomBlue | Produce\noverride | Consumable | MushroomBzerker | Produce\noverride | Consumable | MushroomJotunPuffs | Produce\noverride | Consumable | MushroomMagecap | Produce\noverride | Consumable | MushroomSmokePuff | Produce\noverride | Consumable | MushroomYellow | Produce\noverride | Consumable | NeckTailGrilled | CookedMeat\noverride | Consumable | RottenMeat | RawMeat\noverride | * | BarleyWineBase | MeadBases\noverride | * | BarleyWine | Potions\noverride | * | OozeMork | ResinAndTar\noverride | * | ShieldCore | RareCores\noverride | * | DvergrNeedle | RareCores\noverride | * | BlobVial | RareCores\noverride | * | Ectoplasm | RareCores\noverride | * | Voidplasm | RareCores\noverride | * | Wisp | RareCores\noverride | * | FaderEmber | RareCores\noverride | * | OrbFrostFire | RareCores\noverride | * | OrbThunderBlood | RareCores\noverride | * | PowderedDragonEgg | RareCores\noverride | * | HardAntler | QuestAndBoss\noverride | * | QueenDrop | QuestAndBoss\noverride | * | FaderDrop | QuestAndBoss\noverride | * | FrozenKingDrop | QuestAndBoss\noverride | * | DragonTear | QuestAndBoss\noverride | * | YagluthDrop | QuestAndBoss\noverride | * | DragonEgg | QuestAndBoss\noverride | * | AxeHead1 | QuestAndBoss\noverride | * | AxeHead2 | QuestAndBoss\noverride | * | Pot_Shard_Green | QuestAndBoss\noverride | * | Pot_Shard_Red | QuestAndBoss\noverride | * | CryptKey | QuestAndBoss\noverride | * | DvergrKey | QuestAndBoss\noverride | * | BloodGoldKey | QuestAndBoss\noverride | * | HildirKey_forestcrypt | QuestAndBoss\noverride | * | HildirKey_mountaincave | QuestAndBoss\noverride | * | HildirKey_plainsfortress | QuestAndBoss\noverride | * | chest_hildir1 | QuestAndBoss\noverride | * | chest_hildir2 | QuestAndBoss\noverride | * | chest_hildir3 | QuestAndBoss\noverride | * | GoblinTotem | QuestAndBoss\noverride | * | HatefulBlood | QuestAndBoss\noverride | * | Bell | QuestAndBoss\noverride | * | AncientSeed | QuestAndBoss\noverride | * | AsksvinEgg | Taming\noverride | * | VoltureEgg | Taming\noverride | * | SaddleLox | Taming\noverride | * | SaddleAsksvin | Taming\noverride | * | SaddleMoose | Taming\noverride | * | BombSiege | SiegeAmmo\noverride | * | Catapult_ammo | SiegeAmmo\noverride | * | Catapult_Ammo_BloodGold | SiegeAmmo\n\n# --- Name families where the type word is at the front.\nprefix | Material | MeadBase | MeadBases\nprefix | Material | Upgrader | UpgradeIdols\nprefix | Material | Spice | Spices\nprefix | Material | FireworksRocket_ | Fireworks\nprefix | Material | Mold | CastsAndMoulds\nprefix | Material | AncientGemstone | Valuables\nprefix | Material | Gemstone | Valuables\nprefix | Material | Jute | Textiles\nprefix | Material | Feast | Feasts\nprefix | Consumable | Feast | Feasts\nprefix | Consumable | Mead | Potions\nprefix | Consumable | Cooked | CookedMeat\n# \"Cast: ...\" armor and staff moulds are named *Uncooked like food - must win over the Uncooked rule.\nprefix | Material | ArmorGold | CastsAndMoulds\nprefix | Material | Staff | CastsAndMoulds\n\n# \"Cast: ...\" items are named *GoldUncooked - must win over the generic Uncooked rule below.\nendswith | Material | GoldUncooked | CastsAndMoulds\nendswith | Material | MoldUncooked | CastsAndMoulds\nendswith | Consumable | Cooked | CookedMeat\ncontains | Material | Uncooked | CookingIngredients\n\n# --- Last-word rules for Material items.\nsuffix | Material | Hide | HidesAndFur\nsuffix | Material | Pelt | HidesAndFur\nsuffix | Material | Leather | HidesAndFur\nsuffix | Material | Straps | HidesAndFur\nsuffix | Material | Scraps | HidesAndFur\nsuffix | Material | Meat | RawMeat\nsuffix | Material | Blubber | RawMeat\nsuffix | Material | Raw | RawMeat\nsuffix | Material | Bone | BonesAndSkeleton\nsuffix | Material | Bones | BonesAndSkeleton\nsuffix | Material | Skull | BonesAndSkeleton\nsuffix | Material | Ribcage | BonesAndSkeleton\nsuffix | Material | Pelvic | BonesAndSkeleton\nsuffix | Material | Neck | BonesAndSkeleton\nsuffix | Material | Tooth | BonesAndSkeleton\nsuffix | Material | Fang | BonesAndSkeleton\nsuffix | Material | Claw | BonesAndSkeleton\nsuffix | Material | Claws | BonesAndSkeleton\nsuffix | Material | Paw | BonesAndSkeleton\nsuffix | Material | Antler | BonesAndSkeleton\nsuffix | Material | Mandible | BonesAndSkeleton\nsuffix | Material | Bladder | Organs\nsuffix | Material | Bag | Organs\nsuffix | Material | Entrails | Organs\nsuffix | Material | Sinew | Organs\nsuffix | Material | Heart | Organs\nsuffix | Material | Tissue | Organs\nsuffix | Material | Sack | Organs\nsuffix | Material | Eye | Organs\nsuffix | Material | Gland | Organs\nsuffix | Material | Ore | OresAndMetals\nsuffix | Material | Scrap | OresAndMetals\nsuffix | Material | Nails | OresAndMetals\nsuffix | Material | Copper | OresAndMetals\nsuffix | Material | Bronze | OresAndMetals\nsuffix | Material | Iron | OresAndMetals\nsuffix | Material | Silver | OresAndMetals\nsuffix | Material | Tin | OresAndMetals\nsuffix | Material | Gold | OresAndMetals\nsuffix | Material | Flametal | OresAndMetals\nsuffix | Material | Metal | OresAndMetals\nsuffix | Material | Chain | OresAndMetals\nsuffix | Material | Spring | OresAndMetals\nsuffix | Material | Stone | BuildingStone\nsuffix | Material | Rock | BuildingStone\nsuffix | Material | Marble | BuildingStone\nsuffix | Material | Flint | Minerals\nsuffix | Material | Crystal | Minerals\nsuffix | Material | Coal | Minerals\nsuffix | Material | Obsidian | Minerals\nsuffix | Material | Powder | Minerals\nsuffix | Material | Plate | CraftingParts\nsuffix | Material | Wood | Wood\nsuffix | Material | Log | Wood\nsuffix | Material | Bark | Wood\nsuffix | Material | Branch | Wood\nsuffix | Material | Seeds | PlantsAndSeeds\nsuffix | Material | Flax | PlantsAndSeeds\nsuffix | Material | Barley | Produce\nsuffix | Material | Dandelion | PlantsAndSeeds\nsuffix | Material | Thistle | PlantsAndSeeds\nsuffix | Material | Acorn | PlantsAndSeeds\nsuffix | Material | Acorns | PlantsAndSeeds\nsuffix | Material | Cone | PlantsAndSeeds\nsuffix | Material | Root | PlantsAndSeeds\nsuffix | Material | Roots | PlantsAndSeeds\nsuffix | Material | Turnip | Produce\nsuffix | Material | Seaweed | PlantsAndSeeds\nsuffix | Material | Thread | Textiles\nsuffix | Material | Resin | ResinAndTar\nsuffix | Material | Sap | ResinAndTar\nsuffix | Material | Tar | ResinAndTar\nsuffix | Material | Ooze | ResinAndTar\nsuffix | Material | Pulp | ResinAndTar\nsuffix | Material | Guck | ResinAndTar\nsuffix | Material | Ice | ResinAndTar\nsuffix | Material | Fuel | ResinAndTar\nsuffix | Material | Carapace | ChitinAndScales\nsuffix | Material | Chitin | ChitinAndScales\nsuffix | Material | Scale | ChitinAndScales\nsuffix | Material | Needle | ChitinAndScales\nsuffix | Material | Bee | Taming\nsuffix | Material | Core | RareCores\nsuffix | Material | Eitr | RareCores\nsuffix | Material | Remains | RareCores\nsuffix | Material | Orb | RareCores\nsuffix | Material | Fragment | QuestAndBoss\nsuffix | Material | Coin | Valuables\nsuffix | Material | Coins | Valuables\nsuffix | Material | Necklace | Valuables\nsuffix | Material | Amber | Valuables\nsuffix | Material | Pearl | Valuables\nsuffix | Material | Pearls | Valuables\nsuffix | Material | Ruby | Valuables\nsuffix | Material | Jewel | Valuables\n");
						ChestSorterPlugin.WriteLog($"Category rules updated to v{4}; your previous file was saved as {text}.");
					}
				}
			}
			catch (Exception ex)
			{
				ChestSorterPlugin.WriteLog("CategoryConfig init ERROR (falling back to built-in rules): " + ex.Message);
			}
			Load();
		}

		internal static void ReloadIfChanged()
		{
			try
			{
				if (_path != null && File.Exists(_path) && File.GetLastWriteTimeUtc(_path) != _loadedStampUtc)
				{
					Load();
				}
			}
			catch
			{
			}
		}

		private static void Load()
		{
			List<string> list = new List<string>();
			string text = "# ChestSorter category rules. Edit freely - the file is re-read automatically\n# the next time you press Push after saving it.\n#\n# Line formats (fields separated by |):\n#   version  | <n>\n#   category | <id> | <display name>        id is what gets stored on chests - never rename an id,\n#                                           add an alias line instead\n#   alias    | <old id> | <new id>          chests tagged with <old id> are treated as <new id>\n#   itemtype | <ItemType> | <category id>   fallback for an item type when no rule below matches\n#   override | <scope> | <prefab> | <id>    exact prefab (internal) name\n#   prefix   | <scope> | <text> | <id>      prefab name starts with text\n#   endswith | <scope> | <text> | <id>      prefab name ends with text\n#   contains | <scope> | <text> | <id>      prefab name contains text\n#   suffix   | <scope> | <word> | <id>      LAST PascalCase word of the prefab name equals word\n#   foodcolor | <color> | <id>              food by its icon colour in the game: Health (red),\n#                                           Stamina (yellow), Eitr (blue), Balanced (white)\n#   parent   | <id> | <parent id>           a chest tagged with the parent also takes this category;\n#                                           Push prefers exact-category chests and uses parent-tagged\n#                                           ones as overflow\n# <scope> is an item type (Material, Consumable, Misc, ...) or * for any.\n# Matching order: override, prefix, endswith, contains, suffix, foodcolor, itemtype.\n# Unmatched Material items land in Misc; unmatched items of other types use their item type name.\n\nversion | 4\n\ncategory | Trophy | Trophies\ncategory | Ammo | Ammo\ncategory | SiegeAmmo | Siege Ammo\ncategory | Chest | Armor: Chest\ncategory | Helmet | Armor: Helmets\ncategory | Legs | Armor: Legs\ncategory | Shoulder | Capes\ncategory | Shield | Shields\ncategory | OneHandedWeapon | One-Handed Weapons\ncategory | TwoHandedWeapon | Two-Handed Weapons\ncategory | TwoHandedWeaponLeft | Two-Handed (Left Hand)\ncategory | Bow | Bows & Crossbows\ncategory | Tool | Tools\ncategory | Utility | Utility\ncategory | Torch | Torches & Lanterns\ncategory | Trinket | Trinkets\ncategory | Fish | Fish\ncategory | Food | Food (all kinds)\ncategory | Produce | Food: Produce\ncategory | RawMeat | Food: Raw Meat\ncategory | CookingIngredients | Food: Cooking Ingredients\ncategory | CookedMeat | Food: Cooked Meat\ncategory | MealsHealth | Food: Meals (Health)\ncategory | MealsStamina | Food: Meals (Stamina)\ncategory | MealsEitr | Food: Meals (Eitr)\ncategory | MealsBalanced | Food: Meals (Balanced)\ncategory | Feasts | Food: Feasts\ncategory | Potions | Meads & Potions\ncategory | Customization | Cosmetics\ncategory | HidesAndFur | Hides & Fur\ncategory | BonesAndSkeleton | Bones\ncategory | Organs | Organs\ncategory | OresAndMetals | Ores & Metals\ncategory | BuildingStone | Building Stone\ncategory | Minerals | Minerals\ncategory | Wood | Wood\ncategory | PlantsAndSeeds | Plants & Seeds\ncategory | Textiles | Textiles\ncategory | Feathers | Feathers\ncategory | ResinAndTar | Resin & Tar\ncategory | ChitinAndScales | Chitin & Scales\ncategory | RareCores | Cores & Essences\ncategory | Valuables | Valuables\ncategory | QuestAndBoss | Quest & Boss Items\ncategory | Taming | Taming & Saddles\ncategory | CraftingParts | Crafting Parts\ncategory | MeadBases | Mead Bases\ncategory | UpgradeIdols | Upgrade Idols\ncategory | Spices | Spices\ncategory | Fireworks | Fireworks\ncategory | CastsAndMoulds | Casts & Moulds\ncategory | Misc | Misc\n\n# Old ids from earlier builds.\nalias | StoneAndGems | BuildingStone\nalias | Currency | Valuables\nalias | RawDishes | CookingIngredients\nalias | Consumable | Food\nalias | AmmoNonEquipable | SiegeAmmo\nalias | FeastIngredients | Feasts\n\n# --- Food family: a chest tagged \"Food\" takes all of these.\nparent | Produce | Food\nparent | RawMeat | Food\nparent | CookingIngredients | Food\nparent | CookedMeat | Food\nparent | MealsHealth | Food\nparent | MealsStamina | Food\nparent | MealsEitr | Food\nparent | MealsBalanced | Food\nparent | Feasts | Food\n\n# Prepared food not matched by a name rule below, by its icon colour.\nfoodcolor | Health | MealsHealth\nfoodcolor | Stamina | MealsStamina\nfoodcolor | Eitr | MealsEitr\nfoodcolor | Balanced | MealsBalanced\n\nitemtype | Consumable | Food\nitemtype | AmmoNonEquipable | SiegeAmmo\nitemtype | Material | Misc\nitemtype | Misc | Misc\n\n# --- Exact exceptions: merged-lowercase names the suffix rule can't split\n# (Leatherstraps, Bloodbag...), modifiers after the material word\n# (FlametalNew, OrbFrostFire...), and deliberate regroupings.\noverride | * | BoneFragments | BonesAndSkeleton\noverride | * | Charredskull | BonesAndSkeleton\noverride | * | NeckTail | BonesAndSkeleton\noverride | * | BonemawSerpentScale | ChitinAndScales\noverride | * | Leatherstraps | HidesAndFur\noverride | * | ElakingHairBundle | HidesAndFur\noverride | * | WolfHairBundle | HidesAndFur\noverride | * | Bilebag | Organs\noverride | * | Bloodbag | Organs\noverride | * | Softtissue | Organs\noverride | * | CuredSquirrelHamstring | Organs\noverride | * | Ironpit | OresAndMetals\noverride | * | CharredCogwheel | OresAndMetals\noverride | * | FlametalNew | OresAndMetals\noverride | * | FlametalOreNew | OresAndMetals\noverride | * | Grausten | BuildingStone\noverride | * | PungentPebbles | BuildingStone\noverride | * | Thunderstone | Minerals\noverride | * | SulfurStone | Minerals\noverride | * | SharpeningStone | CraftingParts\noverride | * | CandleWick | CraftingParts\noverride | * | Hook | CraftingParts\noverride | * | BarrelRings | CraftingParts\noverride | * | ScytheHandle | CraftingParts\noverride | * | Blackwood | Wood\noverride | * | Frostwood | Wood\noverride | * | FirConeFrost | PlantsAndSeeds\noverride | * | FragrantBundle | PlantsAndSeeds\noverride | * | Feathers | Feathers\noverride | * | CelestialFeather | Feathers\noverride | * | BreadDough | CookingIngredients\noverride | * | BarleyFlour | CookingIngredients\noverride | * | OatFlour | CookingIngredients\noverride | * | ChickenEgg | Produce\n# Edible as-is, but really crops, forage and cooking ingredients.\noverride | Consumable | Blueberries | Produce\noverride | Consumable | Raspberry | Produce\noverride | Consumable | Cloudberry | Produce\noverride | Consumable | Lingonberry | Produce\noverride | Consumable | Vineberry | Produce\noverride | Consumable | Pukeberries | Produce\noverride | Consumable | Carrot | Produce\noverride | Consumable | Onion | Produce\noverride | Consumable | Kale | Produce\noverride | Consumable | Oat | Produce\noverride | Consumable | Poteitr | Produce\noverride | Consumable | Honey | Produce\noverride | Consumable | RoyalJelly | Produce\noverride | Consumable | Fiddleheadfern | Produce\noverride | Consumable | GlowWorm | Produce\noverride | Consumable | Mushroom | Produce\noverride | Consumable | MushroomBlue | Produce\noverride | Consumable | MushroomBzerker | Produce\noverride | Consumable | MushroomJotunPuffs | Produce\noverride | Consumable | MushroomMagecap | Produce\noverride | Consumable | MushroomSmokePuff | Produce\noverride | Consumable | MushroomYellow | Produce\noverride | Consumable | NeckTailGrilled | CookedMeat\noverride | Consumable | RottenMeat | RawMeat\noverride | * | BarleyWineBase | MeadBases\noverride | * | BarleyWine | Potions\noverride | * | OozeMork | ResinAndTar\noverride | * | ShieldCore | RareCores\noverride | * | DvergrNeedle | RareCores\noverride | * | BlobVial | RareCores\noverride | * | Ectoplasm | RareCores\noverride | * | Voidplasm | RareCores\noverride | * | Wisp | RareCores\noverride | * | FaderEmber | RareCores\noverride | * | OrbFrostFire | RareCores\noverride | * | OrbThunderBlood | RareCores\noverride | * | PowderedDragonEgg | RareCores\noverride | * | HardAntler | QuestAndBoss\noverride | * | QueenDrop | QuestAndBoss\noverride | * | FaderDrop | QuestAndBoss\noverride | * | FrozenKingDrop | QuestAndBoss\noverride | * | DragonTear | QuestAndBoss\noverride | * | YagluthDrop | QuestAndBoss\noverride | * | DragonEgg | QuestAndBoss\noverride | * | AxeHead1 | QuestAndBoss\noverride | * | AxeHead2 | QuestAndBoss\noverride | * | Pot_Shard_Green | QuestAndBoss\noverride | * | Pot_Shard_Red | QuestAndBoss\noverride | * | CryptKey | QuestAndBoss\noverride | * | DvergrKey | QuestAndBoss\noverride | * | BloodGoldKey | QuestAndBoss\noverride | * | HildirKey_forestcrypt | QuestAndBoss\noverride | * | HildirKey_mountaincave | QuestAndBoss\noverride | * | HildirKey_plainsfortress | QuestAndBoss\noverride | * | chest_hildir1 | QuestAndBoss\noverride | * | chest_hildir2 | QuestAndBoss\noverride | * | chest_hildir3 | QuestAndBoss\noverride | * | GoblinTotem | QuestAndBoss\noverride | * | HatefulBlood | QuestAndBoss\noverride | * | Bell | QuestAndBoss\noverride | * | AncientSeed | QuestAndBoss\noverride | * | AsksvinEgg | Taming\noverride | * | VoltureEgg | Taming\noverride | * | SaddleLox | Taming\noverride | * | SaddleAsksvin | Taming\noverride | * | SaddleMoose | Taming\noverride | * | BombSiege | SiegeAmmo\noverride | * | Catapult_ammo | SiegeAmmo\noverride | * | Catapult_Ammo_BloodGold | SiegeAmmo\n\n# --- Name families where the type word is at the front.\nprefix | Material | MeadBase | MeadBases\nprefix | Material | Upgrader | UpgradeIdols\nprefix | Material | Spice | Spices\nprefix | Material | FireworksRocket_ | Fireworks\nprefix | Material | Mold | CastsAndMoulds\nprefix | Material | AncientGemstone | Valuables\nprefix | Material | Gemstone | Valuables\nprefix | Material | Jute | Textiles\nprefix | Material | Feast | Feasts\nprefix | Consumable | Feast | Feasts\nprefix | Consumable | Mead | Potions\nprefix | Consumable | Cooked | CookedMeat\n# \"Cast: ...\" armor and staff moulds are named *Uncooked like food - must win over the Uncooked rule.\nprefix | Material | ArmorGold | CastsAndMoulds\nprefix | Material | Staff | CastsAndMoulds\n\n# \"Cast: ...\" items are named *GoldUncooked - must win over the generic Uncooked rule below.\nendswith | Material | GoldUncooked | CastsAndMoulds\nendswith | Material | MoldUncooked | CastsAndMoulds\nendswith | Consumable | Cooked | CookedMeat\ncontains | Material | Uncooked | CookingIngredients\n\n# --- Last-word rules for Material items.\nsuffix | Material | Hide | HidesAndFur\nsuffix | Material | Pelt | HidesAndFur\nsuffix | Material | Leather | HidesAndFur\nsuffix | Material | Straps | HidesAndFur\nsuffix | Material | Scraps | HidesAndFur\nsuffix | Material | Meat | RawMeat\nsuffix | Material | Blubber | RawMeat\nsuffix | Material | Raw | RawMeat\nsuffix | Material | Bone | BonesAndSkeleton\nsuffix | Material | Bones | BonesAndSkeleton\nsuffix | Material | Skull | BonesAndSkeleton\nsuffix | Material | Ribcage | BonesAndSkeleton\nsuffix | Material | Pelvic | BonesAndSkeleton\nsuffix | Material | Neck | BonesAndSkeleton\nsuffix | Material | Tooth | BonesAndSkeleton\nsuffix | Material | Fang | BonesAndSkeleton\nsuffix | Material | Claw | BonesAndSkeleton\nsuffix | Material | Claws | BonesAndSkeleton\nsuffix | Material | Paw | BonesAndSkeleton\nsuffix | Material | Antler | BonesAndSkeleton\nsuffix | Material | Mandible | BonesAndSkeleton\nsuffix | Material | Bladder | Organs\nsuffix | Material | Bag | Organs\nsuffix | Material | Entrails | Organs\nsuffix | Material | Sinew | Organs\nsuffix | Material | Heart | Organs\nsuffix | Material | Tissue | Organs\nsuffix | Material | Sack | Organs\nsuffix | Material | Eye | Organs\nsuffix | Material | Gland | Organs\nsuffix | Material | Ore | OresAndMetals\nsuffix | Material | Scrap | OresAndMetals\nsuffix | Material | Nails | OresAndMetals\nsuffix | Material | Copper | OresAndMetals\nsuffix | Material | Bronze | OresAndMetals\nsuffix | Material | Iron | OresAndMetals\nsuffix | Material | Silver | OresAndMetals\nsuffix | Material | Tin | OresAndMetals\nsuffix | Material | Gold | OresAndMetals\nsuffix | Material | Flametal | OresAndMetals\nsuffix | Material | Metal | OresAndMetals\nsuffix | Material | Chain | OresAndMetals\nsuffix | Material | Spring | OresAndMetals\nsuffix | Material | Stone | BuildingStone\nsuffix | Material | Rock | BuildingStone\nsuffix | Material | Marble | BuildingStone\nsuffix | Material | Flint | Minerals\nsuffix | Material | Crystal | Minerals\nsuffix | Material | Coal | Minerals\nsuffix | Material | Obsidian | Minerals\nsuffix | Material | Powder | Minerals\nsuffix | Material | Plate | CraftingParts\nsuffix | Material | Wood | Wood\nsuffix | Material | Log | Wood\nsuffix | Material | Bark | Wood\nsuffix | Material | Branch | Wood\nsuffix | Material | Seeds | PlantsAndSeeds\nsuffix | Material | Flax | PlantsAndSeeds\nsuffix | Material | Barley | Produce\nsuffix | Material | Dandelion | PlantsAndSeeds\nsuffix | Material | Thistle | PlantsAndSeeds\nsuffix | Material | Acorn | PlantsAndSeeds\nsuffix | Material | Acorns | PlantsAndSeeds\nsuffix | Material | Cone | PlantsAndSeeds\nsuffix | Material | Root | PlantsAndSeeds\nsuffix | Material | Roots | PlantsAndSeeds\nsuffix | Material | Turnip | Produce\nsuffix | Material | Seaweed | PlantsAndSeeds\nsuffix | Material | Thread | Textiles\nsuffix | Material | Resin | ResinAndTar\nsuffix | Material | Sap | ResinAndTar\nsuffix | Material | Tar | ResinAndTar\nsuffix | Material | Ooze | ResinAndTar\nsuffix | Material | Pulp | ResinAndTar\nsuffix | Material | Guck | ResinAndTar\nsuffix | Material | Ice | ResinAndTar\nsuffix | Material | Fuel | ResinAndTar\nsuffix | Material | Carapace | ChitinAndScales\nsuffix | Material | Chitin | ChitinAndScales\nsuffix | Material | Scale | ChitinAndScales\nsuffix | Material | Needle | ChitinAndScales\nsuffix | Material | Bee | Taming\nsuffix | Material | Core | RareCores\nsuffix | Material | Eitr | RareCores\nsuffix | Material | Remains | RareCores\nsuffix | Material | Orb | RareCores\nsuffix | Material | Fragment | QuestAndBoss\nsuffix | Material | Coin | Valuables\nsuffix | Material | Coins | Valuables\nsuffix | Material | Necklace | Valuables\nsuffix | Material | Amber | Valuables\nsuffix | Material | Pearl | Valuables\nsuffix | Material | Pearls | Valuables\nsuffix | Material | Ruby | Valuables\nsuffix | Material | Jewel | Valuables\n";
			try
			{
				if (_path != null && File.Exists(_path))
				{
					text = File.ReadAllText(_path);
					_loadedStampUtc = File.GetLastWriteTimeUtc(_path);
				}
			}
			catch (Exception ex)
			{
				list.Add("can't read file, using built-in rules: " + ex.Message);
			}
			Rules = CategoryRules.Parse(text, list);
			ChestSorterPlugin.WriteLog($"Category rules loaded: v{Rules.Version}, {Rules.Categories.Count} categories, {list.Count} problem(s).");
			foreach (string item in list)
			{
				ChestSorterPlugin.WriteLog("  category rules: " + item);
			}
		}
	}
	internal static class CategoryPanel
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Close;

			public static UnityAction <1>__SetFromContents;

			public static UnityAction <2>__ClearAll;

			public static Func<string, bool> <3>__IsItemTag;

			public static Func<string, string> <4>__TagDisplayName;

			public static Func<ItemData, string> <5>__GetCategory;
		}

		private const float Width = 300f;

		private const float Height = 480f;

		private const float Pad = 12f;

		private const float RowHeight = 30f;

		private static readonly Color SelectedColor = new Color(1f, 0.72f, 0.2f, 1f);

		private static readonly Color UnselectedColor = new Color(0.78f, 0.78f, 0.78f, 1f);

		private static GameObject _root;

		private static RectTransform _content;

		private static TMP_Text _contentsText;

		private static ScrollRect _scroll;

		private static Container _current;

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

		private static readonly List<(string id, TMP_Text label)> _rows = new List<(string, TMP_Text)>();

		internal static bool IsOpen
		{
			get
			{
				if ((Object)(object)_root != (Object)null)
				{
					return _root.activeSelf;
				}
				return false;
			}
		}

		internal static void Toggle(Container c)
		{
			if (IsOpen && (Object)(object)_current == (Object)(object)c)
			{
				Close();
			}
			else
			{
				Open(c);
			}
		}

		internal static void Close()
		{
			if ((Object)(object)_root != (Object)null)
			{
				_root.SetActive(false);
			}
		}

		internal static void Open(Container c)
		{
			if (!((Object)(object)c == (Object)null) && Ensure())
			{
				_current = c;
				_selected = ContainerCategoryTags.Get(c);
				UpdateContentsText();
				RebuildRows();
				_root.SetActive(true);
				_root.transform.SetAsLastSibling();
				_scroll.verticalNormalizedPosition = 1f;
			}
		}

		private static bool Ensure()
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Expected O, but got Unknown
			//IL_017c: 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_0187: Expected O, but got Unknown
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Expected O, but got Unknown
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Expected O, but got Unknown
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Expected O, but got Unknown
			if ((Object)(object)_root != (Object)null)
			{
				return true;
			}
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_container == (Object)null || (Object)(object)instance.m_stackAllButton == (Object)null || (Object)(object)instance.m_containerName == (Object)null)
			{
				return false;
			}
			_root = new GameObject("ChestSorter_CategoryPanel", new Type[1] { typeof(RectTransform) });
			RectTransform val = (RectTransform)_root.transform;
			((Transform)val).SetParent((Transform)(object)instance.m_container, false);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(1f, 1f);
			val.anchorMax = val2;
			val.anchorMin = val2;
			val.pivot = new Vector2(0f, 1f);
			val.sizeDelta = new Vector2(300f, 480f);
			val.anchoredPosition = new Vector2(ContainerButtons.ColumnRight + 12f, 0f);
			UiClone.IgnoreLayout(_root);
			UiClone.CopyBackground(instance.m_container, _root.AddComponent<Image>());
			TMP_Text obj = UiClone.Text(instance.m_containerName, _root.transform, "Title", "Categories");
			UiClone.Place(obj.rectTransform, 12f, 12f, 236f, 36f);
			obj.alignment = (TextAlignmentOptions)514;
			Button stackAllButton = instance.m_stackAllButton;
			Transform transform = _root.transform;
			object obj2 = <>O.<0>__Close;
			if (obj2 == null)
			{
				UnityAction val3 = Close;
				<>O.<0>__Close = val3;
				obj2 = (object)val3;
			}
			UiClone.Place((RectTransform)((Component)UiClone.Button(stackAllButton, transform, "Close", "X", (UnityAction)obj2)).transform, 254f, 14f, 34f, 34f);
			_contentsText = UiClone.Text(((Object)(object)instance.m_recipeDecription != (Object)null) ? instance.m_recipeDecription : instance.m_containerName, _root.transform, "Contents", "");
			UiClone.Place(_contentsText.rectTransform, 12f, 54f, 276f, 46f);
			_contentsText.alignment = (TextAlignmentOptions)257;
			_contentsText.enableAutoSizing = true;
			_contentsText.fontSizeMin = 10f;
			_contentsText.fontSizeMax = 16f;
			_contentsText.textWrappingMode = (TextWrappingModes)1;
			Button stackAllButton2 = instance.m_stackAllButton;
			Transform transform2 = _root.transform;
			object obj3 = <>O.<1>__SetFromContents;
			if (obj3 == null)
			{
				UnityAction val4 = SetFromContents;
				<>O.<1>__SetFromContents = val4;
				obj3 = (object)val4;
			}
			UiClone.Place((RectTransform)((Component)UiClone.Button(stackAllButton2, transform2, "FromContents", "From contents", (UnityAction)obj3)).transform, 12f, 104f, 168f, 32f);
			Button stackAllButton3 = instance.m_stackAllButton;
			Transform transform3 = _root.transform;
			object obj4 = <>O.<2>__ClearAll;
			if (obj4 == null)
			{
				UnityAction val5 = ClearAll;
				<>O.<2>__ClearAll = val5;
				obj4 = (object)val5;
			}
			UiClone.Place((RectTransform)((Component)UiClone.Button(stackAllButton3, transform3, "Clear", "Clear", (UnityAction)obj4)).transform, 186f, 104f, 102f, 32f);
			_content = UiClone.ScrollArea(_root.transform, "List", 12f, 144f, 276f, 324f, instance.m_recipeListScroll, out _scroll);
			_root.SetActive(false);
			return true;
		}

		private static void RebuildRows()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			foreach (Transform item in (Transform)_content)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			_rows.Clear();
			CategoryRules rules = CategoryConfig.Rules;
			Button stackAllButton = InventoryGui.instance.m_stackAllButton;
			foreach (string item2 in _selected.Where(CategoryRules.IsItemTag).OrderBy<string, string>(ItemCategory.TagDisplayName, StringComparer.OrdinalIgnoreCase).ToList())
			{
				AddRow(stackAllButton, item2, "Item: " + ItemCategory.TagDisplayName(item2));
			}
			foreach (string item3 in _selected.Where((string t) => !CategoryRules.IsItemTag(t) && !rules.IsKnown(t)).ToList())
			{
				AddRow(stackAllButton, item3, item3 + " (not in rules file)");
			}
			foreach (var (id, display) in rules.Categories)
			{
				AddRow(stackAllButton, id, display);
			}
			RefreshRows();
		}

		private static void AddRow(Button template, string id, string display)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			Button obj = UiClone.Button(template, (Transform)(object)_content, "Row_" + id, display, (UnityAction)delegate
			{
				ToggleCategory(id);
			});
			LayoutElement component = ((Component)obj).GetComponent<LayoutElement>();
			component.ignoreLayout = false;
			float minHeight = (component.preferredHeight = 30f);
			component.minHeight = minHeight;
			TMP_Text val = UiClone.LabelOf(((Component)obj).gameObject);
			if ((Object)(object)val != (Object)null)
			{
				val.alignment = (TextAlignmentOptions)4097;
				val.margin = new Vector4(10f, 0f, 6f, 0f);
				val.enableAutoSizing = true;
				val.fontSizeMin = 10f;
				val.fontSizeMax = 18f;
			}
			_rows.Add((id, val));
		}

		private static void RefreshRows()
		{
			//IL_0041: 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)
			foreach (var (item, val) in _rows)
			{
				if (!((Object)(object)val == (Object)null))
				{
					bool flag = _selected.Contains(item);
					((Graphic)val).color = (flag ? SelectedColor : UnselectedColor);
					val.fontStyle = (FontStyles)(flag ? 1 : 0);
				}
			}
		}

		private static void ToggleCategory(string id)
		{
			if (!_selected.Remove(id))
			{
				_selected.Add(id);
			}
			Save();
		}

		private static void SetFromContents()
		{
			_selected = new HashSet<string>(from t in ContentsCategories()
				select t.id, StringComparer.OrdinalIgnoreCase);
			Save();
		}

		private static void ClearAll()
		{
			_selected.Clear();
			Save();
		}

		private static void Save()
		{
			if (!((Object)(object)_current == (Object)null))
			{
				ChestGroups.SetTags(_current, _selected);
				RefreshRows();
			}
		}

		private static List<(string id, int count)> ContentsCategories()
		{
			if (!((Object)(object)_current == (Object)null))
			{
				return (from g in (from i in _current.GetInventory().GetAllItems()
						where i.m_shared != null
						select i).GroupBy(ItemCategory.GetCategory)
					select (Key: g.Key, g.Sum((ItemData i) => i.m_stack)) into t
					orderby t.Item2 descending
					select t).ToList();
			}
			return new List<(string, int)>();
		}

		private static void UpdateContentsText()
		{
			List<(string, int)> list = ContentsCategories();
			_contentsText.text = ((list.Count == 0) ? "This chest is empty." : ("Contents: " + string.Join(", ", from t in list.Take(6)
				select $"{CategoryConfig.Rules.DisplayName(t.id)} ({t.count})") + ((list.Count > 6) ? ", ..." : "")));
		}
	}
	internal sealed class CategoryRules
	{
		private sealed class Rule
		{
			public string Scope;

			public string Pattern;

			public string Category;
		}

		private static readonly Regex WordSplitter = new Regex("[A-Z][a-z0-9]*|[a-z0-9]+", RegexOptions.Compiled);

		private readonly List<(string id, string display)> _categories = new List<(string, string)>();

		private readonly Dictionary<string, string> _display = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, string> _aliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, string> _itemTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, string> _foodColors = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, string> _parents = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private readonly List<Rule> _overrides = new List<Rule>();

		private readonly List<Rule> _prefixes = new List<Rule>();

		private readonly List<Rule> _endsWith = new List<Rule>();

		private readonly List<Rule> _contains = new List<Rule>();

		private readonly List<Rule> _suffixes = new List<Rule>();

		private readonly Dictionary<string, string> _cache = new Dictionary<string, string>();

		internal const string ItemTagPrefix = "item:";

		internal const int FitItem = 3;

		internal const int FitCategory = 2;

		internal const int FitParent = 1;

		internal int Version { get; private set; }

		internal IReadOnlyList<(string id, string display)> Categories => _categories;

		internal static CategoryRules Parse(string text, List<string> errors)
		{
			CategoryRules categoryRules = new CategoryRules();
			int num = 0;
			string[] array = text.Split(new char[1] { '\n' });
			foreach (string text2 in array)
			{
				num++;
				string text3 = text2;
				int num2 = text3.IndexOf('#');
				if (num2 >= 0)
				{
					text3 = text3.Substring(0, num2);
				}
				text3 = text3.Trim();
				if (text3.Length == 0)
				{
					continue;
				}
				string[] array2 = (from s in text3.Split(new char[1] { '|' })
					select s.Trim()).ToArray();
				string text4 = array2[0].ToLowerInvariant();
				bool flag = true;
				switch (text4)
				{
				case "version":
				{
					if (array2.Length == 2 && int.TryParse(array2[1], out var result))
					{
						categoryRules.Version = result;
						break;
					}
					goto default;
				}
				case "category":
					if (array2.Length == 3)
					{
						categoryRules.AddCategory(array2[1], array2[2]);
						break;
					}
					goto default;
				case "alias":
					if (array2.Length == 3)
					{
						categoryRules._aliases[array2[1]] = array2[2];
						break;
					}
					goto default;
				case "itemtype":
					if (array2.Length == 3)
					{
						categoryRules._itemTypes[array2[1]] = array2[2];
						break;
					}
					goto default;
				case "override":
					if (array2.Length == 4)
					{
						categoryRules._overrides.Add(new Rule
						{
							Scope = array2[1],
							Pattern = array2[2],
							Category = array2[3]
						});
						break;
					}
					goto default;
				case "prefix":
					if (array2.Length == 4)
					{
						categoryRules._prefixes.Add(new Rule
						{
							Scope = array2[1],
							Pattern = array2[2],
							Category = array2[3]
						});
						break;
					}
					goto default;
				case "endswith":
					if (array2.Length == 4)
					{
						categoryRules._endsWith.Add(new Rule
						{
							Scope = array2[1],
							Pattern = array2[2],
							Category = array2[3]
						});
						break;
					}
					goto default;
				case "contains":
					if (array2.Length == 4)
					{
						categoryRules._contains.Add(new Rule
						{
							Scope = array2[1],
							Pattern = array2[2],
							Category = array2[3]
						});
						break;
					}
					goto default;
				case "suffix":
					if (array2.Length == 4)
					{
						categoryRules._suffixes.Add(new Rule
						{
							Scope = array2[1],
							Pattern = array2[2],
							Category = array2[3]
						});
						break;
					}
					goto default;
				case "foodcolor":
					if (array2.Length == 3)
					{
						categoryRules._foodColors[array2[1]] = array2[2];
						break;
					}
					goto default;
				case "parent":
					if (array2.Length == 3)
					{
						categoryRules._parents[array2[1]] = array2[2];
						break;
					}
					goto default;
				default:
					flag = false;
					break;
				}
				if (!flag)
				{
					errors.Add($"line {num}: can't parse \"{text2.Trim()}\"");
				}
			}
			foreach (string item in (from x in categoryRules._overrides.Concat(categoryRules._prefixes).Concat(categoryRules._endsWith).Concat(categoryRules._contains)
					.Concat(categoryRules._suffixes)
				select x.Category).Concat(categoryRules._itemTypes.Values).Concat(categoryRules._aliases.Values).Concat(categoryRules._foodColors.Values)
				.Concat(categoryRules._parents.Keys)
				.Concat(categoryRules._parents.Values)
				.Distinct<string>(StringComparer.OrdinalIgnoreCase))
			{
				if (!categoryRules._display.ContainsKey(item))
				{
					errors.Add("category id \"" + item + "\" is used by a rule but never declared with a \"category\" line");
				}
			}
			return categoryRules;
		}

		private void AddCategory(string id, string display)
		{
			if (!_display.ContainsKey(id))
			{
				_categories.Add((id, display));
				_display[id] = display;
			}
		}

		internal bool IsKnown(string id)
		{
			return _display.ContainsKey(Resolve(id));
		}

		internal static bool IsItemTag(string tag)
		{
			return tag?.StartsWith("item:", StringComparison.OrdinalIgnoreCase) ?? false;
		}

		internal string Resolve(string id)
		{
			for (int i = 0; i < 8; i++)
			{
				if (id == null)
				{
					break;
				}
				if (!_aliases.TryGetValue(id, out var value))
				{
					break;
				}
				id = value;
			}
			return id;
		}

		internal string DisplayName(string id)
		{
			id = Resolve(id);
			if (id == null || !_display.TryGetValue(id, out var value))
			{
				return id;
			}
			return value;
		}

		internal int Fit(ICollection<string> tags, string category, string itemName = null)
		{
			if (tags.Count == 0)
			{
				return 0;
			}
			if (itemName != null && tags.Contains("item:" + itemName))
			{
				return 3;
			}
			if (category == null)
			{
				return 0;
			}
			if (tags.Contains(category))
			{
				return 2;
			}
			string key = category;
			for (int i = 0; i < 8; i++)
			{
				if (!_parents.TryGetValue(key, out var value))
				{
					break;
				}
				value = Resolve(value);
				if (tags.Contains(value))
				{
					return 1;
				}
				key = value;
			}
			return 0;
		}

		internal string ParentOf(string id)
		{
			if (!_parents.TryGetValue(Resolve(id), out var value))
			{
				return null;
			}
			return Resolve(value);
		}

		internal string GetCategory(string internalName, string itemType, string foodColor = null)
		{
			string key = itemType + "|" + foodColor + "|" + internalName;
			if (_cache.TryGetValue(key, out var value))
			{
				return value;
			}
			string text = Resolve(Match(internalName ?? "", itemType ?? "", foodColor));
			_cache[key] = text;
			return text;
		}

		private string Match(string name, string itemType, string foodColor)
		{
			foreach (Rule @override in _overrides)
			{
				if (InScope(@override, itemType) && string.Equals(name, @override.Pattern, StringComparison.OrdinalIgnoreCase))
				{
					return @override.Category;
				}
			}
			foreach (Rule prefix in _prefixes)
			{
				if (InScope(prefix, itemType) && name.StartsWith(prefix.Pattern, StringComparison.OrdinalIgnoreCase))
				{
					return prefix.Category;
				}
			}
			foreach (Rule item in _endsWith)
			{
				if (InScope(item, itemType) && name.EndsWith(item.Pattern, StringComparison.OrdinalIgnoreCase))
				{
					return item.Category;
				}
			}
			foreach (Rule contain in _contains)
			{
				if (InScope(contain, itemType) && name.IndexOf(contain.Pattern, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return contain.Category;
				}
			}
			MatchCollection matchCollection = WordSplitter.Matches(name);
			string a = ((matchCollection.Count > 0) ? matchCollection[matchCollection.Count - 1].Value : name);
			foreach (Rule suffix in _suffixes)
			{
				if (InScope(suffix, itemType) && string.Equals(a, suffix.Pattern, StringComparison.OrdinalIgnoreCase))
				{
					return suffix.Category;
				}
			}
			if (foodColor != null && _foodColors.TryGetValue(foodColor, out var value))
			{
				return value;
			}
			if (_itemTypes.TryGetValue(itemType, out var value2))
			{
				return value2;
			}
			if (!string.Equals(itemType, "Material", StringComparison.OrdinalIgnoreCase))
			{
				return itemType;
			}
			return "Misc";
		}

		private static bool InScope(Rule rule, string itemType)
		{
			if (!(rule.Scope == "*"))
			{
				return string.Equals(rule.Scope, itemType, StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}
	}
	internal static class ChestGroups
	{
		private const string ZdoKey = "ChestSorter_Group";

		private const float SameColumnTolerance = 0.6f;

		private const float MaxGapBelow = 4f;

		private const float MemberSearchRadius = 30f;

		internal static string GroupId(Container c)
		{
			ZDO val = GameUtil.Zdo(c);
			if (val == null)
			{
				return "";
			}
			return val.GetString("ChestSorter_Group", "");
		}

		internal static bool InGroup(Container c)
		{
			if ((Object)(object)c != (Object)null)
			{
				return GroupId(c).Length > 0;
			}
			return false;
		}

		private static void SetGroupId(Container c, string id)
		{
			ZDO val = GameUtil.Zdo(c);
			if (val != null)
			{
				GameUtil.ClaimOwnership(c);
				val.Set("ChestSorter_Group", id);
			}
		}

		internal static List<Container> Members(Container c)
		{
			//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)
			string id = GroupId(c);
			if (id.Length == 0)
			{
				return new List<Container> { c };
			}
			Vector3 p = ((Component)c).transform.position;
			return (from x in Object.FindObjectsByType<Container>((FindObjectsSortMode)0)
				where (Object)(object)x != (Object)null && Vector3.Distance(((Component)x).transform.position, p) <= 30f && GroupId(x) == id
				orderby ((Component)x).transform.position.y
				select x).ToList();
		}

		private static Container FindBelow(Container c)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Vector3 p = ((Component)c).transform.position;
			return (from x in Object.FindObjectsByType<Container>((FindObjectsSortMode)0)
				where (Object)(object)x != (Object)null && (Object)(object)x != (Object)(object)c && !Eligibility.IsMovable(x) && Eligibility.Writable(x, c)
				select (chest: x, dxz: Vector2.Distance(new Vector2(((Component)x).transform.position.x, ((Component)x).transform.position.z), new Vector2(p.x, p.z)), dy: p.y - ((Component)x).transform.position.y) into t
				where t.dxz <= 0.6f && t.dy > 0.05f && t.dy <= 4f
				orderby t.dy
				select t.chest).FirstOrDefault();
		}

		internal static string JoinBelow(Container c)
		{
			if (InGroup(c))
			{
				return "This chest is already in a group.";
			}
			Container val = FindBelow(c);
			if ((Object)(object)val == (Object)null)
			{
				return "There's no chest directly below this one.";
			}
			string text = GroupId(val);
			if (text.Length == 0)
			{
				text = Guid.NewGuid().ToString("N");
				SetGroupId(val, text);
			}
			SetGroupId(c, text);
			HashSet<string> hashSet = ContainerCategoryTags.Get(val);
			if (hashSet.Count == 0)
			{
				hashSet = ContainerCategoryTags.Get(c);
			}
			SetTags(c, hashSet);
			int count = Members(c).Count;
			ChestSorterPlugin.WriteLog($"Joined {GameUtil.Where(c)} to the group of {GameUtil.Where(val)} ({count} chests).");
			return $"Joined the group ({count} chests).";
		}

		internal static string Leave(Container c)
		{
			if (!InGroup(c))
			{
				return "This chest is not in a group.";
			}
			SetGroupId(c, "");
			ChestSorterPlugin.WriteLog(GameUtil.Where(c) + " left its group.");
			return "Left the group (categories kept).";
		}

		internal static void SetTags(Container c, IEnumerable<string> tags)
		{
			List<string> categories = tags.ToList();
			foreach (Container item in Members(c))
			{
				ContainerCategoryTags.Set(item, categories);
				ChestLabels.Refresh(item);
			}
		}
	}
	internal static class ChestLabels
	{
		private sealed class Entry
		{
			public GameObject Root;

			public string Text;
		}

		private sealed class Seen
		{
			public string Raw;

			public bool Movable;
		}

		private const string RootName = "ChestSorter_Label";

		private static readonly Dictionary<Container, Entry> _labels = new Dictionary<Container, Entry>();

		private static readonly Dictionary<Container, Seen> _seen = new Dictionary<Container, Seen>();

		private static Dictionary<string, int> _order;

		private static CategoryRules _orderRules;

		private static float _nextScan;

		private static bool _labelsShown;

		private static bool _itemIndexDirty;

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

		private static TMP_FontAsset _font;

		private static Material _material;

		internal static bool HasItemChest(string itemTag)
		{
			return _itemChests.Contains(itemTag);
		}

		internal static void Tick()
		{
			bool value = ChestSorterPlugin.ShowLabels.Value;
			if (value != _labelsShown)
			{
				_labelsShown = value;
				if (!value)
				{
					DestroyLabels();
				}
				foreach (Seen value3 in _seen.Values)
				{
					value3.Raw = "\0";
				}
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null || Time.time < _nextScan)
			{
				return;
			}
			_nextScan = Time.time + 1f;
			foreach (Container item in (from kv in _labels
				where (Object)(object)kv.Key == (Object)null || (Object)(object)kv.Value.Root == (Object)null
				select kv.Key).ToList())
			{
				if ((Object)(object)_labels[item].Root != (Object)null)
				{
					Object.Destroy((Object)(object)_labels[item].Root);
				}
				_labels.Remove(item);
			}
			foreach (Container item2 in _seen.Keys.Where((Container k) => (Object)(object)k == (Object)null).ToList())
			{
				_seen.Remove(item2);
				_itemIndexDirty = true;
			}
			if (_orderRules != null && _orderRules != CategoryConfig.Rules)
			{
				foreach (Seen value4 in _seen.Values)
				{
					value4.Raw = "\0";
				}
			}
			Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
			foreach (Container val in array)
			{
				ZDO val2 = GameUtil.Zdo(val);
				string text = ((val2 != null) ? val2.GetString("ChestSorter_Cats", "") : null);
				if (!_seen.TryGetValue(val, out var value2))
				{
					value2 = new Seen
					{
						Raw = "\0",
						Movable = Eligibility.IsMovable(val)
					};
					_seen[val] = value2;
				}
				if (!(value2.Raw == text))
				{
					value2.Raw = text;
					_itemIndexDirty = true;
					if (!value2.Movable && value)
					{
						Apply(val);
					}
				}
			}
			if (_itemIndexDirty)
			{
				RebuildItemIndex();
			}
		}

		private static void RebuildItemIndex()
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Invalid comparison between Unknown and I4
			_itemIndexDirty = false;
			_itemChests.Clear();
			foreach (KeyValuePair<Container, Seen> item in _seen)
			{
				string raw = item.Value.Raw;
				if ((Object)(object)item.Key == (Object)null || item.Value.Movable || string.IsNullOrEmpty(raw) || raw.IndexOf("item:", StringComparison.OrdinalIgnoreCase) < 0 || (int)item.Key.m_privacy != 2)
				{
					continue;
				}
				string[] array = raw.Split(new char[1] { ',' });
				foreach (string text in array)
				{
					if (CategoryRules.IsItemTag(text))
					{
						_itemChests.Add(text);
					}
				}
			}
		}

		internal static void Refresh(Container c)
		{
			if (!((Object)(object)c == (Object)null))
			{
				if (_seen.TryGetValue(c, out var value))
				{
					value.Raw = "\0";
				}
				if (_labelsShown)
				{
					Apply(c);
				}
			}
		}

		private static void Apply(Container c)
		{
			string text = LabelText(c);
			_labels.TryGetValue(c, out var value);
			if (string.IsNullOrEmpty(text))
			{
				if (value != null)
				{
					if ((Object)(object)value.Root != (Object)null)
					{
						Object.Destroy((Object)(object)value.Root);
					}
					_labels.Remove(c);
				}
			}
			else if (value == null || !((Object)(object)value.Root != (Object)null) || !(value.Text == text))
			{
				if ((Object)(object)value?.Root != (Object)null)
				{
					Object.Destroy((Object)(object)value.Root);
				}
				_labels[c] = new Entry
				{
					Root = Build(c, text),
					Text = text
				};
			}
		}

		internal static void Clear()
		{
			DestroyLabels();
			_seen.Clear();
			_itemChests.Clear();
		}

		private static void DestroyLabels()
		{
			foreach (Entry value in _labels.Values)
			{
				if ((Object)(object)value.Root != (Object)null)
				{
					Object.Destroy((Object)(object)value.Root);
				}
			}
			_labels.Clear();
		}

		private static string LabelText(Container c)
		{
			if (Eligibility.IsMovable(c) || GameUtil.Zdo(c) == null)
			{
				return null;
			}
			HashSet<string> hashSet = ContainerCategoryTags.Get(c);
			if (hashSet.Count == 0)
			{
				return null;
			}
			CategoryRules rules = CategoryConfig.Rules;
			if (_order == null || _orderRules != rules)
			{
				_order = rules.Categories.Select(((string id, string display) cat, int i) => (id: cat.id, i: i)).ToDictionary<(string, int), string, int>(((string id, int i) t) => t.id, ((string id, int i) t) => t.i, StringComparer.OrdinalIgnoreCase);
				_orderRules = rules;
			}
			int value;
			return string.Join("\n", from t in (from t in hashSet
					orderby (!CategoryRules.IsItemTag(t)) ? 1 : 0, (!_order.TryGetValue(t, out value)) ? int.MaxValue : value
					select t).ThenBy<string, string>((string t) => t, StringComparer.OrdinalIgnoreCase)
				select ItemCategory.TagDisplayName(t).ToUpperInvariant());
		}

		private static GameObject Build(Container c, string text)
		{
			//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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_005d: 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_0070: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: 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_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: 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_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: 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_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			Bounds val = LocalBounds(c);
			GameObject val2 = new GameObject("ChestSorter_Label");
			val2.transform.SetParent(((Component)c).transform, false);
			int layer = (val2.layer = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).gameObject.layer : ((Component)c).gameObject.layer));
			(Vector3, float, float)[] array = new(Vector3, float, float)[4]
			{
				(Vector3.forward, ((Bounds)(ref val)).extents.z, ((Bounds)(ref val)).size.x),
				(Vector3.back, ((Bounds)(ref val)).extents.z, ((Bounds)(ref val)).size.x),
				(Vector3.right, ((Bounds)(ref val)).extents.x, ((Bounds)(ref val)).size.z),
				(Vector3.left, ((Bounds)(ref val)).extents.x, ((Bounds)(ref val)).size.z)
			};
			for (int i = 0; i < array.Length; i++)
			{
				(Vector3, float, float) tuple = array[i];
				GameObject val3 = new GameObject("side")
				{
					layer = layer
				};
				val3.transform.SetParent(val2.transform, false);
				val3.transform.localPosition = ((Bounds)(ref val)).center + tuple.Item1 * (tuple.Item2 + 0.03f);
				val3.transform.localRotation = Quaternion.LookRotation(-tuple.Item1, Vector3.up);
				TextMeshPro val4 = val3.AddComponent<TextMeshPro>();
				TMP_FontAsset val5 = Font();
				if ((Object)(object)val5 != (Object)null)
				{
					((TMP_Text)val4).font = val5;
				}
				Material val6 = SharedMaterial(val5);
				if (val6 != null)
				{
					((TMP_Text)val4).fontSharedMaterial = val6;
				}
				((TMP_Text)val4).rectTransform.sizeDelta = new Vector2(Mathf.Max(0.3f, tuple.Item3 * 0.9f), Mathf.Max(0.2f, ((Bounds)(ref val)).size.y * 0.8f));
				((TMP_Text)val4).text = text;
				((TMP_Text)val4).enableAutoSizing = true;
				((TMP_Text)val4).fontSizeMin = 0.3f;
				((TMP_Text)val4).fontSizeMax = 2.5f;
				((TMP_Text)val4).alignment = (TextAlignmentOptions)514;
				((TMP_Text)val4).textWrappingMode = (TextWrappingModes)1;
				((Graphic)val4).color = new Color(1f, 0.55f, 0.1f, 1f);
			}
			return val2;
		}

		private static Bounds LocalBounds(Container c)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_007c: 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_008d: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			Bounds result = default(Bounds);
			MeshRenderer[] componentsInChildren = ((Component)c).GetComponentsInChildren<MeshRenderer>(false);
			foreach (MeshRenderer val in componentsInChildren)
			{
				if ((Object)(object)((Component)val).GetComponent<TMP_Text>() != (Object)null)
				{
					continue;
				}
				Bounds localBounds = ((Renderer)val).localBounds;
				for (int j = 0; j < 8; j++)
				{
					Vector3 val2 = ((Bounds)(ref localBounds)).center + Vector3.Scale(((Bounds)(ref localBounds)).extents, new Vector3((float)(((j & 1) != 0) ? 1 : (-1)), (float)(((j & 2) != 0) ? 1 : (-1)), (float)(((j & 4) != 0) ? 1 : (-1))));
					Vector3 val3 = ((Component)c).transform.InverseTransformPoint(((Component)val).transform.TransformPoint(val2));
					if (!flag)
					{
						((Bounds)(ref result))..ctor(val3, Vector3.zero);
						flag = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(val3);
					}
				}
			}
			if (!flag)
			{
				return new Bounds(new Vector3(0f, 0.5f, 0f), new Vector3(1.4f, 1f, 0.9f));
			}
			return result;
		}

		private static TMP_FontAsset Font()
		{
			if ((Object)(object)_font != (Object)null)
			{
				return _font;
			}
			TMP_FontAsset[] source = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			_font = ((IEnumerable<TMP_FontAsset>)source).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => ((Object)f).name.IndexOf("norse", StringComparison.OrdinalIgnoreCase) >= 0)) ?? ((IEnumerable<TMP_FontAsset>)source).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => ((Object)f).name.IndexOf("averia", StringComparison.OrdinalIgnoreCase) >= 0)) ?? source.FirstOrDefault();
			if ((Object)(object)_font != (Object)null)
			{
				ChestSorterPlugin.WriteLog("Chest labels use font \"" + ((Object)_font).name + "\".");
			}
			return _font;
		}

		private static Material SharedMaterial(TMP_FontAsset font)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_material != (Object)null)
			{
				return _material;
			}
			if ((Object)(object)font == (Object)null || (Object)(object)((TMP_Asset)font).material == (Object)null)
			{
				return null;
			}
			_material = new Material(((TMP_Asset)font).material);
			_material.SetFloat("_OutlineWidth", 0.08f);
			_material.SetColor("_OutlineColor", Color.black);
			_material.EnableKeyword("GLOW_ON");
			_material.SetColor("_GlowColor", new Color(1f, 0.65f, 0.15f, 1f));
			_material.SetFloat("_GlowOffset", 0f);
			_material.SetFloat("_GlowInner", 0.2f);
			_material.SetFloat("_GlowOuter", 0.6f);
			_material.SetFloat("_GlowPower", 0.8f);
			return _material;
		}
	}
	internal static class ContainerButtons
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OnPushPressed;

			public static UnityAction <1>__OnSortPressed;

			public static UnityAction <2>__OnCategoriesPressed;

			public static UnityAction <3>__OnOnlyThesePressed;

			public static UnityAction <4>__OnGroupPressed;

			public static UnityAction <5>__OnUndoPressed;
		}

		private const float Gap = 6f;

		private const float Offset = 12f;

		private const string JoinLabel = "Join below";

		private const string LeaveLabel = "Leave group";

		private static Button _push;

		private static Button _sort;

		private static Button _categories;

		private static Button _group;

		private static Button _undo;

		private static string _groupLabel;

		internal static float ColumnRight { get; private set; } = 180f;

		internal static void Ensure()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_006a: 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_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_0091: Expected O, but got Unknown
			//IL_00ce: 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_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_0100: 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: 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_0155: Expected O, but got Unknown
			//IL_017c: 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_0187: Expected O, but got Unknown
			InventoryGui instance = InventoryGui.instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_stackAllButton == (Object)null) && !((Object)(object)instance.m_container == (Object)null) && !((Object)(object)_push != (Object)null))
			{
				Rect rect = ((RectTransform)((Component)instance.m_stackAllButton).transform).rect;
				Vector2 size = ((Rect)(ref rect)).size;
				ColumnRight = 12f + size.x;
				object obj = <>O.<0>__OnPushPressed;
				if (obj == null)
				{
					UnityAction val = SortActions.OnPushPressed;
					<>O.<0>__OnPushPressed = val;
					obj = (object)val;
				}
				_push = Create(instance, size, "ChestSorter_Push", "Push", 0, (UnityAction)obj);
				object obj2 = <>O.<1>__OnSortPressed;
				if (obj2 == null)
				{
					UnityAction val2 = SortActions.OnSortPressed;
					<>O.<1>__OnSortPressed = val2;
					obj2 = (object)val2;
				}
				_sort = Create(instance, size, "ChestSorter_Sort", "Sort", 1, (UnityAction)obj2);
				object obj3 = <>O.<2>__OnCategoriesPressed;
				if (obj3 == null)
				{
					UnityAction val3 = SortActions.OnCategoriesPressed;
					<>O.<2>__OnCategoriesPressed = val3;
					obj3 = (object)val3;
				}
				_categories = Create(instance, size, "ChestSorter_Categories", "Categories", 2, (UnityAction)obj3);
				object obj4 = <>O.<3>__OnOnlyThesePressed;
				if (obj4 == null)
				{
					UnityAction val4 = SortActions.OnOnlyThesePressed;
					<>O.<3>__OnOnlyThesePressed = val4;
					obj4 = (object)val4;
				}
				Create(instance, size, "ChestSorter_OnlyThese", "Only these items", 3, (UnityAction)obj4);
				object obj5 = <>O.<4>__OnGroupPressed;
				if (obj5 == null)
				{
					UnityAction val5 = SortActions.OnGroupPressed;
					<>O.<4>__OnGroupPressed = val5;
					obj5 = (object)val5;
				}
				_group = Create(instance, size, "ChestSorter_Group", "Join below", 4, (UnityAction)obj5);
				object obj6 = <>O.<5>__OnUndoPressed;
				if (obj6 == null)
				{
					UnityAction val6 = SortActions.OnUndoPressed;
					<>O.<5>__OnUndoPressed = val6;
					obj6 = (object)val6;
				}
				_undo = Create(instance, size, "ChestSorter_Undo", "Undo", 5, (UnityAction)obj6);
				_groupLabel = "Join below";
			}
		}

		internal static void Refresh()
		{
			if ((Object)(object)_undo != (Object)null)
			{
				((Selectable)_undo).interactable = PushEngine.CanUndo;
			}
			if (!((Object)(object)_group == (Object)null) && !((Object)(object)InventoryGui.instance == (Object)null) && InventoryGui.instance.IsContainerOpen())
			{
				string text = (ChestGroups.InGroup(CurrentContainerPatch.Current) ? "Leave group" : "Join below");
				if (!(text == _groupLabel))
				{
					UiClone.SetLabel(((Component)_group).gameObject, text);
					_groupLabel = text;
				}
			}
		}

		private static Button Create(InventoryGui gui, Vector2 size, string name, string label, int index, UnityAction onClick)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055