Decompiled source of ChestCategories v1.2.0

despr0grammer.ChestCategories.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[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 despr0grammer.ChestCategories
{
	internal static class Actions
	{
		internal static Container HoveredStorage(Player player)
		{
			GameObject hoverObject = ((Humanoid)player).GetHoverObject();
			Container val = (((Object)(object)hoverObject != (Object)null) ? hoverObject.GetComponentInParent<Container>() : null);
			if (!((Object)(object)val != (Object)null) || !ChestRegistry.IsStorage(val))
			{
				return null;
			}
			return val;
		}

		internal static void OpenHud()
		{
			Categories.ReloadIfChanged();
			CategoryHud.Open(HoveredStorage(Player.m_localPlayer));
		}

		internal static string AssignChest(Container chest, Category category)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)chest == (Object)null || (Object)(object)localPlayer == (Object)null || !ChestRegistry.IsStorage(chest))
			{
				return Text.ChestGone;
			}
			if (!ChestRegistry.CanUse(chest, localPlayer.GetPlayerID()))
			{
				return Text.NoAccess;
			}
			if (ChestTag.InUse(chest))
			{
				return Text.ChestInUse;
			}
			if (!ChestTag.Set(chest, (category != null) ? category.Id : string.Empty))
			{
				return Text.NoAccess;
			}
			if (category == null)
			{
				return Text.ChestCleared;
			}
			return Text.ChestSet(category.DisplayName(Text.Spanish));
		}

		internal static void CycleHoveredChest(bool backwards)
		{
			Container val = HoveredStorage(Player.m_localPlayer);
			if ((Object)(object)val == (Object)null)
			{
				Notify(Text.LookAtChest);
				return;
			}
			Categories.ReloadIfChanged();
			IReadOnlyList<Category> all = Categories.All;
			if (all.Count == 0)
			{
				Notify(Text.NoCategoriesDefined);
				return;
			}
			int num = IndexOf(ChestTag.Get(val));
			int num2 = (backwards ? (num - 1) : (num + 1));
			if (num2 >= all.Count)
			{
				num2 = -1;
			}
			else if (num2 < -1)
			{
				num2 = all.Count - 1;
			}
			Notify(AssignChest(val, (num2 >= 0) ? all[num2] : null));
		}

		internal static void SortNearby()
		{
			Player localPlayer = Player.m_localPlayer;
			Stopwatch stopwatch = Stopwatch.StartNew();
			Sorter.Report report = Sorter.Run(localPlayer);
			stopwatch.Stop();
			if (!report.HadTargets)
			{
				Notify(Text.NoTaggedChests(Plugin.MenuKeyLabel), center: true);
			}
			else if (report.Moved == 0 && report.Stuck == 0)
			{
				Notify(Text.NothingToSort, center: true);
			}
			else
			{
				Notify(Text.Sorted(report.Moved, report.Stuck), center: true);
			}
			Plugin.Log.LogInfo((object)$"Sort: moved {report.Moved}, did not fit {report.Stuck}, took {stopwatch.ElapsedMilliseconds} ms");
		}

		internal static string OrderNearby()
		{
			Player localPlayer = Player.m_localPlayer;
			Stopwatch stopwatch = Stopwatch.StartNew();
			Orderer.Report report = Orderer.Run(localPlayer);
			stopwatch.Stop();
			string text = ((report.Skipped > 0) ? Text.OrderSkipped : ((report.Moved == 0) ? Text.AlreadyOrdered : Text.Ordered(report.Moved, report.Inventories)));
			Notify(text, center: true);
			Plugin.Log.LogInfo((object)$"Order: moved {report.Moved} in {report.Inventories} places, skipped {report.Skipped}, took {stopwatch.ElapsedMilliseconds} ms");
			return text;
		}

		internal static void Notify(string message, bool center = false)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Character)localPlayer).Message((MessageType)((!center) ? 1 : 2), message, 0, (Sprite)null, false);
			}
		}

		private static int IndexOf(string id)
		{
			IReadOnlyList<Category> all = Categories.All;
			for (int i = 0; i < all.Count; i++)
			{
				if (string.Equals(all[i].Id, id, StringComparison.OrdinalIgnoreCase))
				{
					return i;
				}
			}
			return -1;
		}
	}
	internal static class Categories
	{
		private static readonly List<Category> Loaded = new List<Category>();

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

		private static readonly Dictionary<SharedData, Category> Cache = new Dictionary<SharedData, Category>();

		private static Dictionary<string, string> _prefabBySharedName;

		private static string _path;

		private static DateTime _stamp;

		private static string _diskText;

		private static bool _readFailed;

		private static string _sharedMine;

		private static bool _sharedPending;

		private static float _sharedPublishedAt;

		private static bool _sharedSynced;

		private static bool _sharedFirst = true;

		private static bool _sharedWarned;

		private static bool _backedUpBeforeShared;

		private static long _sharedWorld;

		private static bool _sharedDirty;

		private static float _sharedDirtySince;

		private static int _sharedTooBigVersion = -1;

		private static int _lastLoadProblems;

		private const float MaxUnpublishedSeconds = 60f;

		internal static int Version { get; private set; }

		internal static IReadOnlyList<Category> All => Loaded;

		internal static string FilePath => _path;

		internal static bool SharedActive { get; private set; }

		internal static int SharedRevision => SharedCodec.RevisionOf(_sharedMine);

		internal static void Initialize()
		{
			_path = Path.Combine(Paths.ConfigPath, "despr0grammer.ChestCategories.categories.txt");
			if (!File.Exists(_path))
			{
				try
				{
					WriteFile("# ChestCategories - categorías de cofres / chest categories\n#\n# Puedes editar este archivo a mano o desde el HUD del juego (tecla F7). Cuando guardas desde el HUD, el mod\n# reescribe el archivo entero: tus categorías se conservan y estos comentarios se vuelven a generar.\n# Edit this file by hand or from the in-game HUD (F7 key). Saving from the HUD rewrites the whole file:\n# your categories are kept and these comments are regenerated.\n#\n# Cada bloque [id] es una categoría. Si lo editas a mano, el mod recarga el archivo al abrir el HUD o pulsar F8.\n#\n# Claves / keys:\n#   es / en = nombre en español / name in English\n#   color   = color en el HUD, #RRGGBB\n#   item    = nombre interno del objeto (el de la consola: spawn Wood), separados por comas.\n#             Admite un * al principio o al final:  Mead*   *Uncooked\n#             También acepta el nombre traducible del juego:  $item_wood\n#   type    = tipo de objeto del juego: Material, Consumable, OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft,\n#             Bow, Shield, Helmet, Chest, Legs, Shoulder, Hands, Ammo, AmmoNonEquipable, Trophy, Torch, Tool, Utility,\n#             Trinket, Fish, Misc, Customization ... y dos tipos derivados:\n#             Food   = consumible que da vida, aguante o eitr\n#             Potion = consumible que no es comida (pociones, hidromieles)\n#\n# Prioridad, de más a menos específica: 1) nombre exacto (Wood, $item_wood), 2) nombre con * (Mead*), 3) type.\n# Dentro de cada nivel gana la primera categoría del archivo. Por eso un objeto que asignas a mano desde el HUD\n# (nombre exacto) siempre gana a las reglas por tipo de cualquier categoría.\n# Un cambio de id (lo que va entre corchetes) desvincula los cofres que ya lo tenían asignado.\n\n[wood]\nes = Madera\nen = Wood\ncolor = #C8864A\nitem = Wood, FineWood, RoundLog, ElderBark, YggdrasilWood, Blackwood, Frostwood\n\n[metals]\nes = Metales\nen = Metals\ncolor = #8DB4CC\nitem = Copper, Tin, Bronze, Iron, Silver, BlackMetal, Gold, Flametal*\nitem = CopperOre, TinOre, IronOre, IronScrap, SilverOre, BlackMetalScrap, GoldOre\nitem = BronzeNails, IronNails, Chain\n\n[stone]\nes = Piedras\nen = Stone\ncolor = #A5A9B8\nitem = Stone, Flint, Obsidian, Blackmarble, Grausten, Crystal\n\n[valuables]\nes = Tesoros\nen = Valuables\ncolor = #E0BE4A\nitem = Coins, Amber, AmberPearl, Ruby, SilverNecklace, GemstoneRed, GemstoneBlue, GemstoneGreen\n\n[hides]\nes = Pieles y cuero\nen = Hides and leather\ncolor = #B8895C\nitem = LeatherScraps, DeerHide, TrollHide, WolfPelt, LoxPelt, ScaleHide, MooseHide, SealHide, Leatherstraps\n\n[farming]\nes = Cultivo y semillas\nen = Farming and seeds\ncolor = #8FB24F\nitem = *Seeds, Acorn, FirCone, PineCone, Barley, Flax\n\n[vegetables]\nes = Vegetales y bayas\nen = Vegetables and berries\ncolor = #5FAE6E\nitem = Carrot, Turnip, Onion, Kale, Fiddleheadfern\nitem = Cloudberry, Raspberry, Blueberries, Pukeberries, Lingonberry, Vineberry\nitem = Mushroom, MushroomBlue, MushroomYellow, MushroomJotunPuffs, MushroomMagecap, MushroomSmokePuff\n\n[meats]\nes = Carnes\nen = Meats\ncolor = #C0524E\ntype = Fish\nitem = RawMeat, DeerMeat, WolfMeat, LoxMeat, SerpentMeat, BugMeat, MooseMeat, ChickenMeat, HareMeat\nitem = BjornMeat, BoneMawSerpentMeat, VoltureMeat, AsksvinMeat, NeckTail, FishRaw, RottenMeat\nitem = Cooked*, *MeatCooked, FishCooked, NeckTailGrilled\n\n[meals]\nes = Comidas preparadas\nen = Prepared meals\ncolor = #D99A3A\ntype = Food\nitem = Feast*, KaleChipsUncooked\n\n[cooking]\nes = Materiales para comida\nen = Cooking ingredients\ncolor = #C97BA6\nitem = BarleyFlour, BreadDough, Honey, RoyalJelly, ChickenEgg, Oat, Entrails, Bloodbag\nitem = sap, Dandelion, Thistle, SpiceDeepNorth, SealBlubber, GlowWorm\n\n[tools]\nes = Herramientas\nen = Tools\ncolor = #52AFA0\ntype = Tool, Torch\nitem = Pickaxe*, FishingRod*, FishingBait\n\n[potions]\nes = Pociones e hidromieles\nen = Potions and meads\ncolor = #9879C4\ntype = Potion\nitem = Mead*, BarleyWine*\n\n[weapons]\nes = Armas y escudos\nen = Weapons and shields\ncolor = #B83F4A\ntype = OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft, Bow, Attach_Atgeir, Shield\n\n[armor]\nes = Armaduras\nen = Armor\ncolor = #5C86C4\ntype = Helmet, Chest, Legs, Shoulder, Hands\n\n[trinkets]\nes = Accesorios\nen = Accessories\ncolor = #D6B04A\ntype = Utility, Trinket\n\n[ammo]\nes = Munición\nen = Ammunition\ncolor = #C98A4E\ntype = Ammo, AmmoNonEquipable\n\n[trophies]\nes = Trofeos\nen = Trophies\ncolor = #CFCBE0\ntype = Trophy\n\n[drops]\nes = Botín de criaturas\nen = Creature drops\ncolor = #98A844\nitem = Bilebag, Ooze, OozeMork, Guck, BoneFragments, WolfFang, WolfClaw, WolfHairBundle\nitem = Mandible, Chitin, Carapace, Needle, Softtissue, HardAntler, Feathers, GreydwarfEye, FreezeGland\nitem = GiantBloodSack, SerpentScale, DragonTear, Root, MoleClaws, MooseSinew, ElakingHairBundle, WrithanRoots\n\n[other]\nes = Materiales varios\nen = Other materials\ncolor = #8E8A80\ntype = Material, Misc, Customization\n");
					Plugin.Log.LogInfo((object)("Created the default categories file: " + _path));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Could not create " + _path + "; using the built-in categories. " + ex.Message));
				}
			}
			else
			{
				UpgradeUntouchedDefaults();
			}
			Load();
		}

		private static void UpgradeUntouchedDefaults()
		{
			try
			{
				if (CategoryDefaults.IsUntouchedPreviousDefault(File.ReadAllText(_path, Encoding.UTF8)))
				{
					File.Copy(_path, _path + ".old-defaults.bak", overwrite: true);
					WriteFile("# ChestCategories - categorías de cofres / chest categories\n#\n# Puedes editar este archivo a mano o desde el HUD del juego (tecla F7). Cuando guardas desde el HUD, el mod\n# reescribe el archivo entero: tus categorías se conservan y estos comentarios se vuelven a generar.\n# Edit this file by hand or from the in-game HUD (F7 key). Saving from the HUD rewrites the whole file:\n# your categories are kept and these comments are regenerated.\n#\n# Cada bloque [id] es una categoría. Si lo editas a mano, el mod recarga el archivo al abrir el HUD o pulsar F8.\n#\n# Claves / keys:\n#   es / en = nombre en español / name in English\n#   color   = color en el HUD, #RRGGBB\n#   item    = nombre interno del objeto (el de la consola: spawn Wood), separados por comas.\n#             Admite un * al principio o al final:  Mead*   *Uncooked\n#             También acepta el nombre traducible del juego:  $item_wood\n#   type    = tipo de objeto del juego: Material, Consumable, OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft,\n#             Bow, Shield, Helmet, Chest, Legs, Shoulder, Hands, Ammo, AmmoNonEquipable, Trophy, Torch, Tool, Utility,\n#             Trinket, Fish, Misc, Customization ... y dos tipos derivados:\n#             Food   = consumible que da vida, aguante o eitr\n#             Potion = consumible que no es comida (pociones, hidromieles)\n#\n# Prioridad, de más a menos específica: 1) nombre exacto (Wood, $item_wood), 2) nombre con * (Mead*), 3) type.\n# Dentro de cada nivel gana la primera categoría del archivo. Por eso un objeto que asignas a mano desde el HUD\n# (nombre exacto) siempre gana a las reglas por tipo de cualquier categoría.\n# Un cambio de id (lo que va entre corchetes) desvincula los cofres que ya lo tenían asignado.\n\n[wood]\nes = Madera\nen = Wood\ncolor = #C8864A\nitem = Wood, FineWood, RoundLog, ElderBark, YggdrasilWood, Blackwood, Frostwood\n\n[metals]\nes = Metales\nen = Metals\ncolor = #8DB4CC\nitem = Copper, Tin, Bronze, Iron, Silver, BlackMetal, Gold, Flametal*\nitem = CopperOre, TinOre, IronOre, IronScrap, SilverOre, BlackMetalScrap, GoldOre\nitem = BronzeNails, IronNails, Chain\n\n[stone]\nes = Piedras\nen = Stone\ncolor = #A5A9B8\nitem = Stone, Flint, Obsidian, Blackmarble, Grausten, Crystal\n\n[valuables]\nes = Tesoros\nen = Valuables\ncolor = #E0BE4A\nitem = Coins, Amber, AmberPearl, Ruby, SilverNecklace, GemstoneRed, GemstoneBlue, GemstoneGreen\n\n[hides]\nes = Pieles y cuero\nen = Hides and leather\ncolor = #B8895C\nitem = LeatherScraps, DeerHide, TrollHide, WolfPelt, LoxPelt, ScaleHide, MooseHide, SealHide, Leatherstraps\n\n[farming]\nes = Cultivo y semillas\nen = Farming and seeds\ncolor = #8FB24F\nitem = *Seeds, Acorn, FirCone, PineCone, Barley, Flax\n\n[vegetables]\nes = Vegetales y bayas\nen = Vegetables and berries\ncolor = #5FAE6E\nitem = Carrot, Turnip, Onion, Kale, Fiddleheadfern\nitem = Cloudberry, Raspberry, Blueberries, Pukeberries, Lingonberry, Vineberry\nitem = Mushroom, MushroomBlue, MushroomYellow, MushroomJotunPuffs, MushroomMagecap, MushroomSmokePuff\n\n[meats]\nes = Carnes\nen = Meats\ncolor = #C0524E\ntype = Fish\nitem = RawMeat, DeerMeat, WolfMeat, LoxMeat, SerpentMeat, BugMeat, MooseMeat, ChickenMeat, HareMeat\nitem = BjornMeat, BoneMawSerpentMeat, VoltureMeat, AsksvinMeat, NeckTail, FishRaw, RottenMeat\nitem = Cooked*, *MeatCooked, FishCooked, NeckTailGrilled\n\n[meals]\nes = Comidas preparadas\nen = Prepared meals\ncolor = #D99A3A\ntype = Food\nitem = Feast*, KaleChipsUncooked\n\n[cooking]\nes = Materiales para comida\nen = Cooking ingredients\ncolor = #C97BA6\nitem = BarleyFlour, BreadDough, Honey, RoyalJelly, ChickenEgg, Oat, Entrails, Bloodbag\nitem = sap, Dandelion, Thistle, SpiceDeepNorth, SealBlubber, GlowWorm\n\n[tools]\nes = Herramientas\nen = Tools\ncolor = #52AFA0\ntype = Tool, Torch\nitem = Pickaxe*, FishingRod*, FishingBait\n\n[potions]\nes = Pociones e hidromieles\nen = Potions and meads\ncolor = #9879C4\ntype = Potion\nitem = Mead*, BarleyWine*\n\n[weapons]\nes = Armas y escudos\nen = Weapons and shields\ncolor = #B83F4A\ntype = OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft, Bow, Attach_Atgeir, Shield\n\n[armor]\nes = Armaduras\nen = Armor\ncolor = #5C86C4\ntype = Helmet, Chest, Legs, Shoulder, Hands\n\n[trinkets]\nes = Accesorios\nen = Accessories\ncolor = #D6B04A\ntype = Utility, Trinket\n\n[ammo]\nes = Munición\nen = Ammunition\ncolor = #C98A4E\ntype = Ammo, AmmoNonEquipable\n\n[trophies]\nes = Trofeos\nen = Trophies\ncolor = #CFCBE0\ntype = Trophy\n\n[drops]\nes = Botín de criaturas\nen = Creature drops\ncolor = #98A844\nitem = Bilebag, Ooze, OozeMork, Guck, BoneFragments, WolfFang, WolfClaw, WolfHairBundle\nitem = Mandible, Chitin, Carapace, Needle, Softtissue, HardAntler, Feathers, GreydwarfEye, FreezeGland\nitem = GiantBloodSack, SerpentScale, DragonTear, Root, MoleClaws, MooseSinew, ElakingHairBundle, WrithanRoots\n\n[other]\nes = Materiales varios\nen = Other materials\ncolor = #8E8A80\ntype = Material, Misc, Customization\n");
					Plugin.Log.LogInfo((object)("The categories file was never edited, so it was updated to the new default categories (the old one is kept as " + Path.GetFileName(_path) + ".old-defaults.bak)"));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not update the default categories file: " + ex.Message));
			}
		}

		internal static void ReloadIfChanged()
		{
			try
			{
				if (!File.Exists(_path) || !(File.GetLastWriteTimeUtc(_path) != _stamp))
				{
					return;
				}
				Load();
				if (SharedActive)
				{
					if (Loaded.Count > 0 && _lastLoadProblems == 0)
					{
						MarkSharedDirty();
					}
					else
					{
						Plugin.Log.LogWarning((object)"The categories file has problems or no categories, so this change is not shared with the world until it is fixed.");
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not check the categories file: " + ex.Message));
			}
		}

		internal static Category Get(string id)
		{
			if (string.IsNullOrEmpty(id) || !ByIdMap.TryGetValue(id, out var value))
			{
				return null;
			}
			return value;
		}

		internal static Color ColorOf(Category category, Color fallback)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Color color = default(Color);
			if (category == null || category.Color == null || !ColorUtility.TryParseHtmlString(category.Color, ref color))
			{
				return fallback;
			}
			return Tame(color);
		}

		internal static Color Tame(Color color)
		{
			//IL_0000: 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)
			float num = default(float);
			float num2 = default(float);
			float num3 = default(float);
			Color.RGBToHSV(color, ref num, ref num2, ref num3);
			if (num2 > 0.55f)
			{
				num2 = 0.55f + (num2 - 0.55f) * 0.3f;
			}
			return Color.HSVToRGB(num, num2, Mathf.Clamp(num3, 0.55f, 0.9f));
		}

		internal static Category Of(ItemData item)
		{
			SharedData val = item?.m_shared;
			if (val == null)
			{
				return null;
			}
			if (!Cache.TryGetValue(val, out var value))
			{
				value = Resolve(PrefabName(item), val);
				Cache[val] = value;
			}
			return value;
		}

		internal static Category Resolve(string prefab, SharedData shared)
		{
			string text = ((object)Unsafe.As<ItemType, ItemType>(ref shared.m_itemType)/*cast due to .constrained prefix*/).ToString();
			bool flag = text == "Consumable";
			bool flag2 = flag && (shared.m_food > 0f || shared.m_foodStamina > 0f || shared.m_foodEitr > 0f);
			return CategoryFile.Classify(Loaded, prefab, shared.m_name, text, flag2, flag && !flag2);
		}

		internal static void SyncShared(bool hudOpen)
		{
			ZoneSystem instance = ZoneSystem.instance;
			if (!Plugin.ShareCategories.Value || (Object)(object)instance == (Object)null || ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			long worldUID = ZNet.instance.GetWorldUID();
			if (worldUID != _sharedWorld)
			{
				ResetShared();
				_sharedWorld = worldUID;
			}
			string store = StoreValue(instance);
			_sharedSynced = true;
			if (_sharedDirty)
			{
				if (!hudOpen || Time.unscaledTime - _sharedDirtySince >= 60f)
				{
					PublishShared();
				}
			}
			else
			{
				AdoptOrSeed(store);
			}
		}

		private static void AdoptOrSeed(string store)
		{
			if (store == _sharedMine)
			{
				_sharedPending = false;
			}
			SyncAction syncAction = SharedCodec.Decide(store, _sharedMine, _sharedPending, Time.unscaledTime - _sharedPublishedAt);
			switch (syncAction)
			{
			case SyncAction.Adopt:
				AdoptShared(store);
				return;
			case SyncAction.Seed:
				if (Loaded.Count > 0 && _sharedTooBigVersion != Version)
				{
					PublishShared();
					return;
				}
				break;
			}
			if (syncAction == SyncAction.None && store != null && SharedCodec.RevisionOf(store) < 0 && !_sharedWarned)
			{
				_sharedWarned = true;
				Plugin.Log.LogWarning((object)"The world has a value under the shared categories key that this version cannot read (a newer version of the mod?); it is left alone and the categories are not shared.");
			}
		}

		private static void BeforeEdit()
		{
			ZoneSystem instance = ZoneSystem.instance;
			if (Plugin.ShareCategories.Value && _sharedSynced && !_sharedDirty && (Object)(object)instance != (Object)null && ZRoutedRpc.instance != null)
			{
				AdoptOrSeed(StoreValue(instance));
			}
		}

		private static Category Fresh(Category category)
		{
			object obj;
			if (category != null)
			{
				obj = Get(category.Id);
				if (obj == null)
				{
					return category;
				}
			}
			else
			{
				obj = null;
			}
			return (Category)obj;
		}

		private static void MarkSharedDirty()
		{
			if (Plugin.ShareCategories.Value && _sharedSynced && !_sharedDirty)
			{
				_sharedDirty = true;
				_sharedDirtySince = Time.unscaledTime;
			}
		}

		private static void ResetShared()
		{
			_sharedMine = null;
			_sharedPending = false;
			_sharedSynced = false;
			_sharedFirst = true;
			_sharedWarned = false;
			_backedUpBeforeShared = false;
			_sharedDirty = false;
			_sharedTooBigVersion = -1;
			SharedActive = false;
		}

		private static string StoreValue(ZoneSystem zones)
		{
			string result = default(string);
			if (!zones.GetGlobalKey("despr0grammer_chestcategories", ref result))
			{
				return null;
			}
			return result;
		}

		private static void AdoptShared(string store)
		{
			if (!SharedCodec.TryDecode(store, out var revision, out var text))
			{
				if (!_sharedWarned)
				{
					_sharedWarned = true;
					Plugin.Log.LogWarning((object)"The world's shared categories could not be read (damaged?); they are left alone.");
				}
				return;
			}
			List<string> list = new List<string>();
			List<Category> list2 = CategoryFile.Parse(text.Split('\n'), list);
			foreach (string item in list)
			{
				Plugin.Log.LogWarning((object)("Shared categories: " + item));
			}
			ValidateTypes(list2);
			KeepListBeforeShared(list2);
			bool num = _sharedPending && _sharedMine != null;
			Loaded.Clear();
			Loaded.AddRange(list2);
			Reindex();
			bool flag = Save();
			_sharedMine = store;
			_sharedPending = false;
			SharedActive = true;
			if (num)
			{
				Plugin.Log.LogWarning((object)"The last change to the shared categories did not reach the world (or another player's change replaced it); using the world's list.");
			}
			Plugin.Log.LogInfo((object)string.Format("Using the world's shared categories (revision {0}, {1} categories){2}", revision, Loaded.Count, flag ? string.Empty : "; the local file could not be updated"));
			Actions.Notify(_sharedFirst ? Text.SharedLoaded : Text.SharedUpdated);
			_sharedFirst = false;
		}

		private static void PublishShared()
		{
			ZoneSystem instance = ZoneSystem.instance;
			if (!Plugin.ShareCategories.Value || !_sharedSynced || (Object)(object)instance == (Object)null || ZRoutedRpc.instance == null || _readFailed)
			{
				return;
			}
			_sharedDirty = false;
			string text = StoreValue(instance);
			if (text != null && SharedCodec.RevisionOf(text) < 0)
			{
				if (!_sharedWarned)
				{
					_sharedWarned = true;
					Plugin.Log.LogWarning((object)"The world has a value under the shared categories key that this version cannot read; it is left alone and the categories are not shared.");
				}
				return;
			}
			int num = Math.Max(SharedCodec.RevisionOf(text), Math.Max(SharedCodec.RevisionOf(_sharedMine), 0));
			if (num >= 2147483646)
			{
				Plugin.Log.LogWarning((object)"The shared categories have reached the highest revision there is; they cannot be shared any more.");
				return;
			}
			int num2 = num + 1;
			string text2 = SharedCodec.Encode(num2, CategoryFile.SerializeBody(Loaded));
			if (text2.Length > 60000)
			{
				if (_sharedTooBigVersion != Version)
				{
					_sharedTooBigVersion = Version;
					Plugin.Log.LogWarning((object)$"The category list is too big to share ({text2.Length} characters; the limit is {60000}); it stays on this game only.");
				}
			}
			else
			{
				instance.SetGlobalKey("despr0grammer_chestcategories " + text2);
				_sharedMine = text2;
				_sharedPending = true;
				_sharedPublishedAt = Time.unscaledTime;
				SharedActive = true;
				Plugin.Log.LogInfo((object)$"Shared the categories with the world (revision {num2}, {Loaded.Count} categories)");
			}
		}

		private static void KeepListBeforeShared(List<Category> adopted)
		{
			if (_backedUpBeforeShared)
			{
				return;
			}
			_backedUpBeforeShared = true;
			try
			{
				if (File.Exists(_path) && !(CategoryFile.SerializeBody(Loaded) == CategoryFile.SerializeBody(adopted)))
				{
					string text = $"{_path}.before-shared-{DateTime.Now:yyyyMMdd-HHmmss}";
					File.Copy(_path, text, overwrite: true);
					Plugin.Log.LogInfo((object)("Kept the local categories as " + Path.GetFileName(text) + " before using the world's."));
					string[] files = Directory.GetFiles(Path.GetDirectoryName(_path), Path.GetFileName(_path) + ".before-shared-*");
					Array.Sort(files, (IComparer<string>?)StringComparer.Ordinal);
					for (int i = 0; i < files.Length - 5; i++)
					{
						File.Delete(files[i]);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not keep the local categories before using the world's: " + ex.Message));
			}
		}

		internal static Category Create(string name, out bool saved)
		{
			BeforeEdit();
			Category category = CategoryEditor.Add(Loaded, name);
			saved = category == null || Commit();
			return category;
		}

		internal static bool Delete(Category category)
		{
			BeforeEdit();
			if (!CategoryEditor.Remove(Loaded, category.Id))
			{
				return true;
			}
			return Commit();
		}

		internal static bool Rename(Category category, string spanish, string english)
		{
			BeforeEdit();
			if (CategoryEditor.Rename(Fresh(category), spanish, english))
			{
				return Commit();
			}
			return true;
		}

		internal static bool SetColor(Category category, string color)
		{
			BeforeEdit();
			category = Fresh(category);
			if (!CategoryFile.IsValidColor(color) || string.Equals(category.Color, color, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			category.Color = color.ToUpperInvariant();
			return Commit();
		}

		internal static bool AssignItem(Category category, string prefab)
		{
			BeforeEdit();
			CategoryEditor.Assign(Loaded, Fresh(category), prefab);
			return Commit();
		}

		internal static bool UnassignItem(Category category, string prefab)
		{
			BeforeEdit();
			if (CategoryEditor.Unassign(Fresh(category), prefab))
			{
				return Commit();
			}
			return true;
		}

		internal static bool RemovePattern(Category category, string pattern)
		{
			BeforeEdit();
			if (Fresh(category).Patterns.Remove(pattern))
			{
				return Commit();
			}
			return true;
		}

		internal static bool RemoveType(Category category, string type)
		{
			BeforeEdit();
			if (Fresh(category).Types.Remove(type))
			{
				return Commit();
			}
			return true;
		}

		private static bool Commit()
		{
			Reindex();
			bool result = Save();
			MarkSharedDirty();
			return result;
		}

		private static void Reindex()
		{
			ByIdMap.Clear();
			foreach (Category item in Loaded)
			{
				ByIdMap[item.Id] = item;
			}
			Cache.Clear();
			Version++;
		}

		private static bool Save()
		{
			if (_readFailed)
			{
				Plugin.Log.LogError((object)("Not saving: " + _path + " could not be read earlier, and saving now would overwrite it with the built-in categories."));
				return false;
			}
			try
			{
				BackUpOnce();
				BackUpIfEditedElsewhere();
				_diskText = WriteFile(CategoryFile.Serialize(Loaded));
				_stamp = File.GetLastWriteTimeUtc(_path);
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not save " + _path + ": " + ex.Message));
				return false;
			}
		}

		private static void BackUpOnce()
		{
			string text = _path + ".bak";
			if (File.Exists(_path) && !File.Exists(text))
			{
				File.Copy(_path, text);
			}
		}

		private static void BackUpIfEditedElsewhere()
		{
			if (_diskText != null && File.Exists(_path) && !(File.ReadAllText(_path, Encoding.UTF8) == _diskText))
			{
				string text = $"{_path}.bak-{DateTime.Now:yyyyMMdd-HHmmss}";
				File.Copy(_path, text, overwrite: true);
				Plugin.Log.LogWarning((object)("The categories file was changed outside the game since it was loaded; its content was kept as " + Path.GetFileName(text) + " before saving."));
			}
		}

		private static string WriteFile(string text)
		{
			string text2 = text.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);
			string text3 = _path + ".tmp";
			File.WriteAllText(text3, text2, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
			try
			{
				if (File.Exists(_path))
				{
					File.Replace(text3, _path, _path + ".prev");
				}
				else
				{
					File.Move(text3, _path);
				}
			}
			catch (Exception)
			{
				File.Copy(text3, _path, overwrite: true);
				try
				{
					File.Delete(text3);
				}
				catch (Exception)
				{
				}
			}
			return text2;
		}

		private static void Load()
		{
			List<string> list = new List<string>();
			DateTime minValue = DateTime.MinValue;
			string text = null;
			bool readFailed = false;
			List<Category> list2;
			try
			{
				minValue = File.GetLastWriteTimeUtc(_path);
				text = File.ReadAllText(_path, Encoding.UTF8);
				list2 = CategoryFile.Parse(text.Split('\n'), list);
			}
			catch (Exception ex)
			{
				readFailed = true;
				minValue = DateTime.MinValue;
				text = null;
				Plugin.Log.LogWarning((object)("Could not read " + _path + "; using the built-in categories until it can be read. " + ex.Message));
				list2 = CategoryFile.Parse("# ChestCategories - categorías de cofres / chest categories\n#\n# Puedes editar este archivo a mano o desde el HUD del juego (tecla F7). Cuando guardas desde el HUD, el mod\n# reescribe el archivo entero: tus categorías se conservan y estos comentarios se vuelven a generar.\n# Edit this file by hand or from the in-game HUD (F7 key). Saving from the HUD rewrites the whole file:\n# your categories are kept and these comments are regenerated.\n#\n# Cada bloque [id] es una categoría. Si lo editas a mano, el mod recarga el archivo al abrir el HUD o pulsar F8.\n#\n# Claves / keys:\n#   es / en = nombre en español / name in English\n#   color   = color en el HUD, #RRGGBB\n#   item    = nombre interno del objeto (el de la consola: spawn Wood), separados por comas.\n#             Admite un * al principio o al final:  Mead*   *Uncooked\n#             También acepta el nombre traducible del juego:  $item_wood\n#   type    = tipo de objeto del juego: Material, Consumable, OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft,\n#             Bow, Shield, Helmet, Chest, Legs, Shoulder, Hands, Ammo, AmmoNonEquipable, Trophy, Torch, Tool, Utility,\n#             Trinket, Fish, Misc, Customization ... y dos tipos derivados:\n#             Food   = consumible que da vida, aguante o eitr\n#             Potion = consumible que no es comida (pociones, hidromieles)\n#\n# Prioridad, de más a menos específica: 1) nombre exacto (Wood, $item_wood), 2) nombre con * (Mead*), 3) type.\n# Dentro de cada nivel gana la primera categoría del archivo. Por eso un objeto que asignas a mano desde el HUD\n# (nombre exacto) siempre gana a las reglas por tipo de cualquier categoría.\n# Un cambio de id (lo que va entre corchetes) desvincula los cofres que ya lo tenían asignado.\n\n[wood]\nes = Madera\nen = Wood\ncolor = #C8864A\nitem = Wood, FineWood, RoundLog, ElderBark, YggdrasilWood, Blackwood, Frostwood\n\n[metals]\nes = Metales\nen = Metals\ncolor = #8DB4CC\nitem = Copper, Tin, Bronze, Iron, Silver, BlackMetal, Gold, Flametal*\nitem = CopperOre, TinOre, IronOre, IronScrap, SilverOre, BlackMetalScrap, GoldOre\nitem = BronzeNails, IronNails, Chain\n\n[stone]\nes = Piedras\nen = Stone\ncolor = #A5A9B8\nitem = Stone, Flint, Obsidian, Blackmarble, Grausten, Crystal\n\n[valuables]\nes = Tesoros\nen = Valuables\ncolor = #E0BE4A\nitem = Coins, Amber, AmberPearl, Ruby, SilverNecklace, GemstoneRed, GemstoneBlue, GemstoneGreen\n\n[hides]\nes = Pieles y cuero\nen = Hides and leather\ncolor = #B8895C\nitem = LeatherScraps, DeerHide, TrollHide, WolfPelt, LoxPelt, ScaleHide, MooseHide, SealHide, Leatherstraps\n\n[farming]\nes = Cultivo y semillas\nen = Farming and seeds\ncolor = #8FB24F\nitem = *Seeds, Acorn, FirCone, PineCone, Barley, Flax\n\n[vegetables]\nes = Vegetales y bayas\nen = Vegetables and berries\ncolor = #5FAE6E\nitem = Carrot, Turnip, Onion, Kale, Fiddleheadfern\nitem = Cloudberry, Raspberry, Blueberries, Pukeberries, Lingonberry, Vineberry\nitem = Mushroom, MushroomBlue, MushroomYellow, MushroomJotunPuffs, MushroomMagecap, MushroomSmokePuff\n\n[meats]\nes = Carnes\nen = Meats\ncolor = #C0524E\ntype = Fish\nitem = RawMeat, DeerMeat, WolfMeat, LoxMeat, SerpentMeat, BugMeat, MooseMeat, ChickenMeat, HareMeat\nitem = BjornMeat, BoneMawSerpentMeat, VoltureMeat, AsksvinMeat, NeckTail, FishRaw, RottenMeat\nitem = Cooked*, *MeatCooked, FishCooked, NeckTailGrilled\n\n[meals]\nes = Comidas preparadas\nen = Prepared meals\ncolor = #D99A3A\ntype = Food\nitem = Feast*, KaleChipsUncooked\n\n[cooking]\nes = Materiales para comida\nen = Cooking ingredients\ncolor = #C97BA6\nitem = BarleyFlour, BreadDough, Honey, RoyalJelly, ChickenEgg, Oat, Entrails, Bloodbag\nitem = sap, Dandelion, Thistle, SpiceDeepNorth, SealBlubber, GlowWorm\n\n[tools]\nes = Herramientas\nen = Tools\ncolor = #52AFA0\ntype = Tool, Torch\nitem = Pickaxe*, FishingRod*, FishingBait\n\n[potions]\nes = Pociones e hidromieles\nen = Potions and meads\ncolor = #9879C4\ntype = Potion\nitem = Mead*, BarleyWine*\n\n[weapons]\nes = Armas y escudos\nen = Weapons and shields\ncolor = #B83F4A\ntype = OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft, Bow, Attach_Atgeir, Shield\n\n[armor]\nes = Armaduras\nen = Armor\ncolor = #5C86C4\ntype = Helmet, Chest, Legs, Shoulder, Hands\n\n[trinkets]\nes = Accesorios\nen = Accessories\ncolor = #D6B04A\ntype = Utility, Trinket\n\n[ammo]\nes = Munición\nen = Ammunition\ncolor = #C98A4E\ntype = Ammo, AmmoNonEquipable\n\n[trophies]\nes = Trofeos\nen = Trophies\ncolor = #CFCBE0\ntype = Trophy\n\n[drops]\nes = Botín de criaturas\nen = Creature drops\ncolor = #98A844\nitem = Bilebag, Ooze, OozeMork, Guck, BoneFragments, WolfFang, WolfClaw, WolfHairBundle\nitem = Mandible, Chitin, Carapace, Needle, Softtissue, HardAntler, Feathers, GreydwarfEye, FreezeGland\nitem = GiantBloodSack, SerpentScale, DragonTear, Root, MoleClaws, MooseSinew, ElakingHairBundle, WrithanRoots\n\n[other]\nes = Materiales varios\nen = Other materials\ncolor = #8E8A80\ntype = Material, Misc, Customization\n".Split('\n'), list);
			}
			_readFailed = readFailed;
			_diskText = text;
			_lastLoadProblems = list.Count;
			foreach (string item in list)
			{
				Plugin.Log.LogWarning((object)("despr0grammer.ChestCategories.categories.txt: " + item));
			}
			ValidateTypes(list2);
			Loaded.Clear();
			Loaded.AddRange(list2);
			Reindex();
			_stamp = minValue;
			Plugin.Log.LogInfo((object)$"Loaded {Loaded.Count} chest categories");
			if (Loaded.Count == 0)
			{
				Plugin.Log.LogWarning((object)"The categories file defines no categories, so chests cannot be tagged.");
			}
		}

		private static void ValidateTypes(List<Category> categories)
		{
			HashSet<string> hashSet = new HashSet<string>(Enum.GetNames(typeof(ItemType)), StringComparer.OrdinalIgnoreCase) { "Food", "Potion" };
			foreach (Category category in categories)
			{
				foreach (string type in category.Types)
				{
					if (!hashSet.Contains(type))
					{
						Plugin.Log.LogWarning((object)("despr0grammer.ChestCategories.categories.txt: [" + category.Id + "] type '" + type + "' is not an item type of this game version"));
					}
				}
			}
		}

		private static string PrefabName(ItemData item)
		{
			GameObject dropPrefab = item.m_dropPrefab;
			if ((Object)(object)dropPrefab != (Object)null)
			{
				return StripClone(((Object)dropPrefab).name);
			}
			return PrefabNameFromObjectDb(item.m_shared.m_name);
		}

		private static string PrefabNameFromObjectDb(string sharedName)
		{
			if (_prefabBySharedName == null)
			{
				ObjectDB instance = ObjectDB.instance;
				if ((Object)(object)instance == (Object)null || instance.m_items == null || instance.m_items.Count == 0)
				{
					return null;
				}
				Dictionary<string, string> dictionary = new Dictionary<string, string>();
				foreach (GameObject item in instance.m_items)
				{
					string text = ((!((Object)(object)item != (Object)null)) ? null : item.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name);
					if (!string.IsNullOrEmpty(text) && !dictionary.ContainsKey(text))
					{
						dictionary[text] = ((Object)item).name;
					}
				}
				_prefabBySharedName = dictionary;
			}
			if (!_prefabBySharedName.TryGetValue(sharedName, out var value))
			{
				return null;
			}
			return value;
		}

		private static string StripClone(string name)
		{
			if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return name;
			}
			return name.Substring(0, name.Length - "(Clone)".Length);
		}
	}
	internal static class CategoryHud
	{
		private static int _blockUntilFrame = -1;

		internal static bool Available { get; set; }

		internal static bool IsOpen { get; private set; }

		internal static Container Chest { get; private set; }

		internal static CategoryHudWindow Window { get; set; }

		internal static bool Blocking
		{
			get
			{
				if (!IsOpen)
				{
					return Time.frameCount <= _blockUntilFrame;
				}
				return true;
			}
		}

		internal static void Open(Container chest)
		{
			if (!Available || (Object)(object)Window == (Object)null || IsOpen)
			{
				return;
			}
			Chest = chest;
			IsOpen = true;
			try
			{
				Window.OnOpened(chest);
				((Behaviour)Window).enabled = true;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"The category HUD could not open: {arg}");
				Close();
			}
		}

		internal static void Close()
		{
			if (IsOpen)
			{
				IsOpen = false;
				Chest = null;
				_blockUntilFrame = Time.frameCount + 1;
				if ((Object)(object)Window != (Object)null)
				{
					((Behaviour)Window).enabled = false;
				}
			}
		}
	}
	internal sealed class CategoryHudWindow : MonoBehaviour
	{
		private struct RuleRow
		{
			internal string Title;

			internal string Sub;

			internal Sprite Icon;

			internal int Kind;

			internal string Key;
		}

		private const float PanelMaxWidth = 920f;

		private const float PanelMaxHeight = 660f;

		private const float RuleRowHeight = 44f;

		private const float ItemRowHeight = 46f;

		private const float ContentTop = 138f;

		private const float ContentBottom = 44f;

		private int _tab;

		private string _selectedId;

		private bool _picking;

		private bool _creating;

		private bool _focusNewName;

		private bool _focusSearch;

		private bool _confirmDelete;

		private string _newName = string.Empty;

		private string _search = string.Empty;

		private int _filter;

		private Vector2 _scrollGrid;

		private Vector2 _scrollCategories;

		private Vector2 _scrollRules;

		private Vector2 _scrollItems;

		private string _nameEs = string.Empty;

		private string _nameEn = string.Empty;

		private string _buffersFor;

		private string _status;

		private float _statusUntil;

		private readonly List<RuleRow> _rules = new List<RuleRow>();

		private string _rulesFor;

		private int _rulesVersion = -1;

		private string _rulesLanguage;

		private int _rulesGeneration = -1;

		private readonly List<CatalogEntry> _filtered = new List<CatalogEntry>(700);

		private string _filteredSearch;

		private int _filteredFilter = -1;

		private int _filteredVersion = -1;

		private string _filteredFor;

		private int _filteredInventory = -1;

		private int _filteredGeneration = -1;

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

		private int _inventoryStamp;

		private float _inventoryRead = -10f;

		private void Awake()
		{
			((Behaviour)this).enabled = false;
		}

		internal void OnOpened(Container chest)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			_tab = ((!((Object)(object)chest != (Object)null)) ? 1 : 0);
			_picking = false;
			_creating = false;
			_confirmDelete = false;
			_status = null;
			_search = string.Empty;
			_filter = 0;
			_buffersFor = null;
			_scrollGrid = Vector2.zero;
			_scrollCategories = Vector2.zero;
			_scrollRules = Vector2.zero;
			_scrollItems = Vector2.zero;
			Category category = (((Object)(object)chest != (Object)null) ? Categories.Get(ChestTag.Get(chest)) : null);
			if (category != null)
			{
				_selectedId = category.Id;
			}
			else if (Categories.Get(_selectedId) == null)
			{
				_selectedId = ((Categories.All.Count > 0) ? Categories.All[0].Id : null);
			}
		}

		private void Update()
		{
			if (CategoryHud.IsOpen)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || Menu.IsVisible() || InventoryGui.IsVisible() || Console.IsVisible() || Input.GetKeyDown((KeyCode)27))
				{
					CategoryHud.Close();
				}
			}
		}

		private void OnGUI()
		{
			if (!CategoryHud.IsOpen)
			{
				return;
			}
			try
			{
				Draw();
			}
			catch (ExitGUIException)
			{
				throw;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"The category HUD failed and was closed: {arg}");
				CategoryHud.Close();
			}
		}

		private void Draw()
		{
			//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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			HudStyle.Ensure();
			GUI.depth = -100;
			GUISkin skin = GUI.skin;
			GUI.skin = HudStyle.Skin;
			float num = Mathf.Clamp((float)Screen.height / 1080f, 0.75f, 3f) * Plugin.HudScale.Value;
			GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
			float num2 = (float)Screen.width / num;
			float num3 = (float)Screen.height / num;
			float num4 = Mathf.Min(920f, num2 - 40f);
			float num5 = Mathf.Min(660f, num3 - 40f);
			GUI.BeginGroup(new Rect((num2 - num4) / 2f, (num3 - num5) / 2f, num4, num5));
			try
			{
				DrawFrame(num4, num5);
				DrawTabs(num4);
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(28f, 138f, num4 - 56f, num5 - 138f - 44f);
				GUI.BeginGroup(val);
				try
				{
					if (_tab == 0)
					{
						DrawChestTab(((Rect)(ref val)).width, ((Rect)(ref val)).height);
					}
					else
					{
						DrawCategoriesTab(((Rect)(ref val)).width, ((Rect)(ref val)).height);
					}
				}
				finally
				{
					GUI.EndGroup();
				}
				DrawFooter(num4, num5);
			}
			finally
			{
				GUI.EndGroup();
				GUI.matrix = Matrix4x4.identity;
				GUI.skin = skin;
			}
		}

		private void DrawFrame(float width, float height)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			HudStyle.Frame(new Rect(0f, 0f, width, height));
			HudStyle.DrawTitle(new Rect(70f, 18f, width - 140f, 50f), Text.HudTitle);
			HudStyle.Divider(60f, width - 60f, 74f);
			if (GUI.Button(new Rect(width - 66f, 24f, 38f, 34f), "X", HudStyle.Button))
			{
				CategoryHud.Close();
			}
		}

		private void DrawTabs(float width)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			string[] array = new string[2]
			{
				Text.TabChest,
				Text.TabCategories
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (GUI.Button(new Rect(28f + (float)i * 178f, 90f, 170f, 36f), array[i], (_tab == i) ? HudStyle.TabOn : HudStyle.Button))
				{
					_tab = i;
					_picking = false;
				}
			}
			if (GUI.Button(new Rect(width - 28f - 220f, 90f, 220f, 36f), Text.OrderChests, HudStyle.Button))
			{
				Say(RunOrder());
			}
		}

		private static string RunOrder()
		{
			try
			{
				return Actions.OrderNearby();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Order failed: {arg}");
				return Text.OrderSkipped;
			}
		}

		private void DrawFooter(float width, float height)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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)
			HudStyle.Shadowed(new Rect(32f, height - 36f, 300f, 26f), Text.FooterHint(Plugin.MenuKeyLabel), HudStyle.Small);
			bool flag = Categories.SharedActive && Plugin.ShareCategories.Value;
			if (flag)
			{
				HudStyle.Shadowed(new Rect(width - 190f, height - 36f, 160f, 26f), Text.SharedFooter(Categories.SharedRevision), HudStyle.Small);
			}
			if (_status != null && Time.unscaledTime < _statusUntil)
			{
				HudStyle.Shadowed(new Rect(340f, height - 36f, width - (flag ? 540f : 380f), 26f), _status, HudStyle.Notice);
			}
		}

		private void Say(string message)
		{
			_status = message;
			_statusUntil = Time.unscaledTime + 4f;
		}

		private void DrawChestTab(float width, float height)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			Container chest = CategoryHud.Chest;
			Player localPlayer = Player.m_localPlayer;
			int num;
			int num2;
			if ((Object)(object)chest != (Object)null && (Object)(object)localPlayer != (Object)null)
			{
				num = (ChestRegistry.IsStorage(chest) ? 1 : 0);
				if (num != 0)
				{
					num2 = (ChestRegistry.CanUse(chest, localPlayer.GetPlayerID()) ? 1 : 0);
					goto IL_0039;
				}
			}
			else
			{
				num = 0;
			}
			num2 = 0;
			goto IL_0039;
			IL_0039:
			bool flag = (byte)num2 != 0;
			string text = ((num != 0) ? ChestTag.Get(chest) : string.Empty);
			Category category = Categories.Get(text);
			if (num == 0)
			{
				HudStyle.Shadowed(new Rect(0f, 0f, width, 60f), Text.NoChestTarget(Plugin.MenuKeyLabel), HudStyle.Wrap);
			}
			else
			{
				HudStyle.Shadowed(new Rect(0f, 0f, width, 28f), Text.ChestHeader(Localization.instance.Localize(chest.m_name)), HudStyle.Heading);
				HudStyle.Shadowed(new Rect(0f, 32f, 190f, 26f), Text.CurrentCategory + ":", HudStyle.Small);
				Color contentColor = GUI.contentColor;
				GUI.contentColor = Categories.ColorOf(category, HudStyle.Dim);
				HudStyle.Shadowed(text: (category != null) ? category.DisplayName(Text.Spanish) : ((text.Length > 0) ? Text.UnknownCategory(text) : Text.NoCategory), rect: new Rect(190f, 32f, width - 190f, 26f), style: HudStyle.Plain);
				GUI.contentColor = contentColor;
				if (!flag)
				{
					HudStyle.Shadowed(new Rect(width - 330f, 0f, 330f, 28f), Text.NoAccess, HudStyle.Small);
				}
			}
			IReadOnlyList<Category> all = Categories.All;
			int num3 = all.Count + 1;
			int num4 = Mathf.Max(1, Mathf.FloorToInt((width - 16f) / 250f));
			float num5 = (width - 16f - (float)(num4 - 1) * 8f) / (float)num4;
			int num6 = Mathf.CeilToInt((float)num3 / (float)num4);
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(0f, 66f, width, height - 66f - 48f);
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(0f, 0f, width - 16f, (float)num6 * 54f);
			_scrollGrid = GUI.BeginScrollView(val, _scrollGrid, val2);
			Rect val3 = default(Rect);
			for (int i = 0; i < num3; i++)
			{
				((Rect)(ref val3))..ctor((float)(i % num4) * (num5 + 8f), (float)(i / num4) * 54f, num5, 46f);
				Category category2 = ((i == 0) ? null : all[i - 1]);
				bool selected = ((category2 == null) ? (text.Length == 0) : string.Equals(text, category2.Id, StringComparison.OrdinalIgnoreCase));
				DrawCategoryCell(val3, category2, selected, flag);
				if (flag && GUI.Button(val3, GUIContent.none, HudStyle.Row))
				{
					Say(Actions.AssignChest(chest, category2));
				}
			}
			GUI.EndScrollView();
			if (GUI.Button(new Rect(0f, height - 42f, 240f, 36f), Text.EditCategories, HudStyle.Button))
			{
				_tab = 1;
				_picking = false;
				if (category != null)
				{
					_selectedId = category.Id;
				}
			}
		}

		private static void DrawCategoryCell(Rect rect, Category category, bool selected, bool enabled)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			if (!enabled)
			{
				GUI.color = new Color(1f, 1f, 1f, 0.55f);
			}
			GUI.Box(rect, GUIContent.none, selected ? HudStyle.SlotOn : HudStyle.Slot);
			DrawTag(new Rect(((Rect)(ref rect)).x + 14f, ((Rect)(ref rect)).y + ((Rect)(ref rect)).height / 2f - 9f, 14f, 18f), category);
			Color contentColor = GUI.contentColor;
			GUI.contentColor = (selected ? HudStyle.GoldBright : Color.white);
			HudStyle.Shadowed(new Rect(((Rect)(ref rect)).x + 40f, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width - 48f, ((Rect)(ref rect)).height), (category == null) ? Text.NoCategoryOption : category.DisplayName(Text.Spanish), HudStyle.Plain);
			GUI.contentColor = contentColor;
			GUI.color = color;
		}

		private static void DrawTag(Rect rect, Category category)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			Color color = (Color)((category == null) ? new Color(0.45f, 0.4f, 0.34f, 1f) : Categories.ColorOf(category, HudStyle.Dim));
			HudStyle.Fill(rect, color);
			HudStyle.Border(rect, new Color(0.07f, 0.045f, 0.03f, 0.95f));
			HudStyle.Fill(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width - 4f, 2f), new Color(1f, 1f, 1f, 0.22f));
		}

		private void DrawCategoriesTab(float width, float height)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			DrawCategoryList(new Rect(0f, 0f, 250f, height));
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(266f, 0f, width - 250f - 16f, height);
			Category category = Categories.Get(_selectedId);
			if (category == null)
			{
				HudStyle.Shadowed(val, Text.SelectCategory, HudStyle.Wrap);
				return;
			}
			GUI.BeginGroup(val);
			try
			{
				if (_picking)
				{
					DrawItemPicker(category, ((Rect)(ref val)).width, ((Rect)(ref val)).height);
				}
				else
				{
					DrawCategoryDetail(category, ((Rect)(ref val)).width, ((Rect)(ref val)).height);
				}
			}
			finally
			{
				GUI.EndGroup();
			}
		}

		private void DrawCategoryList(Rect area)
		{
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_01b1: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			float num;
			if (_creating)
			{
				Event current = Event.current;
				bool flag = (int)current.type == 4 && ((int)current.keyCode == 13 || (int)current.keyCode == 271) && GUI.GetNameOfFocusedControl() == "newname";
				GUI.SetNextControlName("newname");
				_newName = GUI.TextField(new Rect(0f, 0f, ((Rect)(ref area)).width, 34f), _newName, 40, HudStyle.Field);
				if (_focusNewName)
				{
					GUI.FocusControl("newname");
					_focusNewName = false;
				}
				if (flag)
				{
					current.Use();
				}
				if (GUI.Button(new Rect(0f, 38f, ((Rect)(ref area)).width / 2f - 4f, 32f), Text.Create, HudStyle.Button) || flag)
				{
					CreateCategory();
				}
				if (GUI.Button(new Rect(((Rect)(ref area)).width / 2f + 4f, 38f, ((Rect)(ref area)).width / 2f - 4f, 32f), Text.Cancel, HudStyle.Button))
				{
					_creating = false;
				}
				num = 78f;
			}
			else
			{
				if (GUI.Button(new Rect(0f, 0f, ((Rect)(ref area)).width, 36f), Text.NewCategory, HudStyle.Button))
				{
					_creating = true;
					_newName = string.Empty;
					_focusNewName = true;
				}
				num = 46f;
			}
			IReadOnlyList<Category> categories = Categories.All;
			VirtualRows(new Rect(0f, num, ((Rect)(ref area)).width, ((Rect)(ref area)).height - num), ref _scrollCategories, categories.Count, 40f, delegate(int i, Rect rect)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				DrawCategoryRow(categories[i], rect);
			});
		}

		private void DrawCategoryRow(Category category, Rect rect)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0079: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
			bool flag = string.Equals(category.Id, _selectedId, StringComparison.OrdinalIgnoreCase);
			GUI.Box(rect, GUIContent.none, flag ? HudStyle.SlotOn : HudStyle.Slot);
			DrawTag(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + ((Rect)(ref rect)).height / 2f - 9f, 14f, 18f), category);
			Color contentColor = GUI.contentColor;
			GUI.contentColor = (flag ? HudStyle.GoldBright : Color.white);
			HudStyle.Shadowed(new Rect(((Rect)(ref rect)).x + 38f, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width - 44f, ((Rect)(ref rect)).height), category.DisplayName(Text.Spanish), HudStyle.Plain);
			GUI.contentColor = contentColor;
			if (GUI.Button(rect, GUIContent.none, HudStyle.Row))
			{
				_selectedId = category.Id;
				_picking = false;
				_confirmDelete = false;
				_scrollRules = Vector2.zero;
			}
		}

		private void CreateCategory()
		{
			bool saved;
			Category category = Categories.Create(_newName, out saved);
			if (category != null)
			{
				_selectedId = category.Id;
				_creating = false;
				_picking = false;
				_buffersFor = null;
				Say(saved ? Text.Created(category.DisplayName(Text.Spanish)) : Text.SaveFailed);
			}
		}

		private void DrawCategoryDetail(Category category, float width, float height)
		{
			//IL_0096: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: 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_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			if (_buffersFor != category.Id)
			{
				_nameEs = category.NameEs ?? string.Empty;
				_nameEn = category.NameEn ?? string.Empty;
				_buffersFor = category.Id;
				_confirmDelete = false;
			}
			float num = (width - 12f) / 2f;
			HudStyle.Shadowed(new Rect(2f, 0f, num, 20f), Text.NameSpanish, HudStyle.Small);
			HudStyle.Shadowed(new Rect(num + 14f, 0f, num, 20f), Text.NameEnglish, HudStyle.Small);
			_nameEs = GUI.TextField(new Rect(0f, 22f, num, 34f), _nameEs, 40, HudStyle.Field);
			_nameEn = GUI.TextField(new Rect(num + 12f, 22f, num, 34f), _nameEn, 40, HudStyle.Field);
			if (GUI.Button(new Rect(0f, 64f, 180f, 32f), Text.SaveNames, HudStyle.Button))
			{
				bool flag = Categories.Rename(category, _nameEs, _nameEn);
				_buffersFor = null;
				Say(flag ? Text.Saved : Text.SaveFailed);
			}
			DrawSwatches(category, width);
			HudStyle.Shadowed(new Rect(2f, 106f, width - 200f, 30f), Text.ItemsHeader(CountRules(category)), HudStyle.Heading);
			if (GUI.Button(new Rect(width - 190f, 104f, 190f, 34f), Text.AddItems, HudStyle.Button))
			{
				_picking = true;
				_focusSearch = true;
				_scrollItems = Vector2.zero;
			}
			RefreshRules(category);
			Rect view = default(Rect);
			((Rect)(ref view))..ctor(0f, 146f, width, height - 146f - 48f);
			if (_rules.Count == 0)
			{
				HudStyle.Shadowed(new Rect(((Rect)(ref view)).x + 2f, ((Rect)(ref view)).y, ((Rect)(ref view)).width, 40f), Text.EmptyCategory, HudStyle.Small);
			}
			else
			{
				VirtualRows(view, ref _scrollRules, _rules.Count, 44f, delegate(int i, Rect rect)
				{
					//IL_001d: Unknown result type (might be due to invalid IL or missing references)
					DrawRuleRow(category, _rules[i], rect);
				});
			}
			DrawDelete(category, height);
		}

		private void DrawSwatches(Category category, float width)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			string[] palette = CategoryEditor.Palette;
			float num = width - (float)palette.Length * 31f + 5f;
			Rect val = default(Rect);
			Color color = default(Color);
			for (int i = 0; i < palette.Length; i++)
			{
				((Rect)(ref val))..ctor(num + (float)i * 31f, 67f, 26f, 26f);
				ColorUtility.TryParseHtmlString(palette[i], ref color);
				if (string.Equals(category.Color, palette[i], StringComparison.OrdinalIgnoreCase))
				{
					HudStyle.Border(new Rect(((Rect)(ref val)).x - 3f, ((Rect)(ref val)).y - 3f, ((Rect)(ref val)).width + 6f, ((Rect)(ref val)).height + 6f), HudStyle.GoldBright);
				}
				HudStyle.Fill(val, Categories.Tame(color));
				HudStyle.Border(val, new Color(0.07f, 0.045f, 0.03f, 0.95f));
				HudStyle.Fill(new Rect(((Rect)(ref val)).x + 2f, ((Rect)(ref val)).y + 2f, ((Rect)(ref val)).width - 4f, 3f), new Color(1f, 1f, 1f, 0.2f));
				if (GUI.Button(val, GUIContent.none, HudStyle.Row))
				{
					Say(Categories.SetColor(category, palette[i]) ? Text.Saved : Text.SaveFailed);
				}
			}
		}

		private void DrawRuleRow(Category category, RuleRow row, Rect rect)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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)
			GUI.Box(rect, GUIContent.none, HudStyle.Slot);
			float num = ((Rect)(ref rect)).x + 14f;
			if (row.Kind == 0)
			{
				HudStyle.SlotIcon(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).height - 4f, ((Rect)(ref rect)).height - 4f), row.Icon);
				num = ((Rect)(ref rect)).x + ((Rect)(ref rect)).height + 8f;
			}
			float num2 = ((Rect)(ref rect)).width - num - 54f;
			if (row.Sub != null)
			{
				HudStyle.Shadowed(new Rect(num, ((Rect)(ref rect)).y + 2f, num2, 22f), row.Title, HudStyle.Label);
				HudStyle.Shadowed(new Rect(num, ((Rect)(ref rect)).y + 22f, num2, 18f), row.Sub, HudStyle.Small);
			}
			else
			{
				HudStyle.Shadowed(new Rect(num, ((Rect)(ref rect)).y, num2, ((Rect)(ref rect)).height), row.Title, HudStyle.Label);
			}
			if (GUI.Button(new Rect(((Rect)(ref rect)).xMax - 46f, ((Rect)(ref rect)).y + 6f, 38f, ((Rect)(ref rect)).height - 12f), "X", HudStyle.Button))
			{
				bool flag = ((row.Kind == 0) ? Categories.UnassignItem(category, row.Key) : ((row.Kind == 1) ? Categories.RemovePattern(category, row.Key) : Categories.RemoveType(category, row.Key)));
				Say(flag ? Text.Removed(row.Title) : Text.SaveFailed);
			}
		}

		private void DrawDelete(Category category, float height)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			string text = (_confirmDelete ? Text.ConfirmDelete : Text.DeleteCategory);
			if (GUI.Button(new Rect(0f, height - 42f, 220f, 36f), text, HudStyle.Button))
			{
				if (!_confirmDelete)
				{
					_confirmDelete = true;
					return;
				}
				string category2 = category.DisplayName(Text.Spanish);
				bool flag = Categories.Delete(category);
				_selectedId = ((Categories.All.Count > 0) ? Categories.All[0].Id : null);
				_confirmDelete = false;
				_buffersFor = null;
				Say(flag ? Text.Deleted(category2) : Text.SaveFailed);
			}
		}

		private static int CountRules(Category category)
		{
			return category.Items.Count + category.Patterns.Count + category.Types.Count;
		}

		private void RefreshRules(Category category)
		{
			string language = Text.Language;
			if (_rulesFor == category.Id && _rulesVersion == Categories.Version && _rulesLanguage == language && _rulesGeneration == ItemCatalog.Generation)
			{
				return;
			}
			_rules.Clear();
			foreach (string item in category.Items)
			{
				CatalogEntry catalogEntry = ItemCatalog.Find(item);
				_rules.Add(new RuleRow
				{
					Title = ((catalogEntry != null) ? catalogEntry.Display : item),
					Sub = item,
					Icon = catalogEntry?.Icon,
					Kind = 0,
					Key = item
				});
			}
			_rules.Sort((RuleRow a, RuleRow b) => string.Compare(a.Title, b.Title, StringComparison.CurrentCultureIgnoreCase));
			foreach (string pattern in category.Patterns)
			{
				_rules.Add(new RuleRow
				{
					Title = Text.RuleName(pattern),
					Kind = 1,
					Key = pattern
				});
			}
			foreach (string type in category.Types)
			{
				_rules.Add(new RuleRow
				{
					Title = Text.RuleType(type),
					Kind = 2,
					Key = type
				});
			}
			_rulesFor = category.Id;
			_rulesVersion = Categories.Version;
			_rulesLanguage = language;
			_rulesGeneration = ItemCatalog.Generation;
		}

		private void DrawItemPicker(Category category, float width, float height)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			GUI.SetNextControlName("search");
			_search = GUI.TextField(new Rect(0f, 0f, width - 334f, 34f), _search, 60, HudStyle.Field);
			if (_focusSearch)
			{
				GUI.FocusControl("search");
				_focusSearch = false;
			}
			string[] array = new string[3]
			{
				Text.FilterAll,
				Text.FilterInventory,
				Text.FilterNone
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (GUI.Button(new Rect(width - 326f + (float)i * 110f, 0f, 106f, 34f), array[i], (_filter == i) ? HudStyle.TabOn : HudStyle.Button))
				{
					_filter = i;
					_scrollItems = Vector2.zero;
				}
			}
			HudStyle.Shadowed(new Rect(2f, 38f, width, 22f), Text.SearchHint, HudStyle.Small);
			RefreshFiltered(category);
			Rect view = default(Rect);
			((Rect)(ref view))..ctor(0f, 66f, width, height - 66f - 48f);
			if (_filtered.Count == 0)
			{
				HudStyle.Shadowed(new Rect(((Rect)(ref view)).x + 2f, ((Rect)(ref view)).y, ((Rect)(ref view)).width, 30f), Text.NoResults, HudStyle.Small);
			}
			else
			{
				VirtualRows(view, ref _scrollItems, _filtered.Count, 46f, delegate(int index, Rect rect)
				{
					//IL_001d: Unknown result type (might be due to invalid IL or missing references)
					DrawItemRow(category, _filtered[index], rect);
				});
			}
			if (GUI.Button(new Rect(0f, height - 42f, 160f, 36f), Text.Done, HudStyle.Button))
			{
				_picking = false;
			}
		}

		private void DrawItemRow(Category category, CatalogEntry entry, Rect rect)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			bool flag = entry.Category == category;
			GUI.Box(rect, GUIContent.none, flag ? HudStyle.SlotOn : HudStyle.Slot);
			HudStyle.SlotIcon(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).height - 4f, ((Rect)(ref rect)).height - 4f), entry.Icon);
			float num = ((Rect)(ref rect)).x + ((Rect)(ref rect)).height + 8f;
			float num2 = ((Rect)(ref rect)).width * 0.5f;
			HudStyle.Shadowed(new Rect(num, ((Rect)(ref rect)).y + 3f, num2 - num, 22f), entry.Display, HudStyle.Label);
			HudStyle.Shadowed(new Rect(num, ((Rect)(ref rect)).y + 24f, num2 - num, 18f), entry.Prefab, HudStyle.Small);
			Rect rect2 = default(Rect);
			((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x + num2, ((Rect)(ref rect)).y, num2 - 14f, ((Rect)(ref rect)).height);
			Color contentColor = GUI.contentColor;
			if (flag)
			{
				GUI.contentColor = HudStyle.GoldBright;
				HudStyle.Shadowed(rect2, entry.Exact ? Text.InThisCategory : Text.InThisCategoryByRule, HudStyle.PlainRight);
			}
			else if (entry.Category != null)
			{
				GUI.contentColor = Categories.ColorOf(entry.Category, HudStyle.Dim);
				HudStyle.Shadowed(rect2, entry.Category.DisplayName(Text.Spanish), HudStyle.PlainRight);
			}
			GUI.contentColor = contentColor;
			if (GUI.Button(rect, GUIContent.none, HudStyle.Row))
			{
				ToggleItem(category, entry);
			}
		}

		private void ToggleItem(Category category, CatalogEntry entry)
		{
			if (entry.Category == category && !entry.Exact)
			{
				Say(Text.ByRuleHint);
				return;
			}
			if (entry.Category == category)
			{
				Say(Categories.UnassignItem(category, entry.Prefab) ? Text.Removed(entry.Display) : Text.SaveFailed);
				return;
			}
			bool flag = Categories.AssignItem(category, entry.Prefab);
			Say(flag ? Text.Assigned(entry.Display, category.DisplayName(Text.Spanish)) : Text.SaveFailed);
		}

		private void RefreshFiltered(Category category)
		{
			if (_filter == 1 && Time.unscaledTime - _inventoryRead > 1f)
			{
				ReadInventory();
			}
			if (_filteredSearch == _search && _filteredFilter == _filter && _filteredVersion == Categories.Version && _filteredFor == category.Id && _filteredInventory == _inventoryStamp && _filteredGeneration == ItemCatalog.Generation)
			{
				return;
			}
			_filtered.Clear();
			string[] array = _search.ToLowerInvariant().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (CatalogEntry item in ItemCatalog.All)
			{
				if ((_filter == 1 && !_inventory.Contains(item.Prefab)) || (_filter == 2 && item.Category != null))
				{
					continue;
				}
				bool flag = true;
				string[] array2 = array;
				foreach (string value in array2)
				{
					if (!item.Search.Contains(value))
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					_filtered.Add(item);
				}
			}
			_filteredSearch = _search;
			_filteredFilter = _filter;
			_filteredVersion = Categories.Version;
			_filteredFor = category.Id;
			_filteredInventory = _inventoryStamp;
			_filteredGeneration = ItemCatalog.Generation;
		}

		private void ReadInventory()
		{
			_inventoryRead = Time.unscaledTime;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems())
			{
				if ((Object)(object)allItem?.m_dropPrefab != (Object)null)
				{
					hashSet.Add(((Object)allItem.m_dropPrefab).name);
				}
			}
			if (!hashSet.SetEquals(_inventory))
			{
				_inventory.Clear();
				_inventory.UnionWith(hashSet);
				_inventoryStamp++;
			}
		}

		private static void VirtualRows(Rect view, ref Vector2 scroll, int count, float rowHeight, Action<int, Rect> drawRow)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref view)).width - 16f, (float)count * rowHeight);
			scroll = GUI.BeginScrollView(view, scroll, val);
			int num = Mathf.Clamp(Mathf.FloorToInt(scroll.y / rowHeight), 0, Mathf.Max(0, count - 1));
			int num2 = Mathf.Clamp(Mathf.CeilToInt((scroll.y + ((Rect)(ref view)).height) / rowHeight), 0, count - 1);
			for (int i = num; i <= num2; i++)
			{
				drawRow(i, new Rect(0f, (float)i * rowHeight, ((Rect)(ref val)).width, rowHeight - 4f));
			}
			GUI.EndScrollView();
		}
	}
	internal sealed class NameSet : IEnumerable<string>, IEnumerable
	{
		private readonly List<string> _order = new List<string>();

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

		internal int Count => _order.Count;

		internal bool Add(string name)
		{
			if (!_lookup.Add(name))
			{
				return false;
			}
			_order.Add(name);
			return true;
		}

		internal bool Remove(string name)
		{
			if (!_lookup.Remove(name))
			{
				return false;
			}
			_order.RemoveAll((string existing) => string.Equals(existing, name, StringComparison.OrdinalIgnoreCase));
			return true;
		}

		internal bool Contains(string name)
		{
			return _lookup.Contains(name);
		}

		public IEnumerator<string> GetEnumerator()
		{
			return _order.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
	internal sealed class Category
	{
		internal string Id;

		internal string NameEn;

		internal string NameEs;

		internal string Color;

		internal readonly NameSet Items = new NameSet();

		internal readonly NameSet Patterns = new NameSet();

		internal readonly NameSet Types = new NameSet();

		internal string DisplayName(bool spanish)
		{
			string text = (spanish ? (NameEs ?? NameEn) : (NameEn ?? NameEs));
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return Id;
		}

		internal bool MatchesExact(string prefab, string shared)
		{
			if (!string.IsNullOrEmpty(prefab) && Items.Contains(prefab))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(shared) && shared[0] == '$')
			{
				return Items.Contains(shared);
			}
			return false;
		}

		internal bool MatchesPattern(string prefab)
		{
			if (string.IsNullOrEmpty(prefab))
			{
				return false;
			}
			foreach (string pattern in Patterns)
			{
				if ((pattern[pattern.Length - 1] == '*') ? prefab.StartsWith(pattern.Substring(0, pattern.Length - 1), StringComparison.OrdinalIgnoreCase) : prefab.EndsWith(pattern.Substring(1), StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		internal bool MatchesType(string itemType, bool isFood, bool isPotion)
		{
			if (!string.IsNullOrEmpty(itemType) && Types.Contains(itemType))
			{
				return true;
			}
			if (!isFood || !Types.Contains("Food"))
			{
				if (isPotion)
				{
					return Types.Contains("Potion");
				}
				return false;
			}
			return true;
		}
	}
	internal static class CategoryFile
	{
		internal static List<Category> Parse(IEnumerable<string> lines, List<string> problems)
		{
			List<Category> result = new List<Category>();
			HashSet<string> ids = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			Category category = null;
			int num = 0;
			foreach (string line in lines)
			{
				num++;
				string text = line.Trim();
				if (text.Length == 0 || text[0] == '#' || text[0] == ';')
				{
					continue;
				}
				if (text[0] == '[')
				{
					category = StartCategory(text, num, ids, result, problems);
					continue;
				}
				int num2 = text.IndexOf('=');
				if (category == null || num2 <= 0)
				{
					problems.Add(string.Format("line {0}: ignored ({1}): {2}", num, (category == null) ? "outside a [category]" : "expected key = value", text));
					continue;
				}
				string key = text.Substring(0, num2).Trim().ToLowerInvariant();
				string value = text.Substring(num2 + 1).Trim();
				ApplyKey(category, key, value, num, problems);
			}
			return result;
		}

		internal static Category Classify(IReadOnlyList<Category> categories, string prefab, string shared, string itemType, bool isFood, bool isPotion)
		{
			for (int i = 0; i < categories.Count; i++)
			{
				if (categories[i].MatchesExact(prefab, shared))
				{
					return categories[i];
				}
			}
			for (int j = 0; j < categories.Count; j++)
			{
				if (categories[j].MatchesPattern(prefab))
				{
					return categories[j];
				}
			}
			for (int k = 0; k < categories.Count; k++)
			{
				if (categories[k].MatchesType(itemType, isFood, isPotion))
				{
					return categories[k];
				}
			}
			return null;
		}

		internal static string Serialize(IReadOnlyList<Category> categories)
		{
			return "# ChestCategories - categorías de cofres / chest categories\n#\n# Puedes editar este archivo a mano o desde el HUD del juego (tecla F7). Cuando guardas desde el HUD, el mod\n# reescribe el archivo entero: tus categorías se conservan y estos comentarios se vuelven a generar.\n# Edit this file by hand or from the in-game HUD (F7 key). Saving from the HUD rewrites the whole file:\n# your categories are kept and these comments are regenerated.\n#\n# Cada bloque [id] es una categoría. Si lo editas a mano, el mod recarga el archivo al abrir el HUD o pulsar F8.\n#\n# Claves / keys:\n#   es / en = nombre en español / name in English\n#   color   = color en el HUD, #RRGGBB\n#   item    = nombre interno del objeto (el de la consola: spawn Wood), separados por comas.\n#             Admite un * al principio o al final:  Mead*   *Uncooked\n#             También acepta el nombre traducible del juego:  $item_wood\n#   type    = tipo de objeto del juego: Material, Consumable, OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft,\n#             Bow, Shield, Helmet, Chest, Legs, Shoulder, Hands, Ammo, AmmoNonEquipable, Trophy, Torch, Tool, Utility,\n#             Trinket, Fish, Misc, Customization ... y dos tipos derivados:\n#             Food   = consumible que da vida, aguante o eitr\n#             Potion = consumible que no es comida (pociones, hidromieles)\n#\n# Prioridad, de más a menos específica: 1) nombre exacto (Wood, $item_wood), 2) nombre con * (Mead*), 3) type.\n# Dentro de cada nivel gana la primera categoría del archivo. Por eso un objeto que asignas a mano desde el HUD\n# (nombre exacto) siempre gana a las reglas por tipo de cualquier categoría.\n# Un cambio de id (lo que va entre corchetes) desvincula los cofres que ya lo tenían asignado.\n\n" + SerializeBody(categories);
		}

		internal static string SerializeBody(IReadOnlyList<Category> categories)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (Category category in categories)
			{
				stringBuilder.Append('[').Append(category.Id).Append("]\n");
				AppendValue(stringBuilder, "es", category.NameEs);
				AppendValue(stringBuilder, "en", category.NameEn);
				AppendValue(stringBuilder, "color", category.Color);
				if (category.Types.Count > 0)
				{
					stringBuilder.Append("type = ").Append(string.Join(", ", category.Types)).Append('\n');
				}
				List<string> list = new List<string>(category.Items);
				list.AddRange(category.Patterns);
				for (int i = 0; i < list.Count; i += 8)
				{
					int count = Math.Min(8, list.Count - i);
					stringBuilder.Append("item = ").Append(string.Join(", ", list.GetRange(i, count))).Append('\n');
				}
				stringBuilder.Append('\n');
			}
			return stringBuilder.ToString();
		}

		internal static bool IsValidColor(string color)
		{
			if (color == null || color.Length != 7 || color[0] != '#')
			{
				return false;
			}
			for (int i = 1; i < 7; i++)
			{
				if (!Uri.IsHexDigit(color[i]))
				{
					return false;
				}
			}
			return true;
		}

		private static void AppendValue(StringBuilder text, string key, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				text.Append(key).Append(" = ").Append(value)
					.Append('\n');
			}
		}

		private static Category StartCategory(string line, int number, HashSet<string> ids, List<Category> result, List<string> problems)
		{
			int num = line.IndexOf(']');
			if (num < 0)
			{
				problems.Add($"line {number}: missing ']': {line}");
				return null;
			}
			string text = line.Substring(1, num - 1).Trim().ToLowerInvariant();
			if (!IsValidId(text))
			{
				problems.Add($"line {number}: invalid category id '{text}' (use a-z, 0-9 and _, up to 32 characters)");
				return null;
			}
			if (!ids.Add(text))
			{
				problems.Add($"line {number}: category '{text}' is defined twice; the second block is ignored");
				return null;
			}
			Category category = new Category
			{
				Id = text
			};
			result.Add(category);
			return category;
		}

		private static void ApplyKey(Category category, string key, string value, int number, List<string> problems)
		{
			switch (key)
			{
			case "en":
				category.NameEn = value;
				break;
			case "es":
				category.NameEs = value;
				break;
			case "color":
				if (IsValidColor(value))
				{
					category.Color = value.ToUpperInvariant();
				}
				else
				{
					problems.Add($"line {number}: color '{value}' ignored (use #RRGGBB)");
				}
				break;
			case "items":
			case "item":
			{
				foreach (string item in Split(value))
				{
					AddItem(category, item, number, problems);
				}
				break;
			}
			case "types":
			case "type":
			{
				foreach (string item2 in Split(value))
				{
					category.Types.Add(item2);
				}
				break;
			}
			default:
				problems.Add($"line {number}: unknown key '{key}' (use es, en, color, item, type)");
				break;
			}
		}

		private static void AddItem(Category category, string entry, int number, List<string> problems)
		{
			int num = 0;
			for (int i = 0; i < entry.Length; i++)
			{
				if (entry[i] == '*')
				{
					num++;
				}
			}
			switch (num)
			{
			case 0:
				category.Items.Add(entry);
				return;
			case 1:
				if (entry.Length > 1 && (entry[0] == '*' || entry[entry.Length - 1] == '*'))
				{
					category.Patterns.Add(entry);
					return;
				}
				break;
			}
			problems.Add($"line {number}: '{entry}' ignored (a * is only allowed at the start or the end)");
		}

		private static IEnumerable<string> Split(string value)
		{
			string[] array = value.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					yield return text;
				}
			}
		}

		private static bool IsValidId(string id)
		{
			if (id.Length == 0 || id.Length > 32)
			{
				return false;
			}
			foreach (char c in id)
			{
				if ((c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '_')
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class CategoryEditor
	{
		internal const int MaxNameLength = 40;

		internal static readonly string[] Palette = new string[8] { "#C8864A", "#8DB4CC", "#E0BE4A", "#5FAE6E", "#C0524E", "#9879C4", "#5C86C4", "#C97BA6" };

		internal static string CleanName(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(raw.Length);
			bool flag = false;
			foreach (char c in raw)
			{
				if (char.IsControl(c) || char.IsWhiteSpace(c))
				{
					flag = stringBuilder.Length > 0;
					continue;
				}
				if (flag)
				{
					stringBuilder.Append(' ');
					flag = false;
				}
				stringBuilder.Append(c);
			}
			string text = stringBuilder.ToString();
			if (text.Length <= 40)
			{
				return text;
			}
			return text.Substring(0, 40).TrimEnd();
		}

		internal static string MakeId(string name, IEnumerable<string> existing)
		{
			HashSet<string> hashSet = new HashSet<string>(existing, StringComparer.OrdinalIgnoreCase);
			StringBuilder stringBuilder = new StringBuilder();
			string text = (name ?? string.Empty).Normalize(NormalizationForm.FormD);
			foreach (char c in text)
			{
				if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
				{
					char c2 = char.ToLowerInvariant(c);
					if ((c2 >= 'a' && c2 <= 'z') || (c2 >= '0' && c2 <= '9'))
					{
						stringBuilder.Append(c2);
					}
					else if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '_')
					{
						stringBuilder.Append('_');
					}
				}
			}
			string text2 = stringBuilder.ToString().Trim('_');
			if (text2.Length == 0)
			{
				text2 = "category";
			}
			if (text2.Length > 28)
			{
				text2 = text2.Substring(0, 28).TrimEnd('_');
			}
			string text3 = text2;
			int num = 2;
			while (hashSet.Contains(text3))
			{
				text3 = text2 + "_" + num.ToString(CultureInfo.InvariantCulture);
				num++;
			}
			return text3;
		}

		internal static string NextColor(IReadOnlyList<Category> categories)
		{
			string[] palette = Palette;
			foreach (string text in palette)
			{
				bool flag = false;
				foreach (Category category in categories)
				{
					if (string.Equals(category.Color, text, StringComparison.OrdinalIgnoreCase))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					return text;
				}
			}
			return Palette[categories.Count % Palette.Length];
		}

		internal static Category Add(List<Category> categories, string name)
		{
			string text = CleanName(name);
			if (text.Length == 0)
			{
				return null;
			}
			List<string> list = new List<string>();
			foreach (Category category2 in categories)
			{
				list.Add(category2.Id);
			}
			Category category = new Category
			{
				Id = MakeId(text, list),
				NameEs = text,
				NameEn = text,
				Color = NextColor(categories)
			};
			categories.Add(category);
			return category;
		}

		internal static bool Remove(List<Category> categories, string id)
		{
			return categories.RemoveAll((Category c) => string.Equals(c.Id, id, StringComparison.OrdinalIgnoreCase)) > 0;
		}

		internal static bool Rename(Category category, string spanish, string english)
		{
			string text = CleanName(spanish);
			string text2 = CleanName(english);
			bool result = false;
			if (text.Length > 0 && text != category.NameEs)
			{
				category.NameEs = text;
				result = true;
			}
			if (text2.Length > 0 && text2 != category.NameEn)
			{
				category.NameEn = text2;
				result = true;
			}
			return result;
		}

		internal static void Assign(IReadOnlyList<Category> categories, Category target, string name)
		{
			foreach (Category category in categories)
			{
				category.Items.Remove(name);
			}
			target.Items.Add(name);
		}

		internal static bool Unassign(Category category, string name)
		{
			return category.Items.Remove(name);
		}
	}
	internal static class CategoryDefaults
	{
		internal const string FileName = "despr0grammer.ChestCategories.categories.txt";

		internal const string Header = "# ChestCategories - categorías de cofres / chest categories\n#\n# Puedes editar este archivo a mano o desde el HUD del juego (tecla F7). Cuando guardas desde el HUD, el mod\n# reescribe el archivo entero: tus categorías se conservan y estos comentarios se vuelven a generar.\n# Edit this file by hand or from the in-game HUD (F7 key). Saving from the HUD rewrites the whole file:\n# your categories are kept and these comments are regenerated.\n#\n# Cada bloque [id] es una categoría. Si lo editas a mano, el mod recarga el archivo al abrir el HUD o pulsar F8.\n#\n# Claves / keys:\n#   es / en = nombre en español / name in English\n#   color   = color en el HUD, #RRGGBB\n#   item    = nombre interno del objeto (el de la consola: spawn Wood), separados por comas.\n#             Admite un * al principio o al final:  Mead*   *Uncooked\n#             También acepta el nombre traducible del juego:  $item_wood\n#   type    = tipo de objeto del juego: Material, Consumable, OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft,\n#             Bow, Shield, Helmet, Chest, Legs, Shoulder, Hands, Ammo, AmmoNonEquipable, Trophy, Torch, Tool, Utility,\n#             Trinket, Fish, Misc, Customization ... y dos tipos derivados:\n#             Food   = consumible que da vida, aguante o eitr\n#             Potion = consumible que no es comida (pociones, hidromieles)\n#\n# Prioridad, de más a menos específica: 1) nombre exacto (Wood, $item_wood), 2) nombre con * (Mead*), 3) type.\n# Dentro de cada nivel gana la primera categoría del archivo. Por eso un objeto que asignas a mano desde el HUD\n# (nombre exacto) siempre gana a las reglas por tipo de cualquier categoría.\n# Un cambio de id (lo que va entre corchetes) desvincula los cofres que ya lo tenían asignado.\n\n";

		internal const string Body = "[wood]\nes = Madera\nen = Wood\ncolor = #C8864A\nitem = Wood, FineWood, RoundLog, ElderBark, YggdrasilWood, Blackwood, Frostwood\n\n[metals]\nes = Metales\nen = Metals\ncolor = #8DB4CC\nitem = Copper, Tin, Bronze, Iron, Silver, BlackMetal, Gold, Flametal*\nitem = CopperOre, TinOre, IronOre, IronScrap, SilverOre, BlackMetalScrap, GoldOre\nitem = BronzeNails, IronNails, Chain\n\n[stone]\nes = Piedras\nen = Stone\ncolor = #A5A9B8\nitem = Stone, Flint, Obsidian, Blackmarble, Grausten, Crystal\n\n[valuables]\nes = Tesoros\nen = Valuables\ncolor = #E0BE4A\nitem = Coins, Amber, AmberPearl, Ruby, SilverNecklace, GemstoneRed, GemstoneBlue, GemstoneGreen\n\n[hides]\nes = Pieles y cuero\nen = Hides and leather\ncolor = #B8895C\nitem = LeatherScraps, DeerHide, TrollHide, WolfPelt, LoxPelt, ScaleHide, MooseHide, SealHide, Leatherstraps\n\n[farming]\nes = Cultivo y semillas\nen = Farming and seeds\ncolor = #8FB24F\nitem = *Seeds, Acorn, FirCone, PineCone, Barley, Flax\n\n[vegetables]\nes = Vegetales y bayas\nen = Vegetables and berries\ncolor = #5FAE6E\nitem = Carrot, Turnip, Onion, Kale, Fiddleheadfern\nitem = Cloudberry, Raspberry, Blueberries, Pukeberries, Lingonberry, Vineberry\nitem = Mushroom, MushroomBlue, MushroomYellow, MushroomJotunPuffs, MushroomMagecap, MushroomSmokePuff\n\n[meats]\nes = Carnes\nen = Meats\ncolor = #C0524E\ntype = Fish\nitem = RawMeat, DeerMeat, WolfMeat, LoxMeat, SerpentMeat, BugMeat, MooseMeat, ChickenMeat, HareMeat\nitem = BjornMeat, BoneMawSerpentMeat, VoltureMeat, AsksvinMeat, NeckTail, FishRaw, RottenMeat\nitem = Cooked*, *MeatCooked, FishCooked, NeckTailGrilled\n\n[meals]\nes = Comidas preparadas\nen = Prepared meals\ncolor = #D99A3A\ntype = Food\nitem = Feast*, KaleChipsUncooked\n\n[cooking]\nes = Materiales para comida\nen = Cooking ingredients\ncolor = #C97BA6\nitem = BarleyFlour, BreadDough, Honey, RoyalJelly, ChickenEgg, Oat, Entrails, Bloodbag\nitem = sap, Dandelion, Thistle, SpiceDeepNorth, SealBlubber, GlowWorm\n\n[tools]\nes = Herramientas\nen = Tools\ncolor = #52AFA0\ntype = Tool, Torch\nitem = Pickaxe*, FishingRod*, FishingBait\n\n[potions]\nes = Pociones e hidromieles\nen = Potions and meads\ncolor = #9879C4\ntype = Potion\nitem = Mead*, BarleyWine*\n\n[weapons]\nes = Armas y escudos\nen = Weapons and shields\ncolor = #B83F4A\ntype = OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft, Bow, Attach_Atgeir, Shield\n\n[armor]\nes = Armaduras\nen = Armor\ncolor = #5C86C4\ntype = Helmet, Chest, Legs, Shoulder, Hands\n\n[trinkets]\nes = Accesorios\nen = Accessories\ncolor = #D6B04A\ntype = Utility, Trinket\n\n[ammo]\nes = Munición\nen = Ammunition\ncolor = #C98A4E\ntype = Ammo, AmmoNonEquipable\n\n[trophies]\nes = Trofeos\nen = Trophies\ncolor = #CFCBE0\ntype = Trophy\n\n[drops]\nes = Botín de criaturas\nen = Creature drops\ncolor = #98A844\nitem = Bilebag, Ooze, OozeMork, Guck, BoneFragments, WolfFang, WolfClaw, WolfHairBundle\nitem = Mandible, Chitin, Carapace, Needle, Softtissue, HardAntler, Feathers, GreydwarfEye, FreezeGland\nitem = GiantBloodSack, SerpentScale, DragonTear, Root, MoleClaws, MooseSinew, ElakingHairBundle, WrithanRoots\n\n[other]\nes = Materiales varios\nen = Other materials\ncolor = #8E8A80\ntype = Material, Misc, Customization\n";

		internal const string Text = "# ChestCategories - categorías de cofres / chest categories\n#\n# Puedes editar este archivo a mano o desde el HUD del juego (tecla F7). Cuando guardas desde el HUD, el mod\n# reescribe el archivo entero: tus categorías se conservan y estos comentarios se vuelven a generar.\n# Edit this file by hand or from the in-game HUD (F7 key). Saving from the HUD rewrites the whole file:\n# your categories are kept and these comments are regenerated.\n#\n# Cada bloque [id] es una categoría. Si lo editas a mano, el mod recarga el archivo al abrir el HUD o pulsar F8.\n#\n# Claves / keys:\n#   es / en = nombre en español / name in English\n#   color   = color en el HUD, #RRGGBB\n#   item    = nombre interno del objeto (el de la consola: spawn Wood), separados por comas.\n#             Admite un * al principio o al final:  Mead*   *Uncooked\n#             También acepta el nombre traducible del juego:  $item_wood\n#   type    = tipo de objeto del juego: Material, Consumable, OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft,\n#             Bow, Shield, Helmet, Chest, Legs, Shoulder, Hands, Ammo, AmmoNonEquipable, Trophy, Torch, Tool, Utility,\n#             Trinket, Fish, Misc, Customization ... y dos tipos derivados:\n#             Food   = consumible que da vida, aguante o eitr\n#             Potion = consumible que no es comida (pociones, hidromieles)\n#\n# Prioridad, de más a menos específica: 1) nombre exacto (Wood, $item_wood), 2) nombre con * (Mead*), 3) type.\n# Dentro de cada nivel gana la primera categoría del archivo. Por eso un objeto que asignas a mano desde el HUD\n# (nombre exacto) siempre gana a las reglas por tipo de cualquier categoría.\n# Un cambio de id (lo que va entre corchetes) desvincula los cofres que ya lo tenían asignado.\n\n[wood]\nes = Madera\nen = Wood\ncolor = #C8864A\nitem = Wood, FineWood, RoundLog, ElderBark, YggdrasilWood, Blackwood, Frostwood\n\n[metals]\nes = Metales\nen = Metals\ncolor = #8DB4CC\nitem = Copper, Tin, Bronze, Iron, Silver, BlackMetal, Gold, Flametal*\nitem = CopperOre, TinOre, IronOre, IronScrap, SilverOre, BlackMetalScrap, GoldOre\nitem = BronzeNails, IronNails, Chain\n\n[stone]\nes = Piedras\nen = Stone\ncolor = #A5A9B8\nitem = Stone, Flint, Obsidian, Blackmarble, Grausten, Crystal\n\n[valuables]\nes = Tesoros\nen = Valuables\ncolor = #E0BE4A\nitem = Coins, Amber, AmberPearl, Ruby, SilverNecklace, GemstoneRed, GemstoneBlue, GemstoneGreen\n\n[hides]\nes = Pieles y cuero\nen = Hides and leather\ncolor = #B8895C\nitem = LeatherScraps, DeerHide, TrollHide, WolfPelt, LoxPelt, ScaleHide, MooseHide, SealHide, Leatherstraps\n\n[farming]\nes = Cultivo y semillas\nen = Farming and seeds\ncolor = #8FB24F\nitem = *Seeds, Acorn, FirCone, PineCone, Barley, Flax\n\n[vegetables]\nes = Vegetales y bayas\nen = Vegetables and berries\ncolor = #5FAE6E\nitem = Carrot, Turnip, Onion, Kale, Fiddleheadfern\nitem = Cloudberry, Raspberry, Blueberries, Pukeberries, Lingonberry, Vineberry\nitem = Mushroom, MushroomBlue, MushroomYellow, MushroomJotunPuffs, MushroomMagecap, MushroomSmokePuff\n\n[meats]\nes = Carnes\nen = Meats\ncolor = #C0524E\ntype = Fish\nitem = RawMeat, DeerMeat, WolfMeat, LoxMeat, SerpentMeat, BugMeat, MooseMeat, ChickenMeat, HareMeat\nitem = BjornMeat, BoneMawSerpentMeat, VoltureMeat, AsksvinMeat, NeckTail, FishRaw, RottenMeat\nitem = Cooked*, *MeatCooked, FishCooked, NeckTailGrilled\n\n[meals]\nes = Comidas preparadas\nen = Prepared meals\ncolor = #D99A3A\ntype = Food\nitem = Feast*, KaleChipsUncooked\n\n[cooking]\nes = Materiales para comida\nen = Cooking ingredients\ncolor = #C97BA6\nitem = BarleyFlour, BreadDough, Honey, RoyalJelly, ChickenEgg, Oat, Entrails, Bloodbag\nitem = sap, Dandelion, Thistle, SpiceDeepNorth, SealBlubber, GlowWorm\n\n[tools]\nes = Herramientas\nen = Tools\ncolor = #52AFA0\ntype = Tool, Torch\nitem = Pickaxe*, FishingRod*, FishingBait\n\n[potions]\nes = Pociones e hidromieles\nen = Potions and meads\ncolor = #9879C4\ntype = Potion\nitem = Mead*, BarleyWine*\n\n[weapons]\nes = Armas y escudos\nen = Weapons and shields\ncolor = #B83F4A\ntype = OneHandedWeapon, TwoHandedWeapon, TwoHandedWeaponLeft, Bow, Attach_Atgeir, Shield\n\n[armor]\nes = Armaduras\nen = Armor\ncolor = #5C86C4\ntype = Helmet, Chest, Legs, Shoulder, Hands\n\n[trinkets]\nes = Accesorios\nen = Accessories\ncolor = #D6B04A\ntype = Utility, Trinket\n\n[ammo]\nes = Munición\nen = Ammunition\ncolor = #C98A4E\ntype = Ammo, AmmoNonEquipable\n\n[trophies]\nes = Trofeos\nen = Trophies\ncolor = #CFCBE0\ntype = Trophy\n\n[drops]\nes = Botín de criaturas\nen = Creature drops\ncolor = #98A844\nitem = Bilebag, Ooze, OozeMork, Guck, BoneFragments, WolfFang, WolfClaw, WolfHairBundle\nitem = Mandible, Chitin, Carapace, Needle, Softtissue, HardAntler, Feathers, GreydwarfEye, FreezeGland\nitem = GiantBloodSack, SerpentScale, DragonTear, Root, MoleClaws, MooseSinew, ElakingHairBundle, WrithanRoots\n\n[other]\nes = Materiales varios\nen = Other materials\ncolor = #8E8A80\ntype = Material, Misc, Customization\n";

		private static readonly string[] PreviousDefaultHashes = new string[1] { "0b90d6ba8756d4ba07116a8b3c69f95aefbe9743ce91e97c065d845a320adf0b" };

		internal static bool IsUntouchedPreviousDefault(string fileText)
		{
			string text = Fingerprint(fileText);
			string[] previousDefaultHashes = PreviousDefaultHashes;
			foreach (string text2 in previousDefaultHashes)
			{
				if (text == text2)
				{
					return true;
				}
			}
			return false;
		}

		internal static string Fingerprint(string text)
		{
			string s = (text ?? string.Empty).TrimStart('\ufeff').Replace("\r\n", "\n");
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(s));
			StringBuilder stringBuilder = new StringBuilder(array.Length * 2);
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder.Append(b.ToString("x2", CultureInfo.InvariantCulture));
			}
			return stringBuilder.ToString();
		}
	}
	internal static class ChestRegistry
	{
		private sealed class Entry
		{
			internal int Id;

			internal Container Container;

			internal Inventory Inventory;

			internal Transform Transform;

			internal ZNetView NetView;

			internal int Storage = -1;
		}

		private struct Candidate
		{
			internal Container Container;

			internal float SqrDistance;
		}

		private static readonly List<Entry> Entries = new List<Entry>(64);

		private static readonly Dictionary<int, Entry> ById = new Dictionary<int, Entry>();

		private static readonly Dictionary<Inventory, Entry> ByInventory = new Dictionary<Inventory, Entry>();

		private static readonly List<Candidate> Scratch = new List<Candidate>(32);

		private static readonly Comparison<Candidate> ByDistance = (Candidate a, Candidate b) => a.SqrDistance.CompareTo(b.SqrDistance);

		internal static void Register(Contai