Decompiled source of GooCosmetics v1.0.1

BepInEx/plugins/GooCosmetics/GooCosmetics.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using GUIFramework;
using HarmonyLib;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;
using Valheim.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GooCosmetics")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+00c009784b5e85fd4acae52dafd6bf559ee16f23")]
[assembly: AssemblyProduct("GooCosmetics")]
[assembly: AssemblyTitle("GooCosmetics")]
[assembly: AssemblyVersion("1.0.1.0")]
namespace GooCosmetics;

internal enum ItemParticleMode
{
	Donor,
	Original,
	None,
	Selected
}
internal sealed class Appearance
{
	internal const string ItemKey = "gnls.GooCosmetics.illusion.v1";

	internal const int MaxLength = 2100;

	internal readonly string Original;

	internal readonly string Donor;

	internal readonly int Variant;

	internal readonly int Quality;

	internal readonly string ParticleDonor;

	internal readonly int ParticleVariant;

	internal readonly int ParticleQuality;

	internal readonly bool PreserveParticles;

	internal readonly bool NoParticles;

	internal readonly bool Hidden;

	internal ItemParticleMode ParticleMode
	{
		get
		{
			if (!NoParticles)
			{
				if (!PreserveParticles)
				{
					if (ParticleDonor == null)
					{
						return ItemParticleMode.Donor;
					}
					return ItemParticleMode.Selected;
				}
				return ItemParticleMode.Original;
			}
			return ItemParticleMode.None;
		}
	}

	internal CollectedAppearance ParticleLook
	{
		get
		{
			if (ParticleDonor != null)
			{
				return new CollectedAppearance(ParticleDonor, ParticleVariant, ParticleQuality);
			}
			return null;
		}
	}

	internal Appearance(string original, string donor, int variant, int quality, bool preserveParticles = false, bool noParticles = false, string particleDonor = null, int particleVariant = 0, int particleQuality = 1)
	{
		Original = original;
		Donor = donor;
		Variant = variant;
		Quality = quality;
		PreserveParticles = preserveParticles;
		NoParticles = noParticles;
		ParticleDonor = particleDonor;
		ParticleVariant = particleVariant;
		ParticleQuality = particleQuality;
	}

	private Appearance(string original)
		: this(original, original, 0, 1, preserveParticles: false, noParticles: true)
	{
		Hidden = true;
	}

	internal static Appearance NoLook(string original)
	{
		return new Appearance(original);
	}

	private static bool ValidName(string name)
	{
		if (string.IsNullOrWhiteSpace(name) || name.Length > 160)
		{
			return false;
		}
		for (int i = 0; i < name.Length; i++)
		{
			if (char.IsControl(name[i]))
			{
				return false;
			}
		}
		return true;
	}

	private static string Encode(string value)
	{
		return Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
	}

	internal string Encode()
	{
		if (Hidden)
		{
			return "4|" + Encode(Original);
		}
		string[] obj = new string[12]
		{
			(ParticleDonor == null) ? "2|" : "3|",
			Encode(Original),
			"|",
			Encode(Donor),
			"|",
			null,
			null,
			null,
			null,
			null,
			null,
			null
		};
		int variant = Variant;
		obj[5] = variant.ToString(CultureInfo.InvariantCulture);
		obj[6] = "|";
		variant = Quality;
		obj[7] = variant.ToString(CultureInfo.InvariantCulture);
		obj[8] = "|";
		obj[9] = (PreserveParticles ? "1" : "0");
		obj[10] = "|";
		obj[11] = (NoParticles ? "1" : "0");
		string text = string.Concat(obj);
		if (ParticleDonor != null)
		{
			string[] obj2 = new string[7]
			{
				text,
				"|",
				Encode(ParticleDonor),
				"|",
				null,
				null,
				null
			};
			variant = ParticleVariant;
			obj2[4] = variant.ToString(CultureInfo.InvariantCulture);
			obj2[5] = "|";
			variant = ParticleQuality;
			obj2[6] = variant.ToString(CultureInfo.InvariantCulture);
			return string.Concat(obj2);
		}
		return text;
	}

	internal static bool TryParse(string text, out Appearance result)
	{
		result = null;
		if (string.IsNullOrEmpty(text) || text.Length > 2100)
		{
			return false;
		}
		string[] array = text.Split(new char[1] { '|' });
		if (array.Length == 2 && array[0] == "4")
		{
			try
			{
				string text2 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(array[1]));
				if (!ValidName(text2))
				{
					return false;
				}
				result = NoLook(text2);
				return true;
			}
			catch (FormatException)
			{
				return false;
			}
			catch (DecoderFallbackException)
			{
				return false;
			}
		}
		bool num = array.Length == 6 && array[0] == "1";
		bool flag = array.Length == 7 && array[0] == "2";
		bool flag2 = array.Length == 10 && array[0] == "3";
		if ((!num && !flag && !flag2) || (array[5] != "0" && array[5] != "1") || ((flag || flag2) && array[6] != "0" && array[6] != "1") || (flag2 && (array[5] != "0" || array[6] != "0")))
		{
			return false;
		}
		if (!int.TryParse(array[3], NumberStyles.None, CultureInfo.InvariantCulture, out var result2) || result2 > 65535 || !int.TryParse(array[4], NumberStyles.None, CultureInfo.InvariantCulture, out var result3) || result3 < 1 || result3 > 65535)
		{
			return false;
		}
		try
		{
			UTF8Encoding uTF8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
			string text3 = uTF8Encoding.GetString(Convert.FromBase64String(array[1]));
			string text4 = uTF8Encoding.GetString(Convert.FromBase64String(array[2]));
			if (!ValidName(text3) || !ValidName(text4))
			{
				return false;
			}
			string text5 = null;
			int result4 = 0;
			int result5 = 1;
			if (flag2)
			{
				text5 = uTF8Encoding.GetString(Convert.FromBase64String(array[7]));
				if (!ValidName(text5) || !int.TryParse(array[8], NumberStyles.None, CultureInfo.InvariantCulture, out result4) || result4 > 65535 || !int.TryParse(array[9], NumberStyles.None, CultureInfo.InvariantCulture, out result5) || result5 < 1 || result5 > 65535)
				{
					return false;
				}
			}
			result = new Appearance(text3, text4, result2, result3, array[5] == "1", flag && array[6] == "1", text5, result4, result5);
			return true;
		}
		catch (FormatException)
		{
			return false;
		}
		catch (DecoderFallbackException)
		{
			return false;
		}
	}
}
internal static class AppearanceCollection
{
	private sealed class State
	{
		internal AppearanceJournal Journal;

		internal bool Writable;

		internal bool Dirty;

		internal AppearanceJournal Granted;

		internal bool GrantedWritable;

		internal bool GrantedDirty;

		internal AppearanceJournal Revoked;

		internal bool RevokedWritable;

		internal bool RevokedDirty;

		internal int Revision;

		internal int ParticleJournalRevision = -1;

		internal int ParticleVisualRevision = -1;

		internal bool HasParticles;

		internal HashSet<string> Unlocked;

		internal int UnlockRevision = -1;

		internal int UnlockVisualRevision = -1;

		internal Dictionary<string, CollectedAppearance> Held;

		internal int HeldRevision = -1;

		internal int HeldVisualRevision = -1;

		internal readonly int Id = ++lastStateId;
	}

	internal sealed class LoadState
	{
		internal string Previous;

		internal string PreviousGranted;

		internal string PreviousRevoked;
	}

	private sealed class Observation
	{
		internal bool Valid;

		internal bool Gear;

		internal int Definitions;

		internal int StateId;

		internal int Revision;

		internal GameObject Prefab;

		internal SharedData Shared;

		internal SharedData Own;

		internal int MaxStack;

		internal int Variant;

		internal int Quality;

		internal int OwnMaxStack;

		internal int OwnType;

		internal string Raw;

		internal readonly List<Classification.Use> Classified = new List<Classification.Use>();

		internal readonly List<string> Normalized = new List<string>();

		internal readonly List<bool> Unscaled = new List<bool>();

		internal void Note(string prefab, bool unscaled)
		{
			if (!Normalized.Contains(prefab))
			{
				Normalized.Add(prefab);
				Unscaled.Add(unscaled);
			}
		}
	}

	private static int lastStateId;

	private static readonly ConditionalWeakTable<Player, State> States = new ConditionalWeakTable<Player, State>();

	private static readonly HashSet<Player> Loading = new HashSet<Player>();

	internal const string GrantedKey = "gnls.GooCosmetics.granted.v1";

	internal const string RevokedKey = "gnls.GooCosmetics.revoked.v1";

	private static readonly ConditionalWeakTable<ItemData, Observation> Observations = new ConditionalWeakTable<ItemData, Observation>();

	private static Observation noting;

	internal static readonly string[] FreeLooks = new string[2] { "HelmetOdin", "CapeOdin" };

	private static readonly Dictionary<string, string> Companions = new Dictionary<string, string>(StringComparer.Ordinal) { { "SwordIron", "SwordIronFire" } };

	private static readonly Dictionary<string, CollectedAppearance> NoneHeld = new Dictionary<string, CollectedAppearance>();

	internal static int FullChecks { get; private set; }

	internal static LoadState BeginLoad(Player player)
	{
		if (!Object.op_Implicit((Object)(object)player))
		{
			return null;
		}
		player.m_customData.TryGetValue("gnls.GooCosmetics.collection.v1", out var value);
		player.m_customData.TryGetValue("gnls.GooCosmetics.granted.v1", out var value2);
		player.m_customData.TryGetValue("gnls.GooCosmetics.revoked.v1", out var value3);
		LoadState result = new LoadState
		{
			Previous = value,
			PreviousGranted = value2,
			PreviousRevoked = value3
		};
		player.m_customData.Remove("gnls.GooCosmetics.collection.v1");
		player.m_customData.Remove("gnls.GooCosmetics.granted.v1");
		player.m_customData.Remove("gnls.GooCosmetics.revoked.v1");
		States.Remove(player);
		Loading.Add(player);
		return result;
	}

	internal static void EndLoad(Player player, LoadState snapshot, bool succeeded)
	{
		if (Object.op_Implicit((Object)(object)player) && snapshot != null)
		{
			Loading.Remove(player);
			States.Remove(player);
			if (succeeded)
			{
				Loaded(player);
				return;
			}
			Restore(player, "gnls.GooCosmetics.collection.v1", snapshot.Previous);
			Restore(player, "gnls.GooCosmetics.granted.v1", snapshot.PreviousGranted);
			Restore(player, "gnls.GooCosmetics.revoked.v1", snapshot.PreviousRevoked);
		}
	}

	internal static void Restore(Player player, string key, string previous)
	{
		if (previous != null && !player.m_customData.ContainsKey(key))
		{
			player.m_customData[key] = previous;
		}
	}

	private static State Get(Player player)
	{
		if (!Object.op_Implicit((Object)(object)player))
		{
			return null;
		}
		if (States.TryGetValue(player, out var value))
		{
			return value;
		}
		player.m_customData.TryGetValue("gnls.GooCosmetics.collection.v1", out var value2);
		AppearanceJournal journal;
		bool flag = AppearanceJournal.TryDecode(value2, out journal);
		player.m_customData.TryGetValue("gnls.GooCosmetics.granted.v1", out var value3);
		AppearanceJournal journal2;
		bool flag2 = AppearanceJournal.TryDecode(value3, out journal2);
		player.m_customData.TryGetValue("gnls.GooCosmetics.revoked.v1", out var value4);
		AppearanceJournal journal3;
		bool flag3 = AppearanceJournal.TryDecode(value4, out journal3);
		value = new State
		{
			Journal = journal,
			Writable = flag,
			Granted = journal2,
			GrantedWritable = flag2,
			Revoked = journal3,
			RevokedWritable = flag3
		};
		States.Add(player, value);
		if (flag && flag2 && flag3)
		{
			Recheat(value);
		}
		if (!flag)
		{
			Plugin.WarnOnce("Saved appearances", new FormatException("The character's appearance collection could not be read. Its saved value has been preserved."));
		}
		if (!flag2)
		{
			Plugin.WarnOnce("Unlocked appearances", new FormatException("The looks added by goocosmetics unlock could not be read. Their saved value has been preserved."));
		}
		if (!flag3)
		{
			Plugin.WarnOnce("Locked appearances", new FormatException("The looks taken away by goocosmetics lock could not be read. Their saved value has been preserved."));
		}
		return value;
	}

	private static void Recheat(State state)
	{
		List<CollectedAppearance> list = null;
		foreach (CollectedAppearance entry in state.Journal.Entries)
		{
			if (MobLooks.TryGet(entry.Prefab, out var look) && look.CommandOnly && !look.Recolor)
			{
				(list ?? (list = new List<CollectedAppearance>())).Add(entry);
			}
		}
		if (list == null)
		{
			return;
		}
		foreach (CollectedAppearance item in list)
		{
			if (state.Granted.Contains(item) || state.Granted.Add(item))
			{
				state.Journal.Remove(item);
				state.GrantedDirty = (state.Dirty = true);
			}
		}
	}

	internal static int Revision(Player player)
	{
		return Get(player)?.Revision ?? 0;
	}

	internal static bool Readable(Player player)
	{
		return Get(player)?.Writable ?? false;
	}

	internal static bool AllReadable(Player player)
	{
		State state = Get(player);
		if (state != null && state.Writable && state.GrantedWritable)
		{
			return state.RevokedWritable;
		}
		return false;
	}

	internal static bool HasParticles(Player player)
	{
		State state = Get(player);
		if (state == null || !state.Writable || !Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return false;
		}
		if (state.ParticleJournalRevision != state.Revision || state.ParticleVisualRevision != Plugin.VisualRevision)
		{
			state.HasParticles = false;
			foreach (CollectedAppearance item in Owned(state))
			{
				if (ParticleSources.Available(item))
				{
					state.HasParticles = true;
					break;
				}
			}
			if (!state.HasParticles)
			{
				foreach (CollectedAppearance item2 in UnlockedLooks(player))
				{
					if (ParticleSources.Available(item2))
					{
						state.HasParticles = true;
						break;
					}
				}
			}
			state.ParticleJournalRevision = state.Revision;
			state.ParticleVisualRevision = Plugin.VisualRevision;
		}
		return state.HasParticles;
	}

	internal static void Loaded(Player player)
	{
		if (Object.op_Implicit((Object)(object)player))
		{
			States.Remove(player);
			ObserveInventory(player);
			Flush(player);
		}
	}

	internal static void ObserveInventory(Player player)
	{
		if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player) || ((Humanoid)player).GetInventory() == null)
		{
			return;
		}
		foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
		{
			Observe(player, allItem);
		}
	}

	internal static void Observe(Player player, ItemData item)
	{
		if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player) || item == null || item.m_stack != 1 || !((Humanoid)player).GetInventory().ContainsItem(item))
		{
			return;
		}
		Observation orCreateValue = Observations.GetOrCreateValue(item);
		string raw = Illusions.Raw(item);
		if (Unchanged(orCreateValue, player, item, raw))
		{
			return;
		}
		orCreateValue.Valid = false;
		orCreateValue.Classified.Clear();
		orCreateValue.Normalized.Clear();
		orCreateValue.Unscaled.Clear();
		Observation observation = noting;
		List<Classification.Use> noted = Classification.Noted;
		noting = orCreateValue;
		Classification.Noted = orCreateValue.Classified;
		try
		{
			FullChecks++;
			orCreateValue.Definitions = Classification.Definitions;
			orCreateValue.Prefab = item.m_dropPrefab;
			orCreateValue.Shared = item.m_shared;
			orCreateValue.MaxStack = item.m_shared?.m_maxStackSize ?? 0;
			orCreateValue.Variant = item.m_variant;
			orCreateValue.Quality = item.m_quality;
			orCreateValue.Raw = raw;
			Check(player, item, raw, orCreateValue);
			orCreateValue.Valid = true;
		}
		finally
		{
			noting = observation;
			Classification.Noted = noted;
		}
	}

	private static bool Unchanged(Observation last, Player player, ItemData item, string raw)
	{
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Invalid comparison between Unknown and I4
		if (!last.Valid || last.Definitions != Classification.Definitions || last.Prefab != item.m_dropPrefab || last.Shared != item.m_shared || (item.m_shared != null && item.m_shared.m_maxStackSize != last.MaxStack) || last.Variant != item.m_variant || last.Quality != item.m_quality || !string.Equals(last.Raw, raw))
		{
			return false;
		}
		if (last.Gear)
		{
			if (!States.TryGetValue(player, out var value) || value.Id != last.StateId || value.Revision != last.Revision)
			{
				return false;
			}
		}
		else
		{
			SharedData val = PrefabShared(item.m_dropPrefab);
			if (val != last.Own || (val != null && (val.m_maxStackSize != last.OwnMaxStack || (int)val.m_itemType != last.OwnType)))
			{
				return false;
			}
		}
		foreach (Classification.Use item2 in last.Classified)
		{
			if (Classification.Of(item2.Prefab) != item2.Signature)
			{
				return false;
			}
		}
		for (int i = 0; i < last.Normalized.Count; i++)
		{
			if (Unscaled(last.Normalized[i]) != last.Unscaled[i])
			{
				return false;
			}
		}
		return true;
	}

	private static SharedData PrefabShared(GameObject prefab)
	{
		if (!Object.op_Implicit((Object)(object)prefab))
		{
			return null;
		}
		return prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared;
	}

	private static void Check(Player player, ItemData item, string raw, Observation last)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		if (!Classification.Eligible(item) && ParticleSources.Plan(item.m_dropPrefab) == null)
		{
			last.Gear = false;
			last.Own = PrefabShared(item.m_dropPrefab);
			last.OwnMaxStack = last.Own?.m_maxStackSize ?? 0;
			SharedData own = last.Own;
			last.OwnType = ((own != null) ? ((int)own.m_itemType) : 0);
		}
		else
		{
			State state = Get(player);
			last.Gear = true;
			last.StateId = state.Id;
			Collect(state, item, raw);
			last.Revision = state.Revision;
		}
	}

	private static void Collect(State state, ItemData item, string raw)
	{
		if (!state.Writable)
		{
			return;
		}
		CollectedAppearance collectedAppearance = NativeLook(item);
		if (!CommandOnly(collectedAppearance.Prefab))
		{
			Add(state, collectedAppearance);
		}
		if (!string.IsNullOrEmpty(raw) && Illusions.TryResolve(raw, StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name), out var appearance, out var _) && !appearance.Hidden)
		{
			CollectedAppearance collectedAppearance2 = Normalize(new CollectedAppearance(appearance.Donor, appearance.Variant, appearance.Quality));
			if (state.GrantedWritable && state.RevokedWritable && !FromUnlockOnly(state, collectedAppearance2) && !CommandOnly(collectedAppearance2.Prefab))
			{
				Add(state, collectedAppearance2);
			}
		}
	}

	private static bool CommandOnly(string prefab)
	{
		if (MobLooks.TryGet(prefab, out var look))
		{
			return look.CommandOnly;
		}
		return false;
	}

	private static void Add(State state, CollectedAppearance look)
	{
		if (state.Journal.Add(look))
		{
			state.Dirty = true;
			state.Revision++;
			if (state.RevokedWritable && state.Revoked.Remove(look))
			{
				state.RevokedDirty = true;
			}
		}
	}

	private static bool FromUnlockOnly(State state, CollectedAppearance look, bool revoked = true)
	{
		if ((state.Granted.Count == 0 && (!revoked || state.Revoked.Count == 0)) || state.Journal.Contains(look))
		{
			return false;
		}
		if (state.Granted.Contains(look) || (revoked && state.Revoked.Contains(look)))
		{
			return true;
		}
		if (!Companions.ContainsValue(look.Prefab))
		{
			return false;
		}
		bool flag = false;
		foreach (CollectedAppearance item in Owned(state))
		{
			if (CompanionOf(item.Prefab) == look.Prefab && look.Equals(Normalize(new CollectedAppearance(look.Prefab, 0, item.Quality))))
			{
				if (state.Journal.Contains(item))
				{
					return false;
				}
				flag = true;
			}
		}
		if (flag || !revoked)
		{
			return flag;
		}
		foreach (CollectedAppearance entry in state.Revoked.Entries)
		{
			if (CompanionOf(entry.Prefab) == look.Prefab && look.Equals(Normalize(new CollectedAppearance(look.Prefab, 0, entry.Quality))))
			{
				return true;
			}
		}
		return false;
	}

	internal static bool Cheated(Player player, CollectedAppearance look)
	{
		State state = Get(player);
		if (look == null || !look.Valid || state == null || !state.Writable || state.Granted.Count == 0)
		{
			return false;
		}
		CollectedAppearance collectedAppearance = Normalize(look);
		if (MobLooks.TryGet(collectedAppearance.Prefab, out var look2))
		{
			if (state.Journal.Contains(collectedAppearance))
			{
				return false;
			}
			string[] unlocks = look2.Unlocks;
			for (int i = 0; i < unlocks.Length; i++)
			{
				string text = Native.PrefabItem(unlocks[i])?.m_shared?.m_name;
				if (text != null && player.IsMaterialKnown(text))
				{
					return false;
				}
			}
		}
		return FromUnlockOnly(state, collectedAppearance, revoked: false);
	}

	internal static bool FirstUnlockItem(Player player, ItemData item)
	{
		string text = item?.m_shared?.m_name;
		if (Object.op_Implicit((Object)(object)player) && text != null && !player.IsMaterialKnown(text) && Object.op_Implicit((Object)(object)item.m_dropPrefab))
		{
			return MobLooks.IsUnlockItem(((Object)item.m_dropPrefab).name);
		}
		return false;
	}

	internal static void ObserveUnlock(Player player, ItemData item)
	{
		if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player))
		{
			return;
		}
		object prefab;
		if (item == null)
		{
			prefab = null;
		}
		else
		{
			GameObject dropPrefab = item.m_dropPrefab;
			prefab = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
		}
		if (MobLooks.IsUnlockItem((string)prefab))
		{
			State state = Get(player);
			if (state != null)
			{
				state.Revision++;
			}
		}
	}

	private static HashSet<string> UnlockedSet(Player player, State state)
	{
		if (state.Unlocked != null && state.UnlockRevision == state.Revision && state.UnlockVisualRevision == Plugin.VisualRevision)
		{
			return state.Unlocked;
		}
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
		if (Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			foreach (MobLook item in MobLooks.Catalog)
			{
				string[] unlocks = item.Unlocks;
				for (int i = 0; i < unlocks.Length; i++)
				{
					string text = Native.PrefabItem(unlocks[i])?.m_shared?.m_name;
					if (text != null && player.IsMaterialKnown(text))
					{
						hashSet.Add(item.Prefab);
						break;
					}
				}
			}
		}
		foreach (CollectedAppearance item2 in Owned(state))
		{
			if (MobLooks.TryGet(item2.Prefab, out var _))
			{
				hashSet.Add(item2.Prefab);
			}
		}
		state.Unlocked = hashSet;
		state.UnlockRevision = state.Revision;
		state.UnlockVisualRevision = Plugin.VisualRevision;
		return hashSet;
	}

	private static IEnumerable<CollectedAppearance> Owned(State state)
	{
		foreach (CollectedAppearance entry in state.Journal.Entries)
		{
			yield return entry;
		}
		foreach (CollectedAppearance entry2 in state.Granted.Entries)
		{
			yield return entry2;
		}
	}

	internal static bool IsFree(string prefab)
	{
		if (prefab != null)
		{
			return Array.IndexOf(FreeLooks, prefab) >= 0;
		}
		return false;
	}

	internal static string CompanionOf(string prefab)
	{
		if (prefab == null || !Companions.TryGetValue(prefab, out var value))
		{
			return null;
		}
		return value;
	}

	internal static string CompanionSource(string prefab)
	{
		foreach (KeyValuePair<string, string> companion in Companions)
		{
			if (companion.Value == prefab)
			{
				return companion.Key;
			}
		}
		return null;
	}

	internal static bool Unlocked(Player player, string prefab)
	{
		State state = Get(player);
		if (state != null && state.Writable && prefab != null)
		{
			if (!IsFree(prefab))
			{
				return UnlockedSet(player, state).Contains(prefab);
			}
			return true;
		}
		return false;
	}

	internal static IEnumerable<CollectedAppearance> UnlockedLooks(Player player)
	{
		State state = Get(player);
		if (state == null || !state.Writable || !Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			yield break;
		}
		HashSet<string> unlocked = UnlockedSet(player, state);
		foreach (MobLook item in MobLooks.Catalog)
		{
			if (unlocked.Contains(item.Prefab) && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(item.Prefab)))
			{
				yield return Normalize(new CollectedAppearance(item.Prefab, 0, 1));
			}
		}
		string[] freeLooks = FreeLooks;
		foreach (string text in freeLooks)
		{
			if (Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(text)))
			{
				yield return Normalize(new CollectedAppearance(text, 0, 1));
			}
		}
		foreach (CollectedAppearance item2 in Owned(state))
		{
			string text2 = CompanionOf(item2.Prefab);
			if (text2 != null && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(text2)))
			{
				yield return Normalize(new CollectedAppearance(text2, 0, item2.Quality));
			}
		}
	}

	internal static bool Grant(Player player, IEnumerable<CollectedAppearance> looks, out List<CollectedAppearance> added, out int full)
	{
		added = new List<CollectedAppearance>();
		full = 0;
		State state = Get(player);
		if (state == null || !state.Writable || !state.GrantedWritable || !state.RevokedWritable || Loading.Contains(player))
		{
			return false;
		}
		HashSet<CollectedAppearance> hashSet = new HashSet<CollectedAppearance>();
		foreach (CollectedAppearance item in Owned(state))
		{
			hashSet.Add(Normalize(item));
		}
		foreach (CollectedAppearance item2 in UnlockedLooks(player))
		{
			hashSet.Add(item2);
		}
		foreach (CollectedAppearance look in looks)
		{
			CollectedAppearance collectedAppearance = Normalize(look);
			if (collectedAppearance == null || !collectedAppearance.Valid || !hashSet.Add(collectedAppearance))
			{
				continue;
			}
			if (!state.Granted.Add(collectedAppearance))
			{
				full++;
				continue;
			}
			added.Add(collectedAppearance);
			if (state.Revoked.Remove(collectedAppearance))
			{
				state.RevokedDirty = true;
			}
		}
		if (added.Count > 0)
		{
			state.GrantedDirty = true;
			state.Revision++;
			Flush(player);
		}
		return true;
	}

	internal static bool Revoke(Player player, string prefab, out int removed, out int unremembered)
	{
		removed = (unremembered = 0);
		State state = Get(player);
		if (state == null || !state.GrantedWritable || !state.RevokedWritable || Loading.Contains(player))
		{
			return false;
		}
		List<CollectedAppearance> list = new List<CollectedAppearance>();
		foreach (CollectedAppearance entry in state.Granted.Entries)
		{
			if (prefab == null || entry.Prefab == prefab)
			{
				list.Add(entry);
			}
		}
		foreach (CollectedAppearance item in list)
		{
			if (!state.Granted.Remove(item))
			{
				continue;
			}
			removed++;
			if (!state.Journal.Contains(item))
			{
				if (state.Revoked.Add(item))
				{
					state.RevokedDirty = true;
				}
				else if (!state.Revoked.Contains(item))
				{
					unremembered++;
				}
			}
		}
		if (removed > 0)
		{
			state.GrantedDirty = true;
			state.Revision++;
			Flush(player);
		}
		return true;
	}

	internal static IEnumerable<string> GrantedPrefabs(Player player)
	{
		State state = Get(player);
		if (state == null)
		{
			yield break;
		}
		HashSet<string> names = new HashSet<string>(StringComparer.Ordinal);
		foreach (CollectedAppearance entry in state.Granted.Entries)
		{
			if (names.Add(entry.Prefab))
			{
				yield return entry.Prefab;
			}
		}
	}

	internal static bool GrantedOnly(Player player, CollectedAppearance look)
	{
		State state = Get(player);
		if (look == null || state == null || !state.Writable)
		{
			return false;
		}
		CollectedAppearance entry = Normalize(look);
		if (state.Granted.Contains(entry))
		{
			return !state.Journal.Contains(entry);
		}
		return false;
	}

	internal static void Flush(Player player)
	{
		if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player))
		{
			return;
		}
		State state = Get(player);
		if (state == null)
		{
			return;
		}
		if (state.Writable && state.Dirty)
		{
			player.m_customData["gnls.GooCosmetics.collection.v1"] = state.Journal.Encode();
			state.Dirty = false;
		}
		if (state.GrantedWritable && state.GrantedDirty)
		{
			if (state.Granted.Count == 0)
			{
				player.m_customData.Remove("gnls.GooCosmetics.granted.v1");
			}
			else
			{
				player.m_customData["gnls.GooCosmetics.granted.v1"] = state.Granted.Encode();
			}
			state.GrantedDirty = false;
		}
		if (state.RevokedWritable && state.RevokedDirty)
		{
			if (state.Revoked.Count == 0)
			{
				player.m_customData.Remove("gnls.GooCosmetics.revoked.v1");
			}
			else
			{
				player.m_customData["gnls.GooCosmetics.revoked.v1"] = state.Revoked.Encode();
			}
			state.RevokedDirty = false;
		}
	}

	internal static void Saving(Player player)
	{
		ObserveInventory(player);
		Flush(player);
	}

	internal static CollectedAppearance Normalize(CollectedAppearance look)
	{
		if (look == null)
		{
			return null;
		}
		bool flag = Unscaled(look.Prefab);
		noting?.Note(look.Prefab, flag);
		if (!flag)
		{
			return look;
		}
		return new CollectedAppearance(look.Prefab, look.Variant, 1);
	}

	private static bool Unscaled(string prefab)
	{
		ItemData val = Native.PrefabItem(prefab);
		if (val?.m_shared != null)
		{
			return val.m_shared.m_scaleByQuality == 0f;
		}
		return false;
	}

	internal static CollectedAppearance NativeLook(ItemData item)
	{
		if ((Object)(object)item?.m_dropPrefab == (Object)null)
		{
			return null;
		}
		return Normalize(new CollectedAppearance(((Object)item.m_dropPrefab).name, item.m_variant, item.m_quality));
	}

	internal static CollectedAppearance VisibleLook(ItemData item)
	{
		if ((Object)(object)item?.m_dropPrefab == (Object)null)
		{
			return null;
		}
		if (!Illusions.TryResolve(Illusions.Raw(item), StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name), out var appearance, out var _) || appearance.Hidden)
		{
			return NativeLook(item);
		}
		return Normalize(new CollectedAppearance(appearance.Donor, appearance.Variant, appearance.Quality));
	}

	internal static bool Contains(Player player, CollectedAppearance look)
	{
		State state = Get(player);
		if (look == null || !look.Valid || state == null || !state.Writable)
		{
			return false;
		}
		CollectedAppearance collectedAppearance = Normalize(look);
		if (state.Journal.Contains(collectedAppearance) || state.Granted.Contains(collectedAppearance))
		{
			return true;
		}
		if (Unlocked(player, collectedAppearance.Prefab) && collectedAppearance.Equals(Normalize(new CollectedAppearance(collectedAppearance.Prefab, 0, 1))))
		{
			return true;
		}
		foreach (CollectedAppearance item in Owned(state))
		{
			if (collectedAppearance.Equals(Normalize(item)) || (CompanionOf(item.Prefab) == collectedAppearance.Prefab && collectedAppearance.Equals(Normalize(new CollectedAppearance(collectedAppearance.Prefab, 0, item.Quality)))))
			{
				return true;
			}
		}
		return false;
	}

	internal static Dictionary<string, CollectedAppearance> HeldByPrefab(Player player)
	{
		State state = Get(player);
		if (state == null || !state.Writable || !Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return NoneHeld;
		}
		if (state.Held != null && state.HeldRevision == state.Revision && state.HeldVisualRevision == Plugin.VisualRevision)
		{
			return state.Held;
		}
		Dictionary<string, CollectedAppearance> dictionary = new Dictionary<string, CollectedAppearance>(StringComparer.Ordinal);
		foreach (CollectedAppearance item in Owned(state))
		{
			Keep(dictionary, Normalize(item));
		}
		foreach (CollectedAppearance item2 in UnlockedLooks(player))
		{
			Keep(dictionary, item2);
		}
		state.Held = dictionary;
		state.HeldRevision = state.Revision;
		state.HeldVisualRevision = Plugin.VisualRevision;
		return dictionary;
	}

	private static void Keep(Dictionary<string, CollectedAppearance> held, CollectedAppearance look)
	{
		if (look != null && look.Valid && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(look.Prefab)) && (!held.TryGetValue(look.Prefab, out var value) || look.Variant < value.Variant || (look.Variant == value.Variant && look.Quality < value.Quality)))
		{
			held[look.Prefab] = look;
		}
	}

	internal static List<CollectedAppearance> Matching(Player player, ItemData target, bool particles = false)
	{
		if (particles)
		{
			return ParticleEffects.Matching(player, target);
		}
		List<CollectedAppearance> list = new List<CollectedAppearance>();
		State state = Get(player);
		if (state == null || !state.Writable || !Classification.Eligible(target) || !Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return list;
		}
		HashSet<CollectedAppearance> hashSet = new HashSet<CollectedAppearance>();
		List<CollectedAppearance> list2 = new List<CollectedAppearance>(Owned(state));
		list2.AddRange(UnlockedLooks(player));
		foreach (CollectedAppearance item in list2)
		{
			CollectedAppearance collectedAppearance = Normalize(item);
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(collectedAppearance.Prefab);
			if (Object.op_Implicit((Object)(object)itemPrefab) && hashSet.Add(collectedAppearance) && Fits(target, itemPrefab))
			{
				list.Add(collectedAppearance);
			}
		}
		AppearanceOrder.Sort(list);
		return list;
	}

	internal static List<CollectedAppearance> Locked(Player player, ItemData target, bool particles = false)
	{
		if (particles)
		{
			return ParticleEffects.Locked(player, target);
		}
		List<CollectedAppearance> list = new List<CollectedAppearance>();
		State state = Get(player);
		if (state == null || !state.Writable || !Classification.Eligible(target) || !Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return list;
		}
		HashSet<string> hashSet = UnlockedSet(player, state);
		foreach (MobLook item in MobLooks.Catalog)
		{
			GameObject val = ((hashSet.Contains(item.Prefab) || item.CommandOnly) ? null : ObjectDB.instance.GetItemPrefab(item.Prefab));
			CollectedAppearance collectedAppearance = (Object.op_Implicit((Object)(object)val) ? Normalize(new CollectedAppearance(item.Prefab, 0, 1)) : null);
			if (collectedAppearance != null && Fits(target, val))
			{
				list.Add(collectedAppearance);
			}
		}
		AppearanceOrder.Sort(list);
		return list;
	}

	private static bool Fits(ItemData target, GameObject prefab)
	{
		if (Classification.PairPrefabs(target.m_dropPrefab, prefab, out var _))
		{
			return VisualCapabilities.Listable(prefab, Classification.Of(prefab).Kind);
		}
		return false;
	}

	internal static ItemData Preview(CollectedAppearance look)
	{
		if (look == null || !look.Valid || !Object.op_Implicit((Object)(object)ObjectDB.instance) || MobLooks.TryGet(look.Prefab, out var _))
		{
			return null;
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(look.Prefab);
		ItemData val = ((!Object.op_Implicit((Object)(object)itemPrefab)) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData);
		if (val?.m_shared == null)
		{
			return null;
		}
		ItemData obj = val.Clone();
		obj.m_customData.Clear();
		obj.m_dropPrefab = itemPrefab;
		obj.m_stack = 1;
		obj.m_variant = look.Variant;
		obj.m_quality = look.Quality;
		obj.m_equipped = false;
		obj.m_durability = obj.GetMaxDurability();
		return obj;
	}

	internal static string Name(CollectedAppearance look)
	{
		if (look == null)
		{
			return "";
		}
		if (MobLooks.TryGet(look.Prefab, out var look2) && !look2.Trophy)
		{
			if (!look2.Recolor)
			{
				return look2.Name;
			}
			return ((look2.Source == "Shadow") ? "Shadow " : "Fallen ") + ItemName(look2.Original);
		}
		return ItemName(look.Prefab);
	}

	internal static string ItemName(string prefab)
	{
		string text = Native.PrefabItem(prefab)?.m_shared?.m_name ?? prefab;
		if (Localization.instance == null)
		{
			return text;
		}
		return Localization.instance.Localize(text);
	}

	internal static Sprite Icon(CollectedAppearance look)
	{
		Sprite badge;
		return Icon(look, out badge);
	}

	internal static Sprite Icon(CollectedAppearance look, out Sprite badge, bool render = true, GearKind kind = GearKind.Unsupported)
	{
		badge = null;
		if (look == null)
		{
			return null;
		}
		if (!MobLooks.TryGet(look.Prefab, out var look2))
		{
			return PrefabIcon(look.Prefab, look.Variant);
		}
		if (look2.Trophy)
		{
			return PrefabIcon(look2.Prefab, 0);
		}
		Sprite val = UnlockIcon(look2);
		Sprite val2 = (look2.Recolor ? PrefabIcon(look2.Original, 0) : MonsterIcon(look2, render, kind));
		if (!Object.op_Implicit((Object)(object)val2))
		{
			if (!Object.op_Implicit((Object)(object)val))
			{
				return PrefabIcon(look2.Prefab, 0);
			}
			return val;
		}
		badge = val;
		return val2;
	}

	internal static bool IconPending(CollectedAppearance look, GearKind kind = GearKind.Unsupported)
	{
		if (look != null && MobLooks.TryGet(look.Prefab, out var look2) && !look2.Recolor && !look2.Trophy)
		{
			return !MobLookIcons.Ready(look2.Prefab, IconFraming.Layout(look2, kind));
		}
		return false;
	}

	private static Sprite MonsterIcon(MobLook mob, bool render, GearKind kind)
	{
		IconLayout layout = IconFraming.Layout(mob, kind);
		if (!render && !MobLookIcons.Ready(mob.Prefab, layout))
		{
			return null;
		}
		GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(mob.Prefab) : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			return null;
		}
		return MobLookIcons.Get(val, layout);
	}

	private static Sprite PrefabIcon(string name, int variant)
	{
		GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(name) : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			return null;
		}
		return VisualCapabilities.Icon(val, variant);
	}

	private static Sprite UnlockIcon(MobLook mob)
	{
		Sprite val = null;
		if (!Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			return null;
		}
		string[] unlocks = mob.Unlocks;
		foreach (string text in unlocks)
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
			Sprite val2 = (Object.op_Implicit((Object)(object)itemPrefab) ? VisualCapabilities.Icon(itemPrefab, 0) : null);
			if (Object.op_Implicit((Object)(object)val2))
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					val = val2;
				}
				string text2 = Native.PrefabItem(text)?.m_shared?.m_name;
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && text2 != null && Player.m_localPlayer.IsMaterialKnown(text2))
				{
					return val2;
				}
			}
		}
		return val;
	}

	internal static bool Describe(CollectedAppearance look, out string topic, out string text)
	{
		topic = (text = null);
		if (look == null || !MobLooks.TryGet(look.Prefab, out var look2))
		{
			return false;
		}
		topic = Name(look);
		List<string> list = new List<string>();
		string[] unlocks = look2.Unlocks;
		foreach (string prefab in unlocks)
		{
			list.Add(ItemName(prefab));
		}
		List<string> list2 = new List<string>();
		if (look2.Recolor)
		{
			list2.Add("the same items as the " + ItemName(look2.Original));
		}
		else
		{
			GearKind[] families = look2.Families;
			foreach (GearKind kind in families)
			{
				list2.Add(FamilyRules.Staff(kind) ? ("staffs held like the " + ItemName(look2.HeldLike)) : FamilyRules.Label(kind).ToLowerInvariant());
			}
		}
		bool flag = Object.op_Implicit((Object)(object)Player.m_localPlayer) && !Unlocked(Player.m_localPlayer, look2.Prefab);
		text = ((!flag) ? "" : (look2.CommandOnly ? "Not in your collection\n" : "Locked\n")) + (look2.Recolor ? (look2.Source + " recolor") : ((look2.Trophy ? "Trophy: " : (look2.Helmet ? "Monster helmet: " : "Monster weapon: ")) + look2.Source)) + "\nUnlocked by: " + (look2.CommandOnly ? "goocosmetics unlock only" : string.Join(" or ", list)) + "\nFits: " + string.Join(", ", list2);
		return true;
	}

	internal static string UnlockHint(CollectedAppearance look)
	{
		if (look == null || !MobLooks.TryGet(look.Prefab, out var look2))
		{
			return "";
		}
		if (look2.CommandOnly)
		{
			return "Only goocosmetics unlock gives this look.";
		}
		List<string> list = new List<string>();
		string[] unlocks = look2.Unlocks;
		foreach (string prefab in unlocks)
		{
			list.Add("the " + ItemName(prefab));
		}
		return "Pick up " + string.Join(" or ", list) + " to unlock this look.";
	}

	internal static string RefusedAppearance(ItemData original, string prefab)
	{
		if (!MobLooks.IsUnlockItem(prefab))
		{
			return "This item can’t be used as an appearance.";
		}
		if (original == null)
		{
			return "Choose the item you want to change first.";
		}
		if (!Features.Has(Feature.Collection) || !Object.op_Implicit((Object)(object)ObjectDB.instance) || !Classification.Eligible(original))
		{
			return "This item can’t be used as an appearance.";
		}
		foreach (MobLook item in MobLooks.Catalog)
		{
			if (Array.IndexOf(item.Unlocks, prefab) >= 0)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Prefab);
				if (Object.op_Implicit((Object)(object)itemPrefab) && Fits(original, itemPrefab))
				{
					return "Choose what this unlocks in the Collection.";
				}
			}
		}
		return "This item can’t be used as an appearance.";
	}
}
internal static class AppearanceOrder
{
	private static readonly HashSet<string> vanilla = new HashSet<string>(StringComparer.Ordinal);

	private static readonly Dictionary<string, string[]> recipes = new Dictionary<string, string[]>(StringComparer.Ordinal);

	private static readonly HashSet<string> craftable = new HashSet<string>(StringComparer.Ordinal);

	private static readonly Dictionary<string, int> resolved = new Dictionary<string, int>(StringComparer.Ordinal);

	private static bool captured;

	private static readonly Regex Formatting = new Regex("<[^>]*>", RegexOptions.CultureInvariant);

	private static readonly List<CollectedAppearance> trailing = new List<CollectedAppearance>();

	internal static bool HasVanillaSnapshot => captured;

	internal static void CaptureVanilla(ObjectDB database)
	{
		if (captured || !Object.op_Implicit((Object)(object)database) || database.m_items == null)
		{
			return;
		}
		bool flag = false;
		foreach (GameObject item in database.m_items)
		{
			if (Object.op_Implicit((Object)(object)item) && ((Object)item).name == "AxeStone")
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return;
		}
		foreach (GameObject item2 in database.m_items)
		{
			if (Object.op_Implicit((Object)(object)item2))
			{
				vanilla.Add(((Object)item2).name);
			}
		}
		foreach (Recipe recipe in database.m_recipes)
		{
			if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item) || recipe.m_resources == null)
			{
				continue;
			}
			List<string> list = new List<string>();
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (Object.op_Implicit((Object)(object)val?.m_resItem))
				{
					list.Add(((Object)val.m_resItem).name);
				}
			}
			string name = ((Object)recipe.m_item).name;
			if (!recipes.ContainsKey(name))
			{
				recipes.Add(name, list.ToArray());
			}
			if (recipe.m_enabled)
			{
				craftable.Add(name);
			}
		}
		captured = true;
	}

	internal static bool Craftable(string name)
	{
		if (captured)
		{
			return craftable.Contains(name);
		}
		return true;
	}

	private static int MaterialRank(string name)
	{
		switch (name)
		{
		case "Gold":
		case "GoldBar":
		case "GoldOre":
		case "AncientGemstoneBlack":
		case "AncientGemstoneGreen":
		case "AncientGemstoneOrange":
		case "AncientGemstonePurple":
			return 800;
		case "AskHide":
		case "GemstoneRed":
		case "MorgenHeart":
		case "FlametalNew":
		case "GemstoneBlue":
		case "GemstoneGreen":
		case "FlametalOreNew":
			return 700;
		case "Eitr":
		case "YggdrasilWood":
		case "Softtissue":
		case "Mandible":
		case "Carapace":
		case "ScaleHide":
		case "BlackCore":
			return 600;
		case "LoxPelt":
		case "LinenThread":
		case "BlackMetal":
		case "BlackMetalScrap":
			return 500;
		case "Crystal":
		case "FenringHair":
		case "FenringClaw":
		case "FreezeGland":
		case "WolfFang":
		case "WolfPelt":
		case "SilverOre":
		case "Silver":
			return 400;
		case "Iron":
		case "Root":
		case "IronScrap":
		case "ElderBark":
		case "Chain":
			return 300;
		case "SurtlingCore":
		case "RoundLog":
		case "FineWood":
		case "TrollHide":
		case "Bronze":
		case "Copper":
		case "Tin":
			return 200;
		default:
			return 100;
		}
	}

	private static int Rank(string name, HashSet<string> path)
	{
		int num = AppearanceProgression.Rank(name);
		if (num > 0)
		{
			return num;
		}
		if (resolved.TryGetValue(name, out var value))
		{
			return value;
		}
		int num2 = MaterialRank(name);
		if (path.Count >= 32 || !path.Add(name))
		{
			return num2;
		}
		if (recipes.TryGetValue(name, out var value2))
		{
			string[] array = value2;
			foreach (string name2 in array)
			{
				num2 = Math.Max(num2, Rank(name2, path) + 1);
			}
		}
		path.Remove(name);
		resolved[name] = num2;
		return num2;
	}

	internal static bool Last(string prefab)
	{
		return MobLooks.IsTrophy(prefab);
	}

	internal static void Sort(List<CollectedAppearance> looks)
	{
		Dictionary<CollectedAppearance, string> names = new Dictionary<CollectedAppearance, string>();
		Dictionary<CollectedAppearance, int> ranks = new Dictionary<CollectedAppearance, int>();
		Dictionary<CollectedAppearance, bool> obtainable = new Dictionary<CollectedAppearance, bool>();
		Dictionary<CollectedAppearance, bool> last = new Dictionary<CollectedAppearance, bool>();
		foreach (CollectedAppearance look in looks)
		{
			names[look] = Formatting.Replace(AppearanceCollection.Name(look), "");
			ranks[look] = Rank(look.Prefab, new HashSet<string>(StringComparer.Ordinal));
			obtainable[look] = Obtainable.Item(look.Prefab);
			last[look] = Last(look.Prefab);
		}
		looks.Sort(delegate(CollectedAppearance left, CollectedAppearance right)
		{
			if (last[left] != last[right])
			{
				if (!last[left])
				{
					return -1;
				}
				return 1;
			}
			if (last[left])
			{
				return AppearanceProgression.Compare(!MobLooks.IsModdedTrophy(left.Prefab), ranks[left], names[left], left, !MobLooks.IsModdedTrophy(right.Prefab), ranks[right], names[right], right);
			}
			int num = obtainable[right].CompareTo(obtainable[left]);
			return (num == 0) ? AppearanceProgression.Compare(IsVanilla(left.Prefab), ranks[left], names[left], left, IsVanilla(right.Prefab), ranks[right], names[right], right) : num;
		});
	}

	internal static void TrophiesLast(List<CollectedAppearance> looks)
	{
		int num = 0;
		trailing.Clear();
		for (int i = 0; i < looks.Count; i++)
		{
			CollectedAppearance collectedAppearance = looks[i];
			if (collectedAppearance != null && Last(collectedAppearance.Prefab))
			{
				trailing.Add(collectedAppearance);
			}
			else
			{
				looks[num++] = collectedAppearance;
			}
		}
		if (trailing.Count != 0)
		{
			looks.RemoveRange(num, looks.Count - num);
			looks.AddRange(trailing);
			trailing.Clear();
		}
	}

	internal static bool IsVanilla(string name)
	{
		if (!captured)
		{
			return AppearanceProgression.Known(name);
		}
		return vanilla.Contains(name);
	}

	internal static IEnumerable<string> VanillaItems()
	{
		if (captured)
		{
			return vanilla;
		}
		List<string> list = new List<string>();
		if (Object.op_Implicit((Object)(object)ObjectDB.instance))
		{
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				if (Object.op_Implicit((Object)(object)item))
				{
					list.Add(((Object)item).name);
				}
			}
		}
		return list;
	}
}
internal static class AppearanceProgression
{
	private static readonly Dictionary<string, int> Ranks = Build();

	internal static bool Known(string name)
	{
		if (name != null)
		{
			return Ranks.ContainsKey(name);
		}
		return false;
	}

	internal static int Rank(string name)
	{
		if (name == null || !Ranks.TryGetValue(name, out var value))
		{
			return 0;
		}
		return value;
	}

	private static void Add(Dictionary<string, int> ranks, int stage, params string[] names)
	{
		for (int i = 0; i < names.Length; i++)
		{
			ranks[names[i]] = stage * 100 + i;
		}
	}

	private static Dictionary<string, int> Build()
	{
		Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
		Add(dictionary, 1, "AxeStone", "AxeFlint", "Club", "KnifeFlint", "SpearFlint", "Bow", "ShieldWood", "ShieldWoodTower", "SledgeStagbreaker", "ArmorRagsChest", "ArmorRagsLegs", "ArmorLeatherChest", "ArmorLeatherLegs", "HelmetLeather", "CapeDeerHide");
		Add(dictionary, 2, "PickaxeAntler", "AxeBronze", "SwordBronze", "MaceBronze", "KnifeCopper", "SpearBronze", "AtgeirBronze", "BowFineWood", "ShieldBronzeBuckler", "ShieldBoneTower", "PickaxeBronze", "ArmorTrollLeatherChest", "ArmorTrollLeatherLegs", "HelmetTrollLeather", "CapeTrollHide", "ArmorBronzeChest", "ArmorBronzeLegs", "HelmetBronze", "HelmetDverger", "HelmetYule", "CapeOdin", "HelmetOdin");
		Add(dictionary, 3, "AxeIron", "SwordIron", "SwordIronFire", "MaceIron", "Battleaxe", "AtgeirIron", "SpearElderbark", "KnifeChitin", "SpearChitin", "BowHuntsman", "SledgeIron", "PickaxeIron", "ShieldIronBuckler", "ShieldBanded", "ShieldIronTower", "ShieldSerpentscale", "ArmorRootChest", "ArmorRootLegs", "HelmetRoot", "ArmorIronChest", "ArmorIronLegs", "HelmetIron");
		Add(dictionary, 4, "SwordSilver", "MaceSilver", "KnifeSilver", "SpearWolfFang", "BowDraugrFang", "BattleaxeCrystal", "FistFenrirClaw", "ShieldSilver", "ArmorWolfChest", "ArmorWolfLegs", "HelmetDrake", "CapeWolf", "ArmorFenringChest", "ArmorFenringLegs", "HelmetFenring");
		Add(dictionary, 5, "AxeBlackMetal", "SwordBlackmetal", "MaceNeedle", "KnifeBlackMetal", "AtgeirBlackmetal", "ShieldBlackmetal", "ShieldBlackmetalTower", "ArmorPaddedCuirass", "ArmorPaddedGreaves", "HelmetPadded", "CapeLox", "CapeLinen");
		Add(dictionary, 6, "AxeJotunBane", "SwordMistwalker", "THSwordKrom", "SledgeDemolisher", "AtgeirHimminAfl", "SpearCarapace", "KnifeSkollAndHati", "BowSpineSnap", "CrossbowArbalest", "PickaxeBlackMetal", "ShieldCarapaceBuckler", "ShieldCarapace", "StaffFireball", "StaffIceShards", "StaffShield", "StaffSkeleton", "ArmorCarapaceChest", "ArmorCarapaceLegs", "HelmetCarapace", "ArmorMageChest", "ArmorMageLegs", "HelmetMage", "CapeFeather");
		Add(dictionary, 7, "AxeBerzerkr", "AxeBerzerkrBlood", "AxeBerzerkrLightning", "AxeBerzerkrNature", "SwordNiedhogg", "SwordNiedhoggBlood", "SwordNiedhoggLightning", "SwordNiedhoggNature", "THSwordSlayer", "THSwordSlayerBlood", "THSwordSlayerLightning", "THSwordSlayerNature", "SpearSplitner", "SpearSplitner_Blood", "SpearSplitner_Lightning", "SpearSplitner_Nature", "BowAshlands", "BowAshlandsBlood", "BowAshlandsStorm", "BowAshlandsRoot", "CrossbowRipper", "CrossbowRipperBlood", "CrossbowRipperLightning", "CrossbowRipperNature", "MaceEldner", "MaceEldnerBlood", "MaceEldnerLightning", "MaceEldnerNature", "SwordDyrnwyn", "ShieldFlametal", "ShieldFlametalTower", "StaffClusterbomb", "StaffLightning", "StaffRedTroll", "StaffGreenRoots", "ArmorFlametalChest", "ArmorFlametalLegs", "HelmetFlametal", "ArmorAshlandsMediumChest", "ArmorAshlandsMediumlegs", "HelmetAshlandsMediumHood", "ArmorMageChest_Ashlands", "ArmorMageLegs_Ashlands", "HelmetMage_Ashlands", "CapeAsksvin", "CapeAsh");
		Add(dictionary, 8, "AxeGold", "AxeGold_BloodLightning", "AxeGold_FrostFire", "BattleaxeGold", "BattleaxeGold_BloodLightning", "BattleaxeGold_FrostFire", "SwordGold", "SwordGold_BloodLightning", "SwordGold_FrostFire", "THSwordGold", "THSwordGold_BloodLightning", "THSwordGold_FrostFire", "MaceGold", "MaceGold_BloodLightning", "MaceGold_FrostFire", "SledgeGold", "SledgeGold_BloodLightning", "SledgeGold_FrostFire", "SpearGold", "SpearGold_BloodLightning", "SpearGold_FrostFire", "AtgeirGold", "AtgeirGold_BloodLightning", "AtgeirGold_FrostFire", "KnifeGold", "KnifeGold_BloodLightning", "KnifeGold_FrostFire", "BowGold", "BowGold_BloodLightning", "BowGold_FrostFire", "CrossbowGold", "CrossbowGold_BloodLightning", "CrossbowGold_FrostFire", "ShieldGold", "ShieldGoldTower", "ShieldGoldBuckler", "StaffFrostOrbs", "StaffThunderBlood", "StaffOrbofAhri", "StaffSpiritCaller", "ArmorDeepNorthHeavyChest", "ArmorDeepNorthHeavylegs", "HelmetDNHeavy", "ArmorDeepNorthMediumChest", "ArmorDeepNorthMediumlegs", "HelmetDNMediumHood", "ArmorDeepNorthMageChest", "ArmorDeepNorthMagelegs", "HelmetDNMage");
		Dictionary<int, int> dictionary2 = new Dictionary<int, int>();
		foreach (MobLook item in MobLooks.Catalog)
		{
			dictionary2.TryGetValue(item.Stage, out var value);
			dictionary[item.Prefab] = item.Stage * 100 + 80 + value;
			dictionary2[item.Stage] = value + 1;
		}
		return dictionary;
	}

	internal static int Compare(bool leftVanilla, int leftRank, string leftName, CollectedAppearance left, bool rightVanilla, int rightRank, string rightName, CollectedAppearance right)
	{
		int num = rightVanilla.CompareTo(leftVanilla);
		if (num == 0 && leftVanilla)
		{
			num = leftRank.CompareTo(rightRank);
		}
		if (num == 0)
		{
			num = StringComparer.CurrentCultureIgnoreCase.Compare(leftName, rightName);
		}
		if (num == 0)
		{
			num = StringComparer.Ordinal.Compare(left.Prefab, right.Prefab);
		}
		if (num == 0)
		{
			int variant = left.Variant;
			num = variant.CompareTo(right.Variant);
		}
		if (num == 0)
		{
			int variant = left.Quality;
			num = variant.CompareTo(right.Quality);
		}
		return num;
	}
}
internal sealed class AppearanceSelection
{
	private CollectedAppearance original;

	private CollectedAppearance previous;

	internal CollectedAppearance Current { get; private set; }

	internal bool IsOriginal
	{
		get
		{
			if (original != null)
			{
				return original.Equals(Current);
			}
			return false;
		}
	}

	internal CollectedAppearance SlotLook => Current ?? original;

	internal bool Dimmed
	{
		get
		{
			if (Current != null)
			{
				return IsOriginal;
			}
			return true;
		}
	}

	internal void Reset(CollectedAppearance native)
	{
		CollectedAppearance collectedAppearance = (Current = native);
		original = collectedAppearance;
		previous = null;
	}

	internal void Select(CollectedAppearance look)
	{
		Current = look;
		if (look != null && !look.Equals(original))
		{
			previous = look;
		}
	}

	internal void SetOriginal(bool enabled)
	{
		Current = (enabled ? original : previous);
	}
}
internal static class ArmorStandLooks
{
	private sealed class StandState
	{
		internal ArmorStand Stand;

		internal ZNetView View;

		internal ZDO Zdo;

		internal uint DataRevision;

		internal readonly string[] Raw = new string[8] { "", "", "", "", "", "", "", "" };
	}

	private static readonly ConditionalWeakTable<VisEquipment, StandState> States = new ConditionalWeakTable<VisEquipment, StandState>();

	private static readonly VisSlot[] Slots;

	internal static string Read(VisEquipment visual, GearSlot slot)
	{
		StandState value = States.GetValue(visual, (VisEquipment v) => new StandState
		{
			Stand = ((Component)v).GetComponentInParent<ArmorStand>()
		});
		ArmorStand stand = value.Stand;
		if (!Object.op_Implicit((Object)(object)stand))
		{
			return "";
		}
		if (!Object.op_Implicit((Object)(object)value.View))
		{
			value.View = (Object.op_Implicit((Object)(object)stand.m_netViewOverride) ? stand.m_netViewOverride : ((Component)stand).GetComponent<ZNetView>());
		}
		ZNetView view = value.View;
		if (!Object.op_Implicit((Object)(object)view) || !view.IsValid())
		{
			return "";
		}
		ZDO zDO = view.GetZDO();
		if (zDO != value.Zdo || zDO.DataRevision != value.DataRevision)
		{
			Refresh(value, zDO);
		}
		return value.Raw[(int)slot];
	}

	private static void Refresh(StandState state, ZDO zdo)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Expected O, but got Unknown
		//IL_0094: Expected O, but got Unknown
		state.Zdo = zdo;
		state.DataRevision = zdo.DataRevision;
		for (int i = 0; i < state.Raw.Length; i++)
		{
			state.Raw[i] = "";
		}
		List<ArmorStandSlot> slots = state.Stand.m_slots;
		if (slots == null)
		{
			return;
		}
		for (int j = 0; j < slots.Count; j++)
		{
			int num = ((slots[j] == null) ? (-1) : Array.IndexOf(Slots, slots[j].m_slot));
			if (num >= 0 && state.Raw[num].Length <= 0)
			{
				try
				{
					ItemData val = new ItemData();
					ItemDrop.LoadFromZDO(val, zdo, j);
					string text = Illusions.Raw(val);
					state.Raw[num] = ((text.Length > 2100) ? "" : text);
				}
				catch (Exception error)
				{
					Plugin.WarnOnce("Armor stand appearance", error);
				}
			}
		}
	}

	static ArmorStandLooks()
	{
		VisSlot[] array = new VisSlot[8];
		RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
		Slots = (VisSlot[])(object)array;
	}
}
internal static class Classification
{
	private sealed class Remembered
	{
		internal int Definitions = -1;

		internal MobLook Look;

		internal string Recolor;

		internal GearSignature Signature;

		private GameObject source;

		private SharedData shared;

		private int maxStack;

		private int stance;

		private int skill;

		private int itemType;

		private string name;

		private Attack primary;

		private Attack secondary;

		private string primaryAnimation;

		private string secondaryAnimation;

		private int primaryChain;

		private int secondaryChain;

		private int primaryType;

		private int secondaryType;

		internal bool Matches(GameObject prefab, SharedData data)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Invalid comparison between Unknown and I4
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Invalid comparison between Unknown and I4
			if (Signature == null || prefab != source || data != shared)
			{
				return false;
			}
			if (data != null)
			{
				if (data.m_maxStackSize == maxStack && (int)data.m_animationState == stance && (int)data.m_skillType == skill && (int)data.m_itemType == itemType && string.Equals(data.m_name, name) && Same(data.m_attack, primary, primaryAnimation, primaryChain, primaryType))
				{
					return Same(data.m_secondaryAttack, secondary, secondaryAnimation, secondaryChain, secondaryType);
				}
				return false;
			}
			return true;
		}

		private static bool Same(Attack attack, Attack kept, string animation, int chain, int type)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if (attack == kept)
			{
				if (attack != null)
				{
					if (string.Equals(attack.m_attackAnimation, animation) && attack.m_attackChainLevels == chain)
					{
						return (int)attack.m_attackType == type;
					}
					return false;
				}
				return true;
			}
			return false;
		}

		internal void Keep(GameObject prefab, SharedData data, GearSignature signature)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected I4, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected I4, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected I4, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			Signature = signature;
			source = prefab;
			shared = data;
			if (data != null)
			{
				maxStack = data.m_maxStackSize;
				stance = (int)data.m_animationState;
				skill = (int)data.m_skillType;
				itemType = (int)data.m_itemType;
				name = data.m_name;
				primary = data.m_attack;
				secondary = data.m_secondaryAttack;
				primaryAnimation = primary?.m_attackAnimation;
				primaryChain = primary?.m_attackChainLevels ?? 0;
				Attack obj = primary;
				primaryType = ((obj != null) ? ((int)obj.m_attackType) : 0);
				secondaryAnimation = secondary?.m_attackAnimation;
				secondaryChain = secondary?.m_attackChainLevels ?? 0;
				Attack obj2 = secondary;
				secondaryType = ((obj2 != null) ? ((int)obj2.m_attackType) : 0);
			}
		}
	}

	internal struct Use
	{
		internal GameObject Prefab;

		internal GearSignature Signature;
	}

	private sealed class Draft
	{
		internal GearKind Kind;

		internal int Stance;

		internal int Skill;

		internal int ItemType;

		internal string Primary = "";

		internal string Secondary = "";

		internal int PrimaryChain;

		internal int SecondaryChain;

		internal int PrimaryType;

		internal int SecondaryType;

		internal int KnownSlot = -1;

		internal int KnownStance = -1;

		internal string Evidence = "Unsupported item";

		internal string Issue = "";

		internal bool IsWeapon => GearSignature.Weapon(Kind);

		internal GearSignature Done()
		{
			return new GearSignature(Kind, Stance, Skill, ItemType, Primary, Secondary, PrimaryChain, SecondaryChain, PrimaryType, SecondaryType, KnownSlot, KnownStance, Evidence, Issue);
		}
	}

	private static readonly HashSet<string> Bucklers = new HashSet<string>(StringComparer.Ordinal) { "ShieldBronzeBuckler", "ShieldIronBuckler", "ShieldCarapaceBuckler", "ShieldGoldBuckler" };

	private static readonly HashSet<string> Towers = new HashSet<string>(StringComparer.Ordinal) { "ShieldWoodTower", "ShieldBoneTower", "ShieldIronTower", "ShieldSerpentscale", "ShieldBlackmetalTower", "ShieldFlametalTower", "ShieldGoldTower" };

	private static readonly HashSet<string> Rounds = new HashSet<string>(StringComparer.Ordinal) { "ShieldWood", "ShieldBanded", "ShieldSilver", "ShieldBlackmetal", "ShieldCarapace", "ShieldFlametal", "ShieldGold" };

	private static readonly GearSignature Unclassified = new GearSignature();

	private static readonly ConditionalWeakTable<GameObject, Remembered> Memos = new ConditionalWeakTable<GameObject, Remembered>();

	private static ObjectDB database;

	private static ZNetScene scene;

	private static int definitions;

	private static int revision = -1;

	private static int registrations = -1;

	private static int items = -1;

	private static int recipes = -1;

	private static int scenePrefabs = -1;

	private static bool allowUnknown;

	internal static List<Use> Noted;

	internal static int Definitions
	{
		get
		{
			ObjectDB instance = ObjectDB.instance;
			ZNetScene instance2 = ZNetScene.instance;
			bool num = Object.op_Implicit((Object)(object)instance);
			bool flag = Object.op_Implicit((Object)(object)instance2);
			int num2 = ((num && instance.m_items != null) ? instance.m_items.Count : (-1));
			int num3 = ((num && instance.m_recipes != null) ? instance.m_recipes.Count : (-1));
			int num4 = ((flag && instance2.m_prefabs != null) ? instance2.m_prefabs.Count : (-1));
			bool flag2 = AllowUnknown;
			if (instance == database && instance2 == scene && revision == Plugin.VisualRevision && registrations == Compatibility.Revision && items == num2 && recipes == num3 && scenePrefabs == num4 && allowUnknown == flag2)
			{
				return definitions;
			}
			database = instance;
			scene = instance2;
			revision = Plugin.VisualRevision;
			registrations = Compatibility.Revision;
			items = num2;
			recipes = num3;
			scenePrefabs = num4;
			allowUnknown = flag2;
			return ++definitions;
		}
	}

	internal static bool AllowUnknown => Plugin.AllowUnknownTypes?.Value ?? false;

	internal static GearSignature Of(ItemData item)
	{
		if ((Object)(object)item?.m_dropPrefab == (Object)null)
		{
			return Unclassified;
		}
		return Of(item.m_dropPrefab);
	}

	private static Remembered Remember(GameObject prefab)
	{
		Remembered orCreateValue = Memos.GetOrCreateValue(prefab);
		int num = Definitions;
		if (orCreateValue.Definitions == num)
		{
			return orCreateValue;
		}
		orCreateValue.Definitions = num;
		orCreateValue.Signature = null;
		orCreateValue.Look = (MobLooks.TryGet(((Object)prefab).name, out var look) ? look : null);
		orCreateValue.Recolor = orCreateValue.Look?.Original;
		return orCreateValue;
	}

	internal static GearSignature Of(GameObject prefab)
	{
		if (!Object.op_Implicit((Object)(object)prefab))
		{
			return Note(prefab, Unclassified);
		}
		Remembered remembered = Remember(prefab);
		GameObject val = ((remembered.Recolor != null && Object.op_Implicit((Object)(object)ObjectDB.instance)) ? ObjectDB.instance.GetItemPrefab(remembered.Recolor) : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			val = prefab;
		}
		SharedData val2 = val.GetComponent<ItemDrop>()?.m_itemData?.m_shared;
		if (!remembered.Matches(val, val2))
		{
			remembered.Keep(val, val2, OfPrefab(val, val2).Done());
		}
		return Note(prefab, remembered.Signature);
	}

	private static GearSignature Note(GameObject prefab, GearSignature signature)
	{
		if (Noted == null)
		{
			return signature;
		}
		foreach (Use item in Noted)
		{
			if (item.Prefab == prefab)
			{
				return signature;
			}
		}
		Noted.Add(new Use
		{
			Prefab = prefab,
			Signature = signature
		});
		return signature;
	}

	private static Draft OfPrefab(GameObject prefab, SharedData shared)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected I4, but got Unknown
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected I4, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected I4, but got Unknown
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Invalid comparison between Unknown and I4
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d5: 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_02ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0387: Unknown result type (might be due to invalid IL or missing references)
		//IL_038d: Invalid comparison between Unknown and I4
		Draft draft = new Draft();
		if (shared == null || shared.m_maxStackSize != 1)
		{
			return draft;
		}
		draft.Stance = (int)shared.m_animationState;
		draft.Skill = (int)shared.m_skillType;
		draft.ItemType = (int)shared.m_itemType;
		draft.Primary = shared.m_attack?.m_attackAnimation ?? "";
		draft.Secondary = shared.m_secondaryAttack?.m_attackAnimation ?? "";
		draft.PrimaryChain = shared.m_attack?.m_attackChainLevels ?? 0;
		draft.SecondaryChain = shared.m_secondaryAttack?.m_attackChainLevels ?? 0;
		Attack attack = shared.m_attack;
		draft.PrimaryType = ((attack == null) ? 3 : ((int)attack.m_attackType));
		Attack secondaryAttack = shared.m_secondaryAttack;
		draft.SecondaryType = ((secondaryAttack == null) ? 3 : ((int)secondaryAttack.m_attackType));
		draft.KnownSlot = (WeaponSlot(shared.m_itemType) ? draft.ItemType : (-1));
		draft.KnownStance = (Enum.IsDefined(typeof(AnimationState), shared.m_animationState) ? draft.Stance : (-1));
		string text = InvalidLooks.Reason(prefab);
		if (text != null)
		{
			return Block(draft, text);
		}
		Compatibility.Gear(((Object)prefab).name, out var rule);
		GearKind gearKind = Kind(((Object)prefab).name, shared);
		GearKind gearKind2 = StanceKind(shared.m_animationState);
		if (gearKind == GearKind.Unsupported && !Enum.IsDefined(typeof(ItemType), shared.m_itemType) && draft.Primary.Length > 0 && gearKind2 != GearKind.Unsupported && KnownWeaponSkill(shared.m_skillType) && SkillFits(gearKind2, shared.m_skillType))
		{
			gearKind = gearKind2;
		}
		draft.Kind = gearKind;
		draft.Evidence = "Native equipment data";
		if (gearKind != GearKind.Unsupported)
		{
			if (!Fits(gearKind, shared) || (rule != null && !FamilyRules.SameFamily(rule.Family, gearKind)))
			{
				return Block(draft, "Conflicting native equipment data or family registration");
			}
		}
		else if (rule != null)
		{
			if (!Fits(rule.Family, shared) || (GearSignature.Weapon(rule.Family) && (draft.Primary.Length == 0 || !SkillFits(rule.Family, shared.m_skillType))))
			{
				return Block(draft, "Registration conflicts with equipment data");
			}
			draft.Kind = rule.Family;
			draft.Evidence = "Explicit compatibility registration";
		}
		else if ((int)shared.m_itemType == 5)
		{
			GearKind kind = WeaponNameClues.Read(((Object)prefab).name, shared.m_name).Resolve(shared);
			if (!Shield(kind))
			{
				return Block(draft, "Shield category could not be identified");
			}
			draft.Kind = kind;
			draft.Evidence = "Shield slot and name clues";
		}
		else
		{
			GearKind gearKind3 = StanceKind(shared.m_animationState);
			bool flag = !Enum.IsDefined(typeof(ItemType), shared.m_itemType) && gearKind3 != GearKind.Unsupported;
			if ((!WeaponSlot(shared.m_itemType) && !flag) || draft.Primary.Length == 0)
			{
				return Block(draft, "No supported weapon slot and attack setup");
			}
			if (KnownWeaponSkill(shared.m_skillType))
			{
				return Block(draft, "Weapon skill conflicts with equipment data");
			}
			GearKind gearKind4 = WeaponNameClues.Read(((Object)prefab).name, shared.m_name).Resolve(shared);
			if (gearKind4 == GearKind.Unsupported || (gearKind4 != GearKind.UnknownWeapon && !Fits(gearKind4, shared)))
			{
				return Block(draft, "Name clues are ambiguous or conflict with equipment data");
			}
			if (gearKind3 != GearKind.Unsupported && gearKind4 != GearKind.UnknownWeapon && gearKind3 != gearKind4)
			{
				return Block(draft, "Weapon stance and name clues disagree");
			}
			draft.Kind = ((gearKind3 != GearKind.Unsupported) ? gearKind3 : gearKind4);
			if (draft.Kind == GearKind.UnknownWeapon && (int)shared.m_itemType == 4)
			{
				draft.Kind = GearKind.Bow;
			}
			if (!Fits(draft.Kind, shared))
			{
				return Block(draft, "Weapon family conflicts with equipment position");
			}
			draft.Evidence = ((draft.Kind == GearKind.UnknownWeapon) ? "Unresolved weapon family" : ((gearKind3 != GearKind.Unsupported) ? "Weapon slot, attack and stance" : "Weapon slot, attack and name clues"));
		}
		if (rule != null && draft.Issue.Length == 0)
		{
			draft.Evidence = "Explicit compatibility registration; native boundaries checked";
		}
		if (rule != null && rule.Moveset != null && draft.IsWeapon && draft.Primary.Length > 0)
		{
			draft.Primary = "registered:" + rule.Moveset + ":primary";
			if (draft.Secondary.Length > 0)
			{
				draft.Secondary = "registered:" + rule.Moveset + ":secondary";
			}
		}
		return draft;
	}

	private static Draft Block(Draft signature, string issue)
	{
		signature.Kind = GearKind.Unsupported;
		signature.Issue = issue;
		signature.Evidence = "Unresolved or conflicting data";
		return signature;
	}

	private static bool Shield(GearKind kind)
	{
		if (kind != GearKind.Buckler && kind != GearKind.RoundShield)
		{
			return kind == GearKind.TowerShield;
		}
		return true;
	}

	private static bool WeaponSlot(ItemType type)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Invalid comparison between Unknown and I4
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Invalid comparison between Unknown and I4
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Invalid comparison between Unknown and I4
		if ((int)type != 3 && (int)type != 14 && (int)type != 22)
		{
			return (int)type == 4;
		}
		return true;
	}

	private static bool KnownWeaponSkill(SkillType skill)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected I4, but got Unknown
		switch (skill - 1)
		{
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 6:
		case 7:
		case 8:
		case 9:
		case 10:
		case 11:
		case 13:
			return true;
		default:
			return false;
		}
	}

	private static bool SkillFits(GearKind kind, SkillType skill)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected I4, but got Unknown
		switch (skill - 1)
		{
		case 0:
			if (kind != GearKind.Sword1H)
			{
				return kind == GearKind.Sword2H;
			}
			return true;
		case 6:
			if (kind != GearKind.Axe1H && kind != GearKind.Axe2H)
			{
				return kind == GearKind.DualAxes;
			}
			return true;
		case 2:
			if (kind != GearKind.Mace1H)
			{
				return kind == GearKind.Sledge;
			}
			return true;
		case 1:
			if (kind != GearKind.Knife)
			{
				return kind == GearKind.DualKnives;
			}
			return true;
		case 4:
			return kind == GearKind.Spear;
		case 3:
			return kind == GearKind.Atgeir;
		case 10:
			return kind == GearKind.Fists;
		case 7:
			return kind == GearKind.Bow;
		case 13:
			return kind == GearKind.Crossbow;
		case 8:
		case 9:
			return FamilyRules.Staff(kind);
		case 11:
			return kind == GearKind.Pickaxe;
		default:
			return true;
		}
	}

	private static GearKind StanceKind(AnimationState stance)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Invalid comparison between Unknown and I4
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Invalid comparison between Unknown and I4
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected I4, but got Unknown
		if ((int)stance != 2)
		{
			if ((int)stance != 3)
			{
				return (stance - 7) switch
				{
					6 => GearKind.Sword2H, 
					0 => GearKind.Atgeir, 
					8 => GearKind.DualAxes, 
					4 => GearKind.DualKnives, 
					3 => GearKind.Crossbow, 
					_ => GearKind.Unsupported, 
				};
			}
			return GearKind.Bow;
		}
		return GearKind.Sledge;
	}

	private static bool Fits(GearKind kind, SharedData shared)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Invalid comparison between Unknown and I4
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Invalid comparison between Unknown and I4
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Invalid comparison between Unknown and I4
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Invalid comparison between Unknown and I4
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Invalid comparison between Unknown and I4
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Invalid comparison between Unknown and I4
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Invalid comparison between Unknown and I4
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Invalid comparison between Unknown and I4
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Invalid comparison between Unknown and I4
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Invalid comparison between Unknown and I4
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Invalid comparison between Unknown and I4
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Expected I4, but got Unknown
		if (Shield(kind))
		{
			return (int)shared.m_itemType == 5;
		}
		switch (kind)
		{
		case GearKind.Chest:
			return (int)shared.m_itemType == 7;
		case GearKind.Legs:
			return (int)shared.m_itemType == 11;
		case GearKind.Cape:
			return (int)shared.m_itemType == 17;
		case GearKind.Helmet:
			return (int)shared.m_itemType == 6;
		default:
		{
			if (!GearSignature.Weapon(kind) || (!WeaponSlot(shared.m_itemType) && Enum.IsDefined(typeof(ItemType), shared.m_itemType)))
			{
				return false;
			}
			bool flag = (int)shared.m_itemType == 3;
			bool flag2 = (int)shared.m_itemType == 14 || (int)shared.m_itemType == 22;
			if ((int)shared.m_animationState == 1 && flag2)
			{
				return false;
			}
			if ((int)shared.m_animationState == 1)
			{
				flag = true;
			}
			if (flag2 && (kind == GearKind.Sword1H || kind == GearKind.Axe1H || kind == GearKind.Mace1H || kind == GearKind.Knife))
			{
				return false;
			}
			if (flag && (kind == GearKind.Sword2H || kind == GearKind.Axe2H || kind == GearKind.Sledge || kind == GearKind.DualAxes || kind == GearKind.DualKnives))
			{
				return false;
			}
			if ((int)shared.m_itemType == 4 && kind != GearKind.Bow && kind != GearKind.Crossbow)
			{
				return false;
			}
			GearKind gearKind = StanceKind(shared.m_animationState);
			if (gearKind != GearKind.Unsupported && gearKind != kind && !FamilyRules.Staff(kind))
			{
				return false;
			}
			AnimationState animationState = shared.m_animationState;
			switch (animationState - 4)
			{
			case 0:
			case 1:
			case 2:
			case 5:
				return false;
			case 4:
				if (kind != GearKind.Axe2H && kind != GearKind.Pickaxe)
				{
					return kind == GearKind.UnknownWeapon;
				}
				return true;
			case 8:
				if (!FamilyRules.Staff(kind))
				{
					return kind == GearKind.UnknownWeapon;
				}
				return true;
			default:
				return true;
			}
		}
		}
	}

	private static GearKind Kind(string prefab, SharedData shared)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: 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_002b: Expected I4, but got Unknown
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Invalid comparison between Unknown and I4
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Invalid comparison between Unknown and I4
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Invalid comparison between Unknown and I4
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Invalid comparison between Unknown and I4
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Expected I4, but got Unknown
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Invalid comparison between Unknown and I4
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Invalid comparison between Unknown and I4
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Invalid comparison between Unknown and I4
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Invalid comparison between Unknown and I4
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Invalid comparison between Unknown and I4
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Invalid comparison between Unknown and I4
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Invalid comparison between Unknown and I4
		//IL_011f: 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_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Expected I4, but got Unknown
		ItemType itemType = shared.m_itemType;
		switch (itemType - 5)
		{
		default:
			if ((int)itemType != 17)
			{
				break;
			}
			return GearKind.Cape;
		case 2:
			return GearKind.Chest;
		case 6:
			return GearKind.Legs;
		case 1:
			return GearKind.Helmet;
		case 0:
			if (!Bucklers.Contains(prefab))
			{
				if (!Towers.Contains(prefab))
				{
					if (!Rounds.Contains(prefab))
					{
						return GearKind.Unsupported;
					}
					return GearKind.RoundShield;
				}
				return GearKind.TowerShield;
			}
			return GearKind.Buckler;
		case 3:
		case 4:
		case 5:
			break;
		}
		if ((int)shared.m_itemType != 3 && (int)shared.m_itemType != 14 && (int)shared.m_itemType != 22 && (int)shared.m_itemType != 4)
		{
			return GearKind.Unsupported;
		}
		SkillType skillType = shared.m_skillType;
		switch (skillType - 8)
		{
		case 0:
			return GearKind.Bow;
		case 6:
			return GearKind.Crossbow;
		case 1:
			return GearKind.ElementalStaff;
		case 2:
			return GearKind.BloodStaff;
		case 4:
			return GearKind.Pickaxe;
		default:
		{
			AnimationState animationState = shared.m_animationState;
			if ((int)animationState != 11)
			{
				if ((int)animationState == 15)
				{
					if ((int)shared.m_skillType != 7)
					{
						return GearKind.Unsupported;
					}
					return GearKind.DualAxes;
				}
				bool flag = (int)shared.m_itemType == 14 || (int)shared.m_itemType == 22;
				skillType = shared.m_skillType;
				switch (skillType - 1)
				{
				case 0:
					if (!flag)
					{
						return GearKind.Sword1H;
					}
					return GearKind.Sword2H;
				case 6:
					if (!flag)
					{
						return GearKind.Axe1H;
					}
					return GearKind.Axe2H;
				case 2:
					if (!flag)
					{
						return GearKind.Mace1H;
					}
					return GearKind.Sledge;
				case 1:
					if (!flag)
					{
						return GearKind.Knife;
					}
					return GearKind.Unsupported;
				case 4:
					return GearKind.Spear;
				case 3:
					return GearKind.Atgeir;
				case 10:
					return GearKind.Fists;
				default:
					return GearKind.Unsupported;
				}
			}
			if ((int)shared.m_skillType != 2)
			{
				return GearKind.Unsupported;
			}
			return GearKind.DualKnives;
		}
		}
	}

	internal static bool Eligible(ItemData item)
	{
		if (item == null || item.m_stack != 1 || item.m_shared == null || item.m_shared.m_maxStackSize != 1)
		{
			return false;
		}
		return Of(item).Kind != GearKind.Unsupported;
	}

	internal static bool Pair(ItemData original, ItemData donor, out string reason)
	{
		if (!Eligible(original) || !Eligible(donor))
		{
			reason = "This item can’t be used as an appearance.";
			return false;
		}
		return PairPrefabs(original.m_dropPrefab, donor.m_dropPrefab, out reason);
	}

	internal static bool PairPrefabs(GameObject original, GameObject donor, out string reason, bool savedAppearance = false)
	{
		MobLook mobLook = (Object.op_Implicit((Object)(object)donor) ? Remember(donor).Look : null);
		if (mobLook != null && !mobLook.Recolor)
		{
			return PairMob(original, mobLook, out reason);
		}
		GearSignature gearSignature = Of(original);
		GearSignature gearSignature2 = Of(donor);
		if ((!Object.op_Implicit((Object)(object)original) || !((Object)(object)original == (Object)(object)donor) || gearSignature.Kind == GearKind.Unsupported) && !FamilyRules.Compatible(gearSignature, gearSignature2, savedAppearance || AllowUnknown))
		{
			reason = ((gearSignature.Kind == GearKind.Unsupported || gearSignature2.Kind == GearKind.Unsupported) ? "This item’s type could not be matched safely. Choose a different appearance." : ((gearSignature.Kind == GearKind.UnknownWeapon && gearSignature2.Kind == GearKind.UnknownWeapon) ? ((!savedAppearance && !AllowUnknown) ? "These weapon types are unknown. Enable Allow mixing of unknown types in settings to use them together." : "These weapons use different equipment positions. Choose a different appearance.") : ((!FamilyRules.SameFamily(gearSignature.Kind, gearSignature2.Kind)) ? FamilyRules.Requirement(gearSignature.Kind) : ((FamilyRules.Staff(gearSignature.Kind) && (gearSignature.ItemType != gearSignature2.ItemType || gearSignature.Stance != gearSignature2.Stance)) ? "These staffs are held differently. Choose a different appearance." : "These weapons use different attacks. Choose a different appearance."))));
			return false;
		}
		reason = "";
		return true;
	}

	private static bool PairMob(GameObject original, MobLook mob, out string reason)
	{
		GearSignature gearSignature = Of(original);
		if (!mob.Dresses(gearSignature.Kind))
		{
			List<string> list = new List<string>();
			GearKind[] families = mob.Families;
			foreach (GearKind kind in families)
			{
				list.Add(FamilyRules.Label(kind).ToLowerInvariant());
			}
			reason = "This appearance only fits: " + string.Join(", ", list) + ".";
			return false;
		}
		if (FamilyRules.Staff(gearSignature.Kind))
		{
			GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(mob.HeldLike) : null);
			if (!Object.op_Implicit((Object)(object)val) || !FamilyRules.Compatible(gearSignature, Of(val)))
			{
				reason = "This appearance only fits staffs held like the " + AppearanceCollection.ItemName(mob.HeldLike) + ".";
				return false;
			}
		}
		reason = "";
		return true;
	}
}
internal sealed class CollectedAppearance : IEquatable<CollectedAppearance>
{
	internal readonly string Prefab;

	internal readonly int Variant;

	internal readonly int Quality;

	internal bool Valid
	{
		get
		{
			if (string.IsNullOrWhiteSpace(Prefab) || Prefab.Length > 160 || Variant < 0 || Variant > 65535 || Quality < 1 || Quality > 65535)
			{
				return false;
			}
			string prefab = Prefab;
			for (int i = 0; i < prefab.Length; i++)
			{
				if (char.IsControl(prefab[i]))
				{
					return false;
				}
			}
			return true;
		}
	}

	internal CollectedAppearance(string prefab, int variant, int quality)
	{
		Prefab = prefab;
		Variant = variant;
		Quality = quality;
	}

	public bool Equals(CollectedAppearance other)
	{
		if (other != null && Prefab == other.Prefab && Variant == other.Variant)
		{
			return Quality == other.Quality;
		}
		return false;
	}

	public override bool Equals(object other)
	{
		return Equals(other as CollectedAppearance);
	}

	public override int GetHashCode()
	{
		return (((((Prefab != null) ? StringComparer.Ordinal.GetHashCode(Prefab) : 0) * 397) ^ Variant) * 397) ^ Quality;
	}

	internal string Encode()
	{
		string[] obj = new string[5]
		{
			Convert.ToBase64String(Encoding.UTF8.GetBytes(Prefab)),
			"|",
			null,
			null,
			null
		};
		int variant = Variant;
		obj[2] = variant.ToString(CultureInfo.InvariantCulture);
		obj[3] = "|";
		variant = Quality;
		obj[4] = variant.ToString(CultureInfo.InvariantCulture);
		return string.Concat(obj);
	}

	internal static bool TryDecode(string line, out CollectedAppearance value)
	{
		value = null;
		string[] array = line.Split(new char[1] { '|' });
		if (array.Length != 3 || array[0].Length > 860 || !int.TryParse(array[1], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || !int.TryParse(array[2], NumberStyles.None, CultureInfo.InvariantCulture, out var result2))
		{
			return false;
		}
		try
		{
			CollectedAppearance collectedAppearance = new CollectedAppearance(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(array[0])), result, result2);
			if (!collectedAppearance.Valid)
			{
				return false;
			}
			value = collectedAppearance;
			return true;
		}
		catch (FormatException)
		{
			return false;
		}
		catch (DecoderFallbackException)
		{
			return false;
		}
	}
}
internal sealed class AppearanceJournal
{
	internal const string Key = "gnls.GooCosmetics.collection.v1";

	internal const int MaxEntries = 8192;

	internal const int MaxLength = 2097152;

	private readonly HashSet<CollectedAppearance> entries = new HashSet<CollectedAppearance>();

	private int encodedLength = 2;

	internal IEnumerable<CollectedAppearance> Entries => entries;

	internal int Count => entries.Count;

	internal bool Contains(CollectedAppearance entry)
	{
		if (entry != null)
		{
			return entries.Contains(entry);
		}
		return false;
	}

	internal bool Add(CollectedAppearance entry)
	{
		if (entry == null || !entry.Valid || entries.Count >= 8192 || entries.Contains(entry))
		{
			return false;
		}
		int num = entry.Encode().Length + ((entries.Count != 0) ? 1 : 0);
		if (encodedLength + num > 2097152)
		{
			return false;
		}
		entries.Add(entry);
		encodedLength += num;
		return true;
	}

	internal bool Remove(CollectedAppearance entry)
	{
		if (entry == null || !entries.Remove(entry))
		{
			return false;
		}
		encodedLength -= entry.Encode().Length + ((entries.Count != 0) ? 1 : 0);
		return true;
	}

	internal string Encode()
	{
		List<string> list = new List<string>();
		foreach (CollectedAppearance entry in entries)
		{
			list.Add(entry.Encode());
		}
		list.Sort(StringComparer.Ordinal);
		return "1\n" + string.Join("\n", list);
	}

	internal static bool TryDecode(string raw, out AppearanceJournal journal)
	{
		journal = new AppearanceJournal();
		if (string.IsNullOrEmpty(raw))
		{
			return true;
		}
		if (raw.Length > 2097152 || !raw.StartsWith("1\n", StringComparison.Ordinal))
		{
			return false;
		}
		if (raw.Length == 2)
		{
			return true;
		}
		string[] array = raw.Substring(2).Split(new char[1] { '\n' });
		if (array.Length > 8192)
		{
			return false;
		}
		AppearanceJournal appearanceJournal = new AppearanceJournal();
		string[] array2 = array;
		for (int i = 0; i < array2.Length; i++)
		{
			if (!CollectedAppearance.TryDecode(array2[i], out var value))
			{
				return false;
			}
			appearanceJournal.Add(value);
		}
		journal = appearanceJournal;
		return true;
	}
}
internal static class CollectionHooks
{
	internal static void CheckContracts()
	{
		Native.Method(typeof(Player), "Load", typeof(ZPackage));
		Native.Method(typeof(Player), "Save", typeof(ZPackage));
		Native.Method(typeof(Player), "AddKnownItem", typeof(ItemData));
		Native.Method(typeof(ObjectDB), "Awake");
		Native.Method(typeof(ObjectDB), "UpdateRegisters");
		if (AccessTools.Field(typeof(Player), "m_customData") == null)
		{
			throw new MissingFieldException("Player.m_customData");
		}
	}
}
[HarmonyPatch(typeof(Player), "Load", new Type[] { typeof(ZPackage) })]
internal static class CollectionLoadPatch
{
	internal sealed class Snapshot
	{
		internal AppearanceCollection.LoadState Collection;

		internal string Favorites;

		internal string NewLooks;

		internal bool FavoritesStarted;

		internal bool NewLooksStarted;
	}

	[HarmonyPrefix]
	private static void Prefix(Player __instance, out Snapshot __state)
	{
		__state = new Snapshot();
		ModdedTrophies.Discover(ObjectDB.instance);
		try
		{
			__state.Collection = AppearanceCollection.BeginLoad(__instance);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Prepare saved appearances", error);
		}
		try
		{
			__state.Favorites = FavoritesStore.BeginLoad(__instance);
			__state.FavoritesStarted = true;
		}
		catch (Exception error2)
		{
			Plugin.WarnOnce("Prepare saved favorites", error2);
		}
		try
		{
			__state.NewLooks = NewLooks.BeginLoad(__instance);
			__state.NewLooksStarted = true;
		}
		catch (Exception error3)
		{
			Plugin.WarnOnce("Prepare new monster looks", error3);
		}
	}

	[HarmonyFinalizer]
	private static Exception Finalizer(Player __instance, Snapshot __state, Exception __exception)
	{
		try
		{
			AppearanceCollection.EndLoad(__instance, __state?.Collection, __exception == null);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Load saved appearances", error);
		}
		try
		{
			if (__state != null && __state.FavoritesStarted)
			{
				FavoritesStore.EndLoad(__instance, __state.Favorites, __exception == null);
			}
		}
		catch (Exception error2)
		{
			Plugin.WarnOnce("Load saved favorites", error2);
		}
		try
		{
			if (__state != null && __state.NewLooksStarted)
			{
				NewLooks.EndLoad(__instance, __state.NewLooks, __exception == null);
			}
		}
		catch (Exception error3)
		{
			Plugin.WarnOnce("Load new monster looks", error3);
		}
		return __exception;
	}
}
[HarmonyPatch(typeof(Player), "Save", new Type[] { typeof(ZPackage) })]
internal static class CollectionSavePatch
{
	[HarmonyPrefix]
	private static void Prefix(Player __instance)
	{
		try
		{
			AppearanceCollection.Saving(__instance);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Save appearances", error);
		}
		try
		{
			FavoritesStore.Saving(__instance);
		}
		catch (Exception error2)
		{
			Plugin.WarnOnce("Save favorites", error2);
		}
	}
}
[HarmonyPatch(typeof(Player), "AddKnownItem", new Type[] { typeof(ItemData) })]
internal static class CollectionObtainPatch
{
	internal struct Pickup
	{
		internal List<MobLook> Fresh;

		internal bool FirstUnlockItem;
	}

	[HarmonyPrefix]
	private static void Prefix(Player __instance, ItemData item, out Pickup __state)
	{
		__state = default(Pickup);
		try
		{
			__state.FirstUnlockItem = AppearanceCollection.FirstUnlockItem(__instance, item);
			if (__state.FirstUnlockItem && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				__state.Fresh = NewLooks.Unlocking(__instance, item);
			}
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Detect new monster looks", error);
		}
	}

	[HarmonyPostfix]
	private static void Postfix(Player __instance, ItemData item, Pickup __state)
	{
		try
		{
			AppearanceCollection.Observe(__instance, item);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Collect appearance", error);
		}
		try
		{
			if (__state.FirstUnlockItem)
			{
				AppearanceCollection.ObserveUnlock(__instance, item);
			}
		}
		catch (Exception error2)
		{
			Plugin.WarnOnce("Unlock monster appearance", error2);
		}
		if (__state.Fresh == null)
		{
			return;
		}
		try
		{
			NewLooks.Add(__instance, __state.Fresh);
			UnlockNotices.Queue(__instance, __state.Fresh);
		}
		catch (Exception error3)
		{
			Plugin.WarnOnce("Announce new monster looks", error3);
		}
	}
}
[HarmonyPatch(typeof(ObjectDB), "Awake")]
internal static class VanillaAppearanceCatalogPatch
{
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static void Prefix(ObjectDB __instance)
	{
		try
		{
			AppearanceOrder.CaptureVanilla(__instance);
		}
		catch (Exception error)
		{
			Plugin.WarnOnce("Vanilla appearance order", error);
		}
	}

	[HarmonyPostfix]
	[HarmonyPriority(0)]
	private static void Postfix(ObjectDB __instance)
	{
		ModdedTrophies.Discover(__instance);
	}
}
[HarmonyPatch(typeof(ObjectDB), "UpdateRegisters")]
internal static class ModdedTrophyCatalogPatch
{
	[HarmonyPostfix]
	[HarmonyPriority(0)]
	private static void Postfix(ObjectDB __instance)
	{
		ModdedTrophies.Discover(__instance);
	}
}
internal static class CollectionPages
{
	internal const int Columns = 4;

	internal const int Rows = 6;

	internal const int Size = 24;

	internal static int Count(int items)
	{
		return Math.Max(1, (Math.Max(0, items) + 24 - 1) / 24);
	}

	internal static int Clamp(int page, int items)
	{
		return Math.Max(0, Math.Min(page, Count(items) - 1));
	}

	internal static int Index(int page, int cell)
	{
		return page * 24 + cell;
	}
}
internal static class CollectionWindow
{
	private sealed class Panel
	{
		private readonly bool particleCollection;

		private GameObject root;

		private WindowDrag drag;

		private NativeItemCell[] cells;

		private TMP_InputField search;

		private Toggle favorites;

		private Toggle showEnemyGear;

		private TMP_Text subtitle;

		private TMP_Text pageLabel;

		private TMP_Text empty;

		private Button previous;

		private Button next;

		private List<CollectedAppearance> entries = new List<CollectedAppearance>();

		private List<CollectedAppearance> matching = new List<CollectedAppearance>();

		private List<CollectedAppearance> locked = new List<CollectedAppearance>();

		private readonly HashSet<CollectedAppearance> lockedLooks = new HashSet<CollectedAppearance>();

		private readonly HashSet<string> shownNew = new HashSet<string>(StringComparer.Ordinal);

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

		private int page;

		private int journalRevision = -1;

		private int visualRevision = -1;

		private int wardrobeRevision = -1;

		private bool allowUnknown;

		private bool lockedShown;

		private bool enemyShown;

		private bool enemyHidden;

		private bool iconsPending;

		private ItemData target;

		internal FocusLayer Layer;

		private CollectedAppearance Selected
		{
			get
			{
				if (!particleCollection)
				{
					return IllusionWindow.Selected;
				}
				return IllusionWindow.SelectedParticles;
			}
		}

		internal bool IsOpen => Object.op_Implicit((Object)(object)root);

		internal int Depth
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)root))
				{
					return -1;
				}
				return root.transform.GetSiblingIndex();
			}
		}

		private GearKind TargetKind
		{
			get
			{
				if (target == null)
				{
					return GearKind.Unsupported;
				}
				return Classification.Of(target).Kind;
			}
		}

		internal Panel(bool particles)
		{
			particleCollection = particles;
		}

		internal bool CoversPointer(Vector2 position)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)drag))
			{
				return drag.Contains(position);
			}
			return false;
		}

		internal void Toggle()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			//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_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: 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_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Unkn