Decompiled source of SeneaL API v1.0.1

patchers/SeneaL-API/SeneaL-API-Early.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;

[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: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SeneaL.API.Early
{
	public static class EarlyPatcher
	{
		public static IEnumerable<string> TargetDLLs => new string[0];

		public static void Patch(AssemblyDefinition assembly)
		{
		}

		public static void Initialize()
		{
			StubBridge.Start();
		}
	}
	public sealed class StubCall
	{
		public string Owner;

		public string Method;

		public object[] Args;

		public Type[] Types;
	}
	public static class StubBridge
	{
		public const string RealPlugin = "AzuEPI.AzuExtendedPlayerInventoryPlugin";

		public const string DefaultIgnore = "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot";

		public const string OldIgnore = "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot,Backpacks,RustyBags,Hunter_Legacy,Magic_Supremacy,MagicPlugin,Wizardry";

		public const string OldIgnore2 = "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot,Magic_Supremacy";

		private static readonly List<StubCall> Calls = new List<StubCall>(8);

		private static readonly HashSet<MethodBase> Done = new HashSet<MethodBase>();

		private static readonly Dictionary<string, Assembly> Origins = new Dictionary<string, Assembly>(8, StringComparer.OrdinalIgnoreCase);

		private static readonly string[] Names = new string[3] { "IsLoaded", "AddSlot", "RemoveSlot" };

		private static ManualLogSource _log;

		private static Harmony _harmony;

		private static int _files = -1;

		private static string[] _ignore;

		private static bool _off;

		public static Action Wake;

		public static int Patched => Done.Count;

		public static void Start()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			if (_harmony != null)
			{
				return;
			}
			_log = Logger.CreateLogSource("SeneaL API Early");
			ReadConfig();
			if (_off)
			{
				_log.LogInfo((object)"embedded AzuEPI API stubs are left alone (Bridge / AzuEPIStubs = false)");
				return;
			}
			_harmony = new Harmony("seneaL.valheim.api.early");
			AppDomain.CurrentDomain.AssemblyLoad += delegate(object s, AssemblyLoadEventArgs e)
			{
				Scan(e.LoadedAssembly);
			};
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int num = 0; num < assemblies.Length; num++)
			{
				Scan(assemblies[num]);
			}
		}

		private static void ReadConfig()
		{
			string text = "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot";
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "seneaL.valheim.api.cfg");
				if (File.Exists(path))
				{
					string[] array = File.ReadAllLines(path);
					for (int i = 0; i < array.Length; i++)
					{
						string text2 = array[i].Trim();
						if (text2.StartsWith("AzuEPIStubIgnore", StringComparison.OrdinalIgnoreCase) && text2.IndexOf('=') > 0)
						{
							text = text2.Substring(text2.IndexOf('=') + 1).Trim();
						}
						else if (text2.StartsWith("AzuEPIStubs", StringComparison.OrdinalIgnoreCase) && text2.IndexOf('=') > 0)
						{
							_off = text2.Substring(text2.IndexOf('=') + 1).Trim().Equals("false", StringComparison.OrdinalIgnoreCase);
						}
					}
				}
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("config: " + ex.Message));
			}
			if (text == "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot,Backpacks,RustyBags,Hunter_Legacy,Magic_Supremacy,MagicPlugin,Wizardry" || text == "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot,Magic_Supremacy")
			{
				text = "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot";
			}
			_ignore = (from n in text.Split(new char[1] { ',' })
				select n.Trim().ToLowerInvariant() into n
				where n.Length > 0
				select n).ToArray();
		}

		public static StubCall[] Take()
		{
			lock (Calls)
			{
				StubCall[] result = Calls.ToArray();
				Calls.Clear();
				return result;
			}
		}

		public static bool Real(Assembly a)
		{
			try
			{
				return a.GetType("AzuEPI.AzuExtendedPlayerInventoryPlugin", throwOnError: false) != null;
			}
			catch
			{
				return false;
			}
		}

		public static void Scan(Assembly a)
		{
			try
			{
				if (a == null || a.IsDynamic)
				{
					return;
				}
				if (Real(a))
				{
					WatchReal(a);
					return;
				}
				string name = a.GetName().Name;
				if (Array.IndexOf(_ignore, name.ToLowerInvariant()) >= 0)
				{
					if (a.GetType("AzuEPI.API", throwOnError: false) != null)
					{
						_log.LogInfo((object)("the AzuEPI API stub in " + name + " is left alone (Bridge / AzuEPIStubIgnore)"));
					}
				}
				else
				{
					Patch(a.GetType("AzuEPI.API", throwOnError: false), a);
					Patch(a.GetType("AzuExtendedPlayerInventory.API", throwOnError: false), a);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("scan " + a?.GetName().Name + ": " + ex.GetType().Name + " " + ex.Message));
				}
			}
		}

		public static Assembly OriginOf(string slot)
		{
			lock (Origins)
			{
				Assembly value;
				return (!string.IsNullOrEmpty(slot) && Origins.TryGetValue(slot, out value)) ? value : null;
			}
		}

		internal static void Note(string slot, Assembly caller, bool remove)
		{
			if (string.IsNullOrEmpty(slot))
			{
				return;
			}
			lock (Origins)
			{
				if (remove)
				{
					Origins.Remove(slot);
				}
				else if (caller != null)
				{
					Origins[slot] = caller;
				}
			}
		}

		private static void WatchReal(Assembly a)
		{
			WatchType(a.GetType("AzuEPI.API", throwOnError: false));
			WatchType(a.GetType("AzuExtendedPlayerInventory.API", throwOnError: false));
		}

		private static void WatchType(Type t)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			if (t == null)
			{
				return;
			}
			MethodInfo[] methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo in methods)
			{
				if ((!(methodInfo.Name != "AddSlot") || !(methodInfo.Name != "RemoveSlot")) && Done.Add(methodInfo))
				{
					try
					{
						_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Hooks).GetMethod("Real", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					catch (Exception ex)
					{
						_log.LogWarning((object)("watch " + methodInfo.Name + ": " + ex.GetType().Name + " " + ex.Message));
					}
				}
			}
		}

		private static void Patch(Type t, Assembly a)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			if (t == null)
			{
				return;
			}
			MethodInfo[] methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo in methods)
			{
				if (Array.IndexOf(Names, methodInfo.Name) < 0 || methodInfo.ReturnType != typeof(bool) || !Done.Add(methodInfo))
				{
					continue;
				}
				try
				{
					MethodInfo method = typeof(Hooks).GetMethod((methodInfo.Name == "IsLoaded") ? "IsLoaded" : "Call", BindingFlags.Static | BindingFlags.NonPublic);
					_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					_log.LogInfo((object)("stub " + t.FullName + "." + methodInfo.Name + "(" + string.Join(", ", (from p in methodInfo.GetParameters()
						select p.ParameterType.Name).ToArray()) + ") in " + a.GetName().Name + " is routed to SeneaL API"));
				}
				catch (Exception ex)
				{
					_log.LogWarning((object)("patch " + methodInfo.Name + ": " + ex.GetType().Name + " " + ex.Message));
				}
			}
		}

		public static bool Answer()
		{
			if (_files < 0)
			{
				_files = 0;
				try
				{
					string[] files = Directory.GetFiles(Paths.PluginPath, "*.dll", SearchOption.AllDirectories);
					for (int i = 0; i < files.Length; i++)
					{
						string fileName = Path.GetFileName(files[i]);
						if (string.Equals(fileName, "AzuExtendedPlayerInventory.dll", StringComparison.OrdinalIgnoreCase))
						{
							_files |= 1;
						}
						else if (string.Equals(fileName, "SeneaL-UI.dll", StringComparison.OrdinalIgnoreCase))
						{
							_files |= 2;
						}
					}
				}
				catch (Exception ex)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)("plugin scan: " + ex.Message));
					}
				}
			}
			return _files != 0;
		}

		internal static void Enqueue(MethodBase m, object[] args)
		{
			lock (Calls)
			{
				Calls.Add(new StubCall
				{
					Owner = m.DeclaringType.Assembly.GetName().Name,
					Method = m.Name,
					Args = args,
					Types = (from p in m.GetParameters()
						select p.ParameterType).ToArray()
				});
			}
			Wake?.Invoke();
		}
	}
	internal static class Hooks
	{
		private static void Real(MethodBase __originalMethod, object[] __args)
		{
			try
			{
				string text = ((__args != null && __args.Length != 0) ? (__args[0] as string) : null);
				if (!string.IsNullOrEmpty(text))
				{
					StubBridge.Note(text, Caller(__originalMethod.DeclaringType.Assembly), __originalMethod.Name == "RemoveSlot");
				}
			}
			catch
			{
			}
		}

		private static Assembly Caller(Assembly real)
		{
			StackTrace stackTrace = new StackTrace(1, fNeedFileInfo: false);
			for (int i = 0; i < stackTrace.FrameCount; i++)
			{
				Assembly assembly = stackTrace.GetFrame(i).GetMethod()?.DeclaringType?.Assembly;
				if (assembly == null || assembly.IsDynamic || assembly == real || assembly == typeof(Hooks).Assembly)
				{
					continue;
				}
				string name = assembly.GetName().Name;
				switch (name)
				{
				case "mscorlib":
				case "netstandard":
				case "BepInEx":
					continue;
				}
				if (!name.StartsWith("System", StringComparison.Ordinal) && !name.StartsWith("0Harmony", StringComparison.Ordinal) && !name.StartsWith("MonoMod", StringComparison.Ordinal) && !name.StartsWith("Unity", StringComparison.Ordinal) && !name.StartsWith("DMD", StringComparison.Ordinal))
				{
					if (!(name == "SeneaL-API") && !name.StartsWith("assembly_", StringComparison.Ordinal))
					{
						return assembly;
					}
					return null;
				}
			}
			return null;
		}

		private static bool IsLoaded(ref bool __result)
		{
			__result = StubBridge.Answer();
			return false;
		}

		private static bool Call(MethodBase __originalMethod, object[] __args, ref bool __result)
		{
			StubBridge.Enqueue(__originalMethod, __args);
			__result = true;
			return false;
		}
	}
}

plugins/SeneaL-API/SeneaL-API.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SeneaL.API.Early;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SeneaL-API")]
[assembly: AssemblyProduct("SeneaL-API")]
[assembly: AssemblyCompany("seneaL")]
[assembly: AssemblyCopyright("seneaL")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SeneaL.API
{
	public static class Api
	{
		public const string Guid = "seneaL.valheim.api";

		public const string Name = "SeneaL API";

		public const int Contract = 2;

		public static readonly string[] Features = new string[16]
		{
			"notices", "dialogs", "hud", "slots", "slots.linked", "azuepi", "hud.placed", "slots.use", "slots.time", "slots.place",
			"slots.icon", "theme", "dialogs.choice", "theme.icons", "settings", "slots.handlers"
		};

		public static string Version => "1.0.1";

		public static bool Has(string feature)
		{
			return Array.IndexOf(Features, feature) >= 0;
		}
	}
	internal static class AzuBridge
	{
		private sealed class Entry
		{
			public SlotDef Def;

			public Func<Player, ItemData> Get;

			public Func<ItemData, bool> Valid;
		}

		public const string Guid = "Azumatt.AzuExtendedPlayerInventory";

		private static readonly Dictionary<string, Entry> Known = new Dictionary<string, Entry>(8);

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

		private static readonly HashSet<string> Seen = new HashSet<string>();

		private static Type _bound;

		private static MethodInfo _slots;

		private static MethodInfo _desc;

		private static bool _warned;

		public static Func<Assembly, string> NameOf;

		public static int Count => Known.Count;

		public static bool Bound => _bound != null;

		public static bool Bind(Type api)
		{
			if (api == null)
			{
				if (_bound != null)
				{
					Clear();
				}
				return false;
			}
			if ((object)api == _bound)
			{
				return true;
			}
			Clear();
			MethodInfo method = api.GetMethod("GetSlots", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null);
			MethodInfo method2 = api.GetMethod("TryGetSlotDescriptor", BindingFlags.Static | BindingFlags.Public);
			if (method == null || method2 == null || method2.GetParameters().Length != 2)
			{
				if (!_warned)
				{
					_warned = true;
					Plugin.Warn("AzuEPI is installed, but its slot API has another shape than this bridge knows; its slots are not shown.");
				}
				return false;
			}
			_slots = method;
			_desc = method2;
			_bound = api;
			return true;
		}

		private static string OriginOf(Delegate fn, Type api)
		{
			Assembly assembly = fn?.Method.DeclaringType?.Assembly;
			if (assembly == null || (api != null && assembly == api.Assembly))
			{
				return null;
			}
			if (NameOf == null)
			{
				return assembly.GetName().Name;
			}
			return NameOf(assembly);
		}

		public static void Clear()
		{
			CustomSlots.RemoveAll("Azumatt.AzuExtendedPlayerInventory");
			Known.Clear();
			_bound = null;
			_slots = (_desc = null);
		}

		public static void Sync()
		{
			if (_bound == null)
			{
				return;
			}
			object obj;
			try
			{
				obj = _slots.Invoke(null, null);
			}
			catch (Exception e)
			{
				Plugin.Fail("AzuEPI GetSlots", e);
				return;
			}
			if (obj == null)
			{
				return;
			}
			Type type = obj.GetType();
			string[] array = (string[])type.GetProperty("SlotNames")?.GetValue(obj, null);
			string[] array2 = (string[])type.GetProperty("OriginalSlotNames")?.GetValue(obj, null);
			Array array3 = (Array)(type.GetProperty("GetItemFuncs")?.GetValue(obj, null));
			Array array4 = (Array)(type.GetProperty("IsValidFuncs")?.GetValue(obj, null));
			if (array == null || array2 == null)
			{
				return;
			}
			Seen.Clear();
			for (int i = 0; i < array.Length; i++)
			{
				object[] array5 = new object[2] { i, null };
				if (!(bool)_desc.Invoke(null, array5) || array5[1] == null)
				{
					continue;
				}
				object obj2 = array5[1];
				Type type2 = obj2.GetType();
				if (!(bool)type2.GetProperty("IsCustom").GetValue(obj2, null) || (bool)type2.GetProperty("IsQuickSlot").GetValue(obj2, null))
				{
					continue;
				}
				string text = (string.IsNullOrEmpty(array2[i]) ? array[i] : array2[i]);
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				Seen.Add(text);
				Func<Player, ItemData> func = ((array3 != null && i < array3.Length) ? (array3.GetValue(i) as Func<Player, ItemData>) : null);
				Func<ItemData, bool> valid = ((array4 != null && i < array4.Length) ? (array4.GetValue(i) as Func<ItemData, bool>) : null);
				if (!Known.TryGetValue(text, out var value))
				{
					Entry entry;
					value = (entry = new Entry());
					value.Def = new SlotDef(text)
					{
						Title = array[i],
						Origin = (SlotOrigins.Get(text) ?? SlotOrigins.Get(array[i]) ?? OriginOf(func, _bound)),
						Mode = SlotMode.Linked,
						Order = 1000 + i,
						Source = (Player p) => entry.Get?.Invoke(p),
						Accepts = (ItemData it) => entry.Valid == null || entry.Valid(it)
					};
					value.Get = func;
					value.Valid = valid;
					Known[text] = value;
					CustomSlots.Add("Azumatt.AzuExtendedPlayerInventory", value.Def);
				}
				else
				{
					value.Get = func;
					value.Valid = valid;
					if (string.IsNullOrEmpty(value.Def.Origin))
					{
						value.Def.Origin = SlotOrigins.Get(text) ?? SlotOrigins.Get(array[i]) ?? OriginOf(func, _bound);
					}
					if (value.Def.Title != array[i])
					{
						value.Def.Title = array[i];
					}
				}
			}
			Gone.Clear();
			foreach (string key in Known.Keys)
			{
				if (!Seen.Contains(key))
				{
					Gone.Add(key);
				}
			}
			for (int num = 0; num < Gone.Count; num++)
			{
				Known.Remove(Gone[num]);
				CustomSlots.Remove("Azumatt.AzuExtendedPlayerInventory", Gone[num]);
			}
		}
	}
	internal static class SlotOrigins
	{
		private static readonly Dictionary<string, string> Names = new Dictionary<string, string>(8, StringComparer.OrdinalIgnoreCase);

		public static Func<string, string> Fallback;

		public static int Count => Names.Count;

		public static void Record(string slot, string mod)
		{
			if (!string.IsNullOrEmpty(slot) && !string.IsNullOrEmpty(mod))
			{
				Names[slot] = mod;
			}
		}

		public static void Forget(string slot)
		{
			if (!string.IsNullOrEmpty(slot))
			{
				Names.Remove(slot);
			}
		}

		public static string Get(string slot)
		{
			if (!string.IsNullOrEmpty(slot))
			{
				if (!Names.TryGetValue(slot, out var value))
				{
					return Fallback?.Invoke(slot);
				}
				return value;
			}
			return null;
		}

		public static void Clear()
		{
			Names.Clear();
		}

		public static Assembly Pick(IList<Assembly> frames, Assembly own, Func<Assembly, bool> skip)
		{
			for (int i = 0; i < frames.Count; i++)
			{
				Assembly assembly = frames[i];
				if (!(assembly == null) && (skip == null || !skip(assembly)))
				{
					if (!(assembly == own))
					{
						return assembly;
					}
					return null;
				}
			}
			return null;
		}
	}
	internal static class StubHost
	{
		private sealed class Emu
		{
			public SlotDef Def;

			public readonly List<Func<Player, ItemData>> Gets = new List<Func<Player, ItemData>>(2);

			public readonly List<Func<ItemData, bool>> Valids = new List<Func<ItemData, bool>>(2);
		}

		public const string Owner = "AzuEPI.emulated";

		private static readonly Dictionary<string, Emu> Emus = new Dictionary<string, Emu>(8);

		private static bool _on;

		public static void Start()
		{
			if (!_on && AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "SeneaL-API-Early"))
			{
				_on = true;
				Hook();
			}
		}

		private static void Hook()
		{
			StubBridge.Wake = Drain;
			Plugin.Log.LogInfo((object)("AzuEPI API stubs embedded in other mods: " + StubBridge.Patched + " methods routed here"));
			Drain();
		}

		private static void Drain()
		{
			StubCall[] array = StubBridge.Take();
			foreach (StubCall val in array)
			{
				try
				{
					Handle(val);
				}
				catch (Exception e)
				{
					Plugin.Fail("AzuEPI stub " + val.Owner + "." + val.Method, e);
				}
			}
		}

		private static void Handle(StubCall c)
		{
			Plugin.Trace("AzuEPI stub " + c.Owner + "." + c.Method + " " + ((c.Args.Length != 0) ? c.Args[0] : ""));
			if (c.Args.Length != 0 && c.Args[0] is string slot)
			{
				if (c.Method == "AddSlot")
				{
					SlotOrigins.Record(slot, PluginName(c.Owner));
				}
				else if (c.Method == "RemoveSlot")
				{
					SlotOrigins.Forget(slot);
				}
			}
			if (Chainloader.PluginInfos.TryGetValue("Azumatt.AzuExtendedPlayerInventory", out var value) && Object.op_Implicit((Object)(object)value.Instance))
			{
				MethodInfo methodInfo = ((object)value.Instance).GetType().Assembly.GetType("AzuEPI.API")?.GetMethod(c.Method, c.Types);
				if (methodInfo != null)
				{
					methodInfo.Invoke(null, c.Args);
					return;
				}
			}
			if (!CustomSlots.AzuManaged)
			{
				Emulate(c);
			}
		}

		private static void Emulate(StubCall c)
		{
			string text = ((c.Args.Length != 0) ? (c.Args[0] as string) : null);
			if (string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			if (c.Method == "RemoveSlot")
			{
				Emus.Remove(text);
				CustomSlots.Remove("AzuEPI.emulated", text);
				return;
			}
			Func<Player, ItemData> func = null;
			Func<ItemData, bool> valid = null;
			if (c.Args.Length == 4 && c.Args[1] is Func<Player, ItemData> func2)
			{
				func = func2;
				valid = c.Args[2] as Func<ItemData, bool>;
			}
			else
			{
				if (c.Args.Length == 3)
				{
					object obj = c.Args[1];
					string prefab = obj as string;
					if (prefab != null)
					{
						valid = (ItemData it) => it != null && Object.op_Implicit((Object)(object)it.m_dropPrefab) && ((Object)it.m_dropPrefab).name == prefab;
						goto IL_0170;
					}
				}
				if (c.Args.Length == 3 && c.Args[1] is IEnumerable<string> source)
				{
					HashSet<string> set = new HashSet<string>(source.Where((string n) => !string.IsNullOrWhiteSpace(n)));
					valid = (ItemData it) => it != null && Object.op_Implicit((Object)(object)it.m_dropPrefab) && set.Contains(((Object)it.m_dropPrefab).name);
				}
				else if (c.Args.Length == 4 && c.Args[1] is Func<ItemData, bool> func3)
				{
					valid = func3;
				}
			}
			goto IL_0170;
			IL_0170:
			if (func == null && valid == null)
			{
				return;
			}
			if (!Emus.TryGetValue(text, out var value))
			{
				Emu emu;
				value = (emu = new Emu());
				value.Def = new SlotDef(text)
				{
					Title = text,
					Origin = PluginName(c.Owner),
					Mode = SlotMode.Linked,
					Order = 2000 + Emus.Count,
					Source = (Player p) => Find(emu, p),
					Accepts = (ItemData it) => emu.Valids.Any((Func<ItemData, bool> f) => f(it))
				};
				Emus[text] = value;
			}
			if (func != null)
			{
				value.Gets.Add(func);
			}
			if (valid != null)
			{
				value.Valids.Add(valid);
				if (func == null)
				{
					value.Gets.Add(delegate(Player p)
					{
						if (p == null)
						{
							return (ItemData)null;
						}
						Inventory inventory = ((Humanoid)p).GetInventory();
						return (inventory == null) ? null : inventory.GetEquippedItems()?.FirstOrDefault(valid);
					});
				}
			}
			CustomSlots.Add("AzuEPI.emulated", value.Def);
		}

		private static string PluginName(string asm)
		{
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				if (Object.op_Implicit((Object)(object)value.Instance) && ((object)value.Instance).GetType().Assembly.GetName().Name == asm)
				{
					return value.Metadata.Name;
				}
			}
			return asm;
		}

		private static ItemData Find(Emu e, Player p)
		{
			for (int i = 0; i < e.Gets.Count; i++)
			{
				ItemData val = e.Gets[i](p);
				if (val != null)
				{
					return val;
				}
			}
			return null;
		}
	}
	internal static class BuildInfo
	{
		public const string Channel = "release";

		public const string Version = "1.0.1";

		public const int Number = 0;

		public const string Time = "2026-10-18 12:14 UTC";

		public const string Id = "0eead253";
	}
	internal sealed class Slot<T> where T : class
	{
		private sealed class Entry
		{
			public string Owner;

			public T Provider;

			public int Priority;

			public int Serial;
		}

		private readonly List<Entry> _items = new List<Entry>(2);

		private int _serial;

		public T Current { get; private set; }

		public string Owner { get; private set; }

		public int Count => _items.Count;

		public event Action<T> Changed;

		public void Set(string owner, T provider, int priority)
		{
			if (string.IsNullOrEmpty(owner))
			{
				throw new ArgumentException("owner is required", "owner");
			}
			if (provider == null)
			{
				throw new ArgumentNullException("provider");
			}
			Drop(owner);
			_items.Add(new Entry
			{
				Owner = owner,
				Provider = provider,
				Priority = priority,
				Serial = ++_serial
			});
			Pick();
		}

		public T Get(string owner)
		{
			for (int i = 0; i < _items.Count; i++)
			{
				if (_items[i].Owner == owner)
				{
					return _items[i].Provider;
				}
			}
			return null;
		}

		public bool Remove(string owner)
		{
			if (!Drop(owner))
			{
				return false;
			}
			Pick();
			return true;
		}

		private bool Drop(string owner)
		{
			for (int i = 0; i < _items.Count; i++)
			{
				if (!(_items[i].Owner != owner))
				{
					_items.RemoveAt(i);
					return true;
				}
			}
			return false;
		}

		private void Pick()
		{
			Entry entry = null;
			for (int i = 0; i < _items.Count; i++)
			{
				Entry entry2 = _items[i];
				if (entry == null || entry2.Priority > entry.Priority || (entry2.Priority == entry.Priority && entry2.Serial > entry.Serial))
				{
					entry = entry2;
				}
			}
			T val = ((entry != null) ? entry.Provider : null);
			if (val != Current)
			{
				Current = val;
				Owner = entry?.Owner;
				this.Changed?.Invoke(val);
			}
		}
	}
	public enum DialogKind
	{
		Confirm,
		Message,
		Input,
		Choice
	}
	public abstract class Dialog
	{
		internal int Depth;

		internal string Owner;

		internal int Frame;

		internal Action Fallback;

		public abstract DialogKind Kind { get; }

		public string Title { get; set; }

		public string Text { get; set; }

		public bool Centered { get; set; }

		public bool IsOpen { get; internal set; }

		public bool Vanilla { get; internal set; }

		public void Close()
		{
			Dialogs.Close(this);
		}

		internal void Answer(Action callback)
		{
			if (IsOpen)
			{
				if (!Vanilla)
				{
					Dialogs.Close(this);
				}
				try
				{
					callback?.Invoke();
				}
				catch (Exception e)
				{
					Plugin.Fail("dialog callback", e);
				}
				Dialogs.Finish(this);
			}
		}
	}
	public sealed class ConfirmDialog : Dialog
	{
		private readonly Action _yes;

		private readonly Action _no;

		public override DialogKind Kind => DialogKind.Confirm;

		public string YesText { get; set; }

		public string NoText { get; set; }

		public ConfirmDialog(Action yes, Action no)
		{
			_yes = yes;
			_no = no;
		}

		public void Yes()
		{
			Answer(_yes);
		}

		public void No()
		{
			Answer(_no);
		}
	}
	public sealed class MessageDialog : Dialog
	{
		private readonly Action _ok;

		public override DialogKind Kind => DialogKind.Message;

		public string OkText { get; set; }

		public MessageDialog(Action ok)
		{
			_ok = ok;
		}

		public void Ok()
		{
			Answer(_ok);
		}
	}
	public sealed class InputDialog : Dialog
	{
		private readonly Action<string> _submit;

		private readonly Action _cancel;

		private readonly Func<string, bool> _validate;

		public override DialogKind Kind => DialogKind.Input;

		public string Value { get; set; }

		public string Placeholder { get; set; }

		public int CharLimit { get; set; }

		public InputDialog(Action<string> submit, Action cancel, Func<string, bool> validate)
		{
			_submit = submit;
			_cancel = cancel;
			_validate = validate;
		}

		public bool IsValid(string value)
		{
			if (_validate != null)
			{
				return _validate(value);
			}
			return true;
		}

		public void Submit(string value)
		{
			if (base.IsOpen && IsValid(value))
			{
				Answer(delegate
				{
					_submit?.Invoke(value);
				});
			}
		}

		public void Cancel()
		{
			Answer(_cancel);
		}
	}
	public sealed class DialogButton
	{
		public string Text { get; set; }

		public bool Primary { get; set; }

		public Action Click { get; set; }

		public DialogButton(string text, Action click, bool primary = false)
		{
			Text = text;
			Click = click;
			Primary = primary;
		}
	}
	public sealed class ChoiceDialog : Dialog
	{
		private readonly DialogButton[] _buttons;

		public override DialogKind Kind => DialogKind.Choice;

		public IReadOnlyList<DialogButton> Buttons => _buttons;

		public int CancelIndex { get; set; }

		public ChoiceDialog(DialogButton[] buttons)
		{
			_buttons = buttons ?? new DialogButton[0];
			CancelIndex = _buttons.Length - 1;
		}

		public void Choose(int index)
		{
			if (index >= 0 && index < _buttons.Length)
			{
				Answer(_buttons[index].Click);
			}
		}

		public void Cancel()
		{
			if (CancelIndex >= 0)
			{
				Choose(CancelIndex);
			}
			else
			{
				Close();
			}
		}
	}
	public abstract class DialogProvider
	{
		public abstract bool Show(Dialog dialog);

		public virtual void Hide(Dialog dialog)
		{
		}
	}
	[HarmonyPatch(typeof(UnifiedPopup), "Push")]
	internal static class PushPopupPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static bool Pre(PopupBase popup, bool __runOriginal)
		{
			if (__runOriginal)
			{
				return !Dialogs.InterceptPush(popup);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(UnifiedPopup), "Pop")]
	internal static class PopPopupPatch
	{
		[HarmonyPrefix]
		private static bool Pre()
		{
			return !Dialogs.InterceptPop();
		}
	}
	[HarmonyPatch(typeof(UnifiedPopup), "IsVisible")]
	internal static class PopupVisiblePatch
	{
		[HarmonyPostfix]
		private static void Post(ref bool __result)
		{
			if (!__result)
			{
				__result = Dialogs.IsOpen;
			}
		}
	}
	[HarmonyPatch(typeof(UnifiedPopup), "WasVisibleThisFrame")]
	internal static class PopupWasVisiblePatch
	{
		[HarmonyPostfix]
		private static void Post(ref bool __result)
		{
			if (!__result)
			{
				__result = Dialogs.ClosedThisFrame;
			}
		}
	}
	[HarmonyPatch(typeof(TextInput), "RequestText")]
	internal static class RequestTextPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static bool Pre(TextReceiver sign, string topic, int charLimit, bool __runOriginal)
		{
			if (__runOriginal)
			{
				return !Dialogs.InterceptText(sign, topic, charLimit);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(TextInput), "IsVisible")]
	internal static class TextVisiblePatch
	{
		[HarmonyPostfix]
		private static void Post(ref bool __result)
		{
			if (!__result)
			{
				__result = Dialogs.InputOpen;
			}
		}
	}
	public static class Dialogs
	{
		private static readonly Slot<DialogProvider> Providers = new Slot<DialogProvider>();

		private static readonly List<Dialog> Opened = new List<Dialog>(2);

		private static readonly FieldRef<UnifiedPopup, Stack<PopupBase>> Stack = AccessTools.FieldRefAccess<UnifiedPopup, Stack<PopupBase>>("popupStack");

		private static readonly FieldInfo Instance = AccessTools.Field(typeof(UnifiedPopup), "instance");

		private static bool _passing;

		private static int _closedFrame = -1;

		public static bool HasProvider => Providers.Current != null;

		public static bool BridgeEnabled
		{
			get
			{
				if (Plugin.BridgeDialogs != null)
				{
					return Plugin.BridgeDialogs.Value;
				}
				return false;
			}
		}

		public static bool IsOpen => Opened.Count > 0;

		public static Dialog Top
		{
			get
			{
				if (Opened.Count <= 0)
				{
					return null;
				}
				return Opened[Opened.Count - 1];
			}
		}

		public static bool InputOpen => Opened.Exists(IsInput);

		public static bool ClosedThisFrame => _closedFrame == Time.frameCount;

		public static void Register(string owner, DialogProvider provider, int priority = 0)
		{
			List<Dialog> orphans = Release(owner);
			Providers.Set(owner, provider, priority);
			Replace(orphans);
		}

		public static bool Unregister(string owner)
		{
			List<Dialog> orphans = Release(owner);
			bool result = Providers.Remove(owner);
			Replace(orphans);
			return result;
		}

		public static ConfirmDialog Confirm(string title, string text, Action yes, Action no = null, string yesText = null, string noText = null)
		{
			PopupButtonCallback val = default(PopupButtonCallback);
			PopupButtonCallback val4 = default(PopupButtonCallback);
			ConfirmDialog obj = new ConfirmDialog(yes, no)
			{
				Title = Localize(title),
				Text = Localize(text),
				YesText = yesText,
				NoText = noText,
				Fallback = delegate
				{
					//IL_001e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0023: Unknown result type (might be due to invalid IL or missing references)
					//IL_0025: Expected O, but got Unknown
					//IL_002a: Expected O, but got Unknown
					//IL_004c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0056: Expected O, but got Unknown
					//IL_003d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0042: Unknown result type (might be due to invalid IL or missing references)
					//IL_0044: Expected O, but got Unknown
					//IL_0049: Expected O, but got Unknown
					string text2 = title;
					string text3 = text;
					PopupButtonCallback obj2 = val;
					if (obj2 == null)
					{
						PopupButtonCallback val2 = delegate
						{
							UnifiedPopup.Pop();
							yes?.Invoke();
						};
						PopupButtonCallback val3 = val2;
						val = val2;
						obj2 = val3;
					}
					PopupButtonCallback obj3 = val4;
					if (obj3 == null)
					{
						PopupButtonCallback val5 = delegate
						{
							UnifiedPopup.Pop();
							no?.Invoke();
						};
						PopupButtonCallback val3 = val5;
						val4 = val5;
						obj3 = val3;
					}
					UnifiedPopup.Push((PopupBase)new YesNoPopup(text2, text3, obj2, obj3, true, true));
				}
			};
			Open(obj);
			return obj;
		}

		public static ChoiceDialog Choose(string title, string text, params DialogButton[] buttons)
		{
			if (buttons == null || buttons.Length == 0)
			{
				throw new ArgumentException("at least one button is required", "buttons");
			}
			DialogButton[] array = buttons;
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == null)
				{
					throw new ArgumentNullException("buttons");
				}
			}
			PopupButtonCallback val = default(PopupButtonCallback);
			PopupButtonCallback val4 = default(PopupButtonCallback);
			PopupButtonCallback val6 = default(PopupButtonCallback);
			ChoiceDialog obj = new ChoiceDialog(buttons)
			{
				Title = Localize(title),
				Text = Localize(text),
				Fallback = delegate
				{
					//IL_0060: Unknown result type (might be due to invalid IL or missing references)
					//IL_0065: Unknown result type (might be due to invalid IL or missing references)
					//IL_0067: Expected O, but got Unknown
					//IL_006c: Expected O, but got Unknown
					//IL_0037: Unknown result type (might be due to invalid IL or missing references)
					//IL_0041: Expected O, but got Unknown
					//IL_0029: Unknown result type (might be due to invalid IL or missing references)
					//IL_002e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0030: Expected O, but got Unknown
					//IL_0035: Expected O, but got Unknown
					//IL_008e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0098: Expected O, but got Unknown
					//IL_007f: Unknown result type (might be due to invalid IL or missing references)
					//IL_0084: Unknown result type (might be due to invalid IL or missing references)
					//IL_0086: Expected O, but got Unknown
					//IL_008b: Expected O, but got Unknown
					if (buttons.Length == 1)
					{
						string text2 = title;
						string text3 = text;
						PopupButtonCallback obj2 = val;
						if (obj2 == null)
						{
							PopupButtonCallback val2 = delegate
							{
								UnifiedPopup.Pop();
								buttons[0].Click?.Invoke();
							};
							PopupButtonCallback val3 = val2;
							val = val2;
							obj2 = val3;
						}
						UnifiedPopup.Push((PopupBase)new WarningPopup(text2, text3, obj2, true));
					}
					else
					{
						string text4 = title;
						string text5 = text;
						PopupButtonCallback obj3 = val4;
						if (obj3 == null)
						{
							PopupButtonCallback val5 = delegate
							{
								UnifiedPopup.Pop();
								buttons[0].Click?.Invoke();
							};
							PopupButtonCallback val3 = val5;
							val4 = val5;
							obj3 = val3;
						}
						PopupButtonCallback obj4 = val6;
						if (obj4 == null)
						{
							PopupButtonCallback val7 = delegate
							{
								UnifiedPopup.Pop();
								buttons[1].Click?.Invoke();
							};
							PopupButtonCallback val3 = val7;
							val6 = val7;
							obj4 = val3;
						}
						UnifiedPopup.Push((PopupBase)new YesNoPopup(text4, text5, obj3, obj4, true, true));
					}
				}
			};
			Open(obj);
			return obj;
		}

		public static MessageDialog Message(string title, string text, Action ok = null, string okText = null)
		{
			PopupButtonCallback val = default(PopupButtonCallback);
			MessageDialog obj = new MessageDialog(ok)
			{
				Title = Localize(title),
				Text = Localize(text),
				OkText = okText,
				Fallback = delegate
				{
					//IL_002c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0036: Expected O, but got Unknown
					//IL_001e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0023: Unknown result type (might be due to invalid IL or missing references)
					//IL_0025: Expected O, but got Unknown
					//IL_002a: Expected O, but got Unknown
					string text2 = title;
					string text3 = text;
					PopupButtonCallback obj2 = val;
					if (obj2 == null)
					{
						PopupButtonCallback val2 = delegate
						{
							UnifiedPopup.Pop();
							ok?.Invoke();
						};
						PopupButtonCallback val3 = val2;
						val = val2;
						obj2 = val3;
					}
					UnifiedPopup.Push((PopupBase)new WarningPopup(text2, text3, obj2, true));
				}
			};
			Open(obj);
			return obj;
		}

		public static InputDialog Input(string title, string value, Action<string> submit, Action cancel = null, int charLimit = 0, Func<string, bool> validate = null, string placeholder = null)
		{
			PopupButtonCallback val = default(PopupButtonCallback);
			ValidateTextEntry val4 = default(ValidateTextEntry);
			RetrieveTextEntryResult val7 = default(RetrieveTextEntryResult);
			InputDialog obj = new InputDialog(submit, cancel, validate)
			{
				Title = Localize(title),
				Value = value,
				CharLimit = charLimit,
				Placeholder = placeholder,
				Fallback = delegate
				{
					//IL_0024: Unknown result type (might be due to invalid IL or missing references)
					//IL_0029: Unknown result type (might be due to invalid IL or missing references)
					//IL_002b: Expected O, but got Unknown
					//IL_0030: Expected O, but got Unknown
					//IL_0043: Unknown result type (might be due to invalid IL or missing references)
					//IL_0048: Unknown result type (might be due to invalid IL or missing references)
					//IL_004a: Expected O, but got Unknown
					//IL_004f: Expected O, but got Unknown
					//IL_0070: Unknown result type (might be due to invalid IL or missing references)
					//IL_007a: Expected O, but got Unknown
					//IL_0062: 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_0069: Expected O, but got Unknown
					//IL_006e: Expected O, but got Unknown
					string text = title;
					string text2 = value;
					string text3 = placeholder;
					PopupButtonCallback obj2 = val;
					if (obj2 == null)
					{
						PopupButtonCallback val2 = delegate
						{
							UnifiedPopup.Pop();
							cancel?.Invoke();
						};
						PopupButtonCallback val3 = val2;
						val = val2;
						obj2 = val3;
					}
					ValidateTextEntry obj3 = val4;
					if (obj3 == null)
					{
						ValidateTextEntry val5 = (string s) => validate == null || validate(s);
						ValidateTextEntry val6 = val5;
						val4 = val5;
						obj3 = val6;
					}
					RetrieveTextEntryResult obj4 = val7;
					if (obj4 == null)
					{
						RetrieveTextEntryResult val8 = delegate(string s)
						{
							UnifiedPopup.Pop();
							submit?.Invoke(s);
						};
						RetrieveTextEntryResult val9 = val8;
						val7 = val8;
						obj4 = val9;
					}
					UnifiedPopup.Push((PopupBase)new TextEntryPopup(text, text2, text3, obj2, obj3, obj4, true));
				}
			};
			Open(obj);
			return obj;
		}

		public static void Close(Dialog dialog)
		{
			if (dialog != null && dialog.IsOpen)
			{
				dialog.IsOpen = false;
				Opened.Remove(dialog);
				_closedFrame = Time.frameCount;
				Hide(Providers.Current, dialog);
			}
		}

		private static void Hide(DialogProvider p, Dialog dialog)
		{
			if (p == null)
			{
				return;
			}
			try
			{
				p.Hide(dialog);
			}
			catch (Exception e)
			{
				Plugin.Fail("DialogProvider " + dialog.Owner, e);
			}
		}

		internal static void DropStale()
		{
			for (int num = Opened.Count - 1; num >= 0; num--)
			{
				if (Opened[num].Frame < Time.frameCount)
				{
					Dialog dialog = Opened[num];
					Close(dialog);
					Plugin.Trace("dialog dropped on a scene change: " + dialog.Title);
				}
			}
		}

		internal static void Finish(Dialog d)
		{
			if (d.IsOpen && Opened.Count > 0 && Opened[Opened.Count - 1] == d)
			{
				Close(d);
			}
		}

		internal static bool InterceptPush(PopupBase popup)
		{
			if (_passing || Providers.Current == null || !Plugin.BridgeDialogs.Value)
			{
				return false;
			}
			Dialog dialog = FromPopup(popup);
			if (dialog == null)
			{
				return false;
			}
			dialog.Vanilla = true;
			dialog.Fallback = delegate
			{
				UnifiedPopup.Push(popup);
			};
			return Present(dialog);
		}

		internal static bool InterceptText(TextReceiver sign, string topic, int charLimit)
		{
			if (_passing || sign == null || Providers.Current == null || !Plugin.BridgeDialogs.Value)
			{
				return false;
			}
			return Present(new InputDialog((Action<string>)sign.SetText, (Action)null, (Func<string, bool>)null)
			{
				Title = Localize(topic),
				Value = sign.GetText(),
				CharLimit = charLimit,
				Vanilla = true,
				Fallback = delegate
				{
					if ((Object)(object)TextInput.instance != (Object)null)
					{
						TextInput.instance.RequestText(sign, topic, charLimit);
					}
				}
			});
		}

		internal static bool InterceptPop()
		{
			int count = Opened.Count;
			if (count == 0)
			{
				return false;
			}
			Dialog dialog = Opened[count - 1];
			if (dialog.Depth != Depth())
			{
				return false;
			}
			Close(dialog);
			return true;
		}

		private static Dialog FromPopup(PopupBase popup)
		{
			YesNoPopup val = (YesNoPopup)(object)((popup is YesNoPopup) ? popup : null);
			if (val != null)
			{
				return new ConfirmDialog((val.yesCallback == null) ? null : new Action(val.yesCallback.Invoke), (val.noCallback == null) ? null : new Action(val.noCallback.Invoke))
				{
					Title = ((FixedPopupBase)val).header,
					Text = ((FixedPopupBase)val).text
				};
			}
			WarningPopup val2 = (WarningPopup)(object)((popup is WarningPopup) ? popup : null);
			if (val2 != null)
			{
				PopupButtonCallback okCallback = val2.okCallback;
				return new MessageDialog((okCallback == null) ? null : new Action(okCallback.Invoke))
				{
					Title = ((FixedPopupBase)val2).header,
					Text = ((FixedPopupBase)val2).text
				};
			}
			TextEntryPopup val3 = (TextEntryPopup)(object)((popup is TextEntryPopup) ? popup : null);
			if (val3 != null)
			{
				PopupButtonCallback cancelCallback = val3.cancelCallback;
				RetrieveTextEntryResult sendResultCallback = val3.sendResultCallback;
				ValidateTextEntry validationCallback = val3.validationCallback;
				return new InputDialog((sendResultCallback == null) ? null : new Action<string>(sendResultCallback.Invoke), (cancelCallback == null) ? null : new Action(cancelCallback.Invoke), (validationCallback == null) ? null : new Func<string, bool>(validationCallback.Invoke))
				{
					Title = ((FixedPopupBase)val3).header,
					Text = ((FixedPopupBase)val3).text,
					Placeholder = val3.placeholderString
				};
			}
			return null;
		}

		private static bool Present(Dialog d)
		{
			DialogProvider current = Providers.Current;
			if (current == null)
			{
				return false;
			}
			d.IsOpen = true;
			d.Depth = Depth();
			d.Owner = Providers.Owner;
			d.Frame = Time.frameCount;
			Opened.Add(d);
			bool flag;
			try
			{
				flag = current.Show(d);
			}
			catch (Exception e)
			{
				Plugin.Fail("DialogProvider " + Providers.Owner, e);
				flag = false;
			}
			if (!flag && d.IsOpen)
			{
				d.IsOpen = false;
				Opened.Remove(d);
			}
			Plugin.Trace("dialog " + d.Kind.ToString() + (d.Vanilla ? " (game)" : "") + (flag ? "" : " (declined)") + ": " + d.Title);
			return flag;
		}

		private static void Open(Dialog d)
		{
			if (!Present(d))
			{
				Pass(d.Fallback);
			}
		}

		private static List<Dialog> Release(string owner)
		{
			List<Dialog> list = null;
			for (int num = Opened.Count - 1; num >= 0; num--)
			{
				Dialog dialog = Opened[num];
				if (!(dialog.Owner != owner))
				{
					dialog.IsOpen = false;
					Opened.RemoveAt(num);
					Hide(Providers.Get(owner), dialog);
					(list ?? (list = new List<Dialog>(2))).Insert(0, dialog);
				}
			}
			return list;
		}

		private static void Replace(List<Dialog> orphans)
		{
			if (orphans == null)
			{
				return;
			}
			foreach (Dialog orphan in orphans)
			{
				if (!Present(orphan))
				{
					Pass(orphan.Fallback);
				}
			}
		}

		private static void Pass(Action push)
		{
			if (push == null)
			{
				return;
			}
			_passing = true;
			try
			{
				push();
			}
			finally
			{
				_passing = false;
			}
		}

		private static bool IsInput(Dialog d)
		{
			return d.Kind == DialogKind.Input;
		}

		private static string Localize(string s)
		{
			Localization instance = Localization.instance;
			if (instance != null && !string.IsNullOrEmpty(s))
			{
				return instance.Localize(s);
			}
			return s;
		}

		private static int Depth()
		{
			object? value = Instance.GetValue(null);
			UnifiedPopup val = (UnifiedPopup)((value is UnifiedPopup) ? value : null);
			if (!((Object)(object)val == (Object)null))
			{
				return Stack.Invoke(val).Count;
			}
			return 0;
		}
	}
	public sealed class HudElement
	{
		public bool Busy { get; set; }

		public bool Placed { get; set; }

		public string Owner { get; }

		public string Id { get; }

		public string Key => Owner + "/" + Id;

		public string Title { get; set; }

		public RectTransform Rect { get; }

		public bool Alive => (Object)(object)Rect != (Object)null;

		internal HudElement(string owner, string id, string title, RectTransform rect)
		{
			Owner = owner;
			Id = id;
			Title = title;
			Rect = rect;
		}
	}
	public abstract class HudProvider
	{
		public abstract void Added(HudElement element);

		public virtual void Removed(HudElement element)
		{
		}
	}
	public static class HudElements
	{
		private static readonly Slot<HudProvider> Providers = new Slot<HudProvider>();

		private static readonly List<HudElement> Items = new List<HudElement>(8);

		private static HudProvider _fed;

		public static bool HasProvider => Providers.Current != null;

		public static void Register(string owner, HudProvider provider, int priority = 0)
		{
			Providers.Set(owner, provider, priority);
			Feed();
		}

		public static bool Unregister(string owner)
		{
			if (!Providers.Remove(owner))
			{
				return false;
			}
			Feed();
			return true;
		}

		public static HudElement Add(string owner, string id, string title, RectTransform rect)
		{
			if (string.IsNullOrEmpty(owner))
			{
				throw new ArgumentException("owner is required", "owner");
			}
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("id is required", "id");
			}
			if ((Object)(object)rect == (Object)null)
			{
				throw new ArgumentNullException("rect");
			}
			Prune();
			for (int i = 0; i < Items.Count; i++)
			{
				if (Items[i].Owner == owner && Items[i].Id == id)
				{
					Drop(i);
				}
			}
			HudElement hudElement = new HudElement(owner, id, title, rect);
			Items.Add(hudElement);
			Notify(Providers.Current, hudElement);
			return hudElement;
		}

		public static bool Remove(HudElement element)
		{
			int num = Items.IndexOf(element);
			if (num < 0)
			{
				return false;
			}
			Drop(num);
			return true;
		}

		public static int RemoveAll(string owner)
		{
			int num = 0;
			for (int num2 = Items.Count - 1; num2 >= 0; num2--)
			{
				if (!(Items[num2].Owner != owner))
				{
					Drop(num2);
					num++;
				}
			}
			return num;
		}

		public static HudElement Find(string owner, string id)
		{
			Prune();
			for (int i = 0; i < Items.Count; i++)
			{
				if (Items[i].Owner == owner && Items[i].Id == id)
				{
					return Items[i];
				}
			}
			return null;
		}

		public static HudElement[] Snapshot()
		{
			Prune();
			return Items.ToArray();
		}

		private static void Drop(int i)
		{
			HudElement element = Items[i];
			Items.RemoveAt(i);
			HudProvider current = Providers.Current;
			if (current == null)
			{
				return;
			}
			try
			{
				current.Removed(element);
			}
			catch (Exception e)
			{
				Plugin.Fail("HudProvider " + Providers.Owner, e);
			}
		}

		private static void Prune()
		{
			for (int num = Items.Count - 1; num >= 0; num--)
			{
				if (!Items[num].Alive)
				{
					Drop(num);
				}
			}
		}

		private static void Feed()
		{
			HudProvider current = Providers.Current;
			if (current == _fed)
			{
				return;
			}
			_fed = current;
			if (current != null)
			{
				Prune();
				for (int i = 0; i < Items.Count; i++)
				{
					Notify(current, Items[i]);
				}
			}
		}

		private static void Notify(HudProvider p, HudElement e)
		{
			if (p == null)
			{
				return;
			}
			try
			{
				p.Added(e);
			}
			catch (Exception e2)
			{
				Plugin.Fail("HudProvider " + Providers.Owner, e2);
			}
		}
	}
	public enum NoticeKind
	{
		TopLeft,
		Center,
		Unlock,
		Biome
	}
	public sealed class Notice
	{
		public bool Log { get; set; } = true;

		public NoticeKind Kind { get; set; }

		public string Raw { get; set; }

		public string Text { get; set; }

		public string Title { get; set; }

		public int Amount { get; set; }

		public Sprite Icon { get; set; }

		public bool Stinger { get; set; }

		public bool ShowWhenHidden { get; set; }

		public bool Vanilla { get; internal set; }
	}
	public abstract class NoticeProvider
	{
		public abstract bool Show(Notice notice);
	}
	[HarmonyPatch(typeof(MessageHud), "ShowMessage")]
	internal static class ShowMessagePatch
	{
		[HarmonyPrefix]
		private static bool Pre(MessageType type, string text, int amount, Sprite icon, bool showDespiteHiddenHUD, bool log)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			if ((int)type == 1)
			{
				return !Notices.Intercept(NoticeKind.TopLeft, text, null, amount, icon, showDespiteHiddenHUD, log, stinger: false);
			}
			if ((int)type == 2)
			{
				return !Notices.Intercept(NoticeKind.Center, text, null, amount, icon, showDespiteHiddenHUD, log, stinger: false);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MessageHud), "QueueUnlockMsg")]
	internal static class QueueUnlockPatch
	{
		[HarmonyPrefix]
		private static bool Pre(Sprite icon, string topic, string description)
		{
			return !Notices.Intercept(NoticeKind.Unlock, description, topic, 0, icon, force: true, log: true, stinger: false);
		}
	}
	[HarmonyPatch(typeof(MessageHud), "ShowBiomeFoundMsg")]
	internal static class BiomeFoundPatch
	{
		[HarmonyPrefix]
		private static bool Pre(string text, bool playStinger)
		{
			return !Notices.Intercept(NoticeKind.Biome, text, null, 0, null, force: true, log: false, playStinger);
		}
	}
	public static class Notices
	{
		private static readonly Slot<NoticeProvider> Providers = new Slot<NoticeProvider>();

		private static bool _passing;

		private static readonly Action<MessageHud, string> Add = (Action<MessageHud, string>)Delegate.CreateDelegate(typeof(Action<MessageHud, string>), AccessTools.Method(typeof(MessageHud), "AddLog", (Type[])null, (Type[])null));

		public static bool HasProvider => Providers.Current != null;

		public static bool BridgeEnabled
		{
			get
			{
				if (Plugin.BridgeNotices != null)
				{
					return Plugin.BridgeNotices.Value;
				}
				return false;
			}
		}

		public static event Action<Notice> Shown;

		public static void Register(string owner, NoticeProvider provider, int priority = 0)
		{
			Providers.Set(owner, provider, priority);
		}

		public static bool Unregister(string owner)
		{
			return Providers.Remove(owner);
		}

		public static void Show(string text, NoticeKind kind = NoticeKind.TopLeft, int amount = 0, Sprite icon = null, bool log = true)
		{
			if (kind == NoticeKind.Unlock || kind == NoticeKind.Biome)
			{
				throw new ArgumentException("use Unlock or Biome", "kind");
			}
			Send(Make(kind, text, null, amount, icon, log, vanilla: false));
		}

		public static void Unlock(Sprite icon, string title, string text)
		{
			Notice notice = Make(NoticeKind.Unlock, text, title, 0, icon, log: true, vanilla: false);
			notice.ShowWhenHidden = true;
			Send(notice);
		}

		public static void Biome(string text, bool stinger = true)
		{
			Notice notice = Make(NoticeKind.Biome, text, null, 0, null, log: false, vanilla: false);
			notice.Stinger = stinger;
			notice.ShowWhenHidden = true;
			Send(notice);
		}

		public static void Send(Notice notice)
		{
			if (notice == null)
			{
				throw new ArgumentNullException("notice");
			}
			if (notice.Raw == null)
			{
				notice.Raw = notice.Text;
			}
			if (notice.Text == null)
			{
				notice.Text = Localize(notice.Raw);
			}
			if (!Dispatch(notice))
			{
				Pass(notice);
			}
		}

		internal static bool Intercept(NoticeKind kind, string raw, string title, int amount, Sprite icon, bool force, bool log, bool stinger)
		{
			if (_passing || (Providers.Current == null && Notices.Shown == null) || !Plugin.BridgeNotices.Value)
			{
				return false;
			}
			Notice notice = Make(kind, raw, title, amount, icon, log, vanilla: true);
			notice.ShowWhenHidden = force;
			notice.Stinger = stinger;
			return Dispatch(notice);
		}

		private static Notice Make(NoticeKind kind, string raw, string title, int amount, Sprite icon, bool log, bool vanilla)
		{
			return new Notice
			{
				Kind = kind,
				Raw = raw,
				Text = Localize(raw),
				Title = ((title == null) ? null : Localize(title)),
				Amount = amount,
				Icon = icon,
				Log = log,
				Vanilla = vanilla
			};
		}

		private static string Localize(string s)
		{
			Localization instance = Localization.instance;
			if (instance != null && !string.IsNullOrEmpty(s))
			{
				return instance.Localize(s);
			}
			return s;
		}

		private static bool Dispatch(Notice n)
		{
			if (Hud.IsUserHidden() && !n.ShowWhenHidden)
			{
				return true;
			}
			Plugin.Trace("notice " + n.Kind.ToString() + (n.Vanilla ? " (game)" : "") + ": " + n.Text);
			Action<Notice> shown = Notices.Shown;
			if (shown != null)
			{
				Delegate[] invocationList = shown.GetInvocationList();
				foreach (Delegate obj in invocationList)
				{
					try
					{
						((Action<Notice>)obj)(n);
					}
					catch (Exception e)
					{
						Plugin.Fail("Notices.Shown", e);
					}
				}
			}
			NoticeProvider current = Providers.Current;
			if (current == null)
			{
				return false;
			}
			bool flag;
			try
			{
				flag = current.Show(n);
			}
			catch (Exception e2)
			{
				Plugin.Fail("NoticeProvider " + Providers.Owner, e2);
				return false;
			}
			if (flag && n.Log)
			{
				Journal(n);
			}
			return flag;
		}

		private static void Journal(Notice n)
		{
			MessageHud instance = MessageHud.instance;
			if (!((Object)(object)instance == (Object)null) && n.Kind != NoticeKind.Biome)
			{
				Add(instance, (n.Kind == NoticeKind.Unlock) ? (n.Title + ": " + n.Text) : n.Text);
			}
		}

		private static void Pass(Notice n)
		{
			MessageHud instance = MessageHud.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			_passing = true;
			try
			{
				switch (n.Kind)
				{
				case NoticeKind.TopLeft:
					instance.ShowMessage((MessageType)1, n.Raw, n.Amount, n.Icon, n.ShowWhenHidden, n.Log);
					break;
				case NoticeKind.Center:
					instance.ShowMessage((MessageType)2, n.Raw, n.Amount, n.Icon, n.ShowWhenHidden, n.Log);
					break;
				case NoticeKind.Unlock:
					instance.QueueUnlockMsg(n.Icon, n.Title, n.Raw);
					break;
				case NoticeKind.Biome:
					instance.ShowBiomeFoundMsg(n.Raw, n.Stinger);
					break;
				}
			}
			finally
			{
				_passing = false;
			}
		}
	}
	[BepInPlugin("seneaL.valheim.api", "SeneaL API", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Version = "1.0.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> BridgeNotices;

		internal static ConfigEntry<bool> BridgeDialogs;

		internal static ConfigEntry<bool> BridgeAzu;

		internal static ConfigEntry<bool> BridgeStubs;

		internal static ConfigEntry<string> StubIgnore;

		internal static ConfigEntry<bool> Verbose;

		private float _azuAt;

		private float _pollAt;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BridgeNotices = ((BaseUnityPlugin)this).Config.Bind<bool>("Bridge", "Notices", true, "Route the game's own messages (top-left, center, unlock and biome banners) to the notice provider, if one is registered.");
			BridgeDialogs = ((BaseUnityPlugin)this).Config.Bind<bool>("Bridge", "Dialogs", true, "Route the game's own confirm, warning and text input windows to the dialog provider, if one is registered.");
			BridgeAzu = ((BaseUnityPlugin)this).Config.Bind<bool>("Bridge", "AzuEPI", true, "Show the equipment slots that other mods add through Azumatt's AzuEPI in the UI mod as slots of this API, if AzuEPI is installed. AzuEPI keeps running them; the UI only shows and moves the items.");
			BridgeStubs = ((BaseUnityPlugin)this).Config.Bind<bool>("Bridge", "AzuEPIStubs", true, "Route the AzuEPI API stubs that other mods carry inside their own dll (their methods do nothing) to this API, so such a mod's AddSlot reaches the UI mod (through AzuEPI if it is installed, otherwise emulated). Needs the patcher file in BepInEx/patchers and a game restart.");
			StubIgnore = ((BaseUnityPlugin)this).Config.Bind<string>("Bridge", "AzuEPIStubIgnore", "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot", "Assembly names (comma separated) of mods whose AzuEPI stub is left alone, because the UI mod already supports them itself. Remove a name to route that mod too. Needs a game restart.");
			AzuBridge.NameOf = PluginName;
			SlotOrigins.Fallback = delegate(string slot)
			{
				Assembly assembly = StubBridge.OriginOf(slot);
				return (!(assembly != null)) ? null : PluginName(assembly);
			};
			if (StubIgnore.Value == "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot,Backpacks,RustyBags,Hunter_Legacy,Magic_Supremacy,MagicPlugin,Wizardry" || StubIgnore.Value == "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot,Magic_Supremacy")
			{
				StubIgnore.Value = "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot";
			}
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Verbose", false, "Log every routed notice and dialog.");
			SceneManager.activeSceneChanged += OnScene;
			_harmony = new Harmony("seneaL.valheim.api");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)("SeneaL API 1.0.1 (contract " + 2 + ") loaded: " + string.Join(", ", Api.Features)));
		}

		private void Update()
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime >= _azuAt)
			{
				_azuAt = unscaledTime + 1f;
				TickAzu();
			}
			if (unscaledTime >= _pollAt)
			{
				_pollAt = unscaledTime + 0.25f;
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					CustomSlots.Poll(Player.m_localPlayer);
				}
			}
		}

		private static void TickAzu()
		{
			if (!Chainloader.PluginInfos.TryGetValue("Azumatt.AzuExtendedPlayerInventory", out var value) || !Object.op_Implicit((Object)(object)value.Instance))
			{
				if (AzuBridge.Bound)
				{
					AzuBridge.Clear();
				}
			}
			else if (!BridgeAzu.Value || CustomSlots.AzuManaged)
			{
				if (AzuBridge.Bound)
				{
					AzuBridge.Clear();
				}
			}
			else if (AzuBridge.Bind(((object)value.Instance).GetType().Assembly.GetType("AzuEPI.API")))
			{
				AzuBridge.Sync();
			}
		}

		private void Start()
		{
			StubHost.Start();
		}

		private void OnDestroy()
		{
			SceneManager.activeSceneChanged -= OnScene;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private static void OnScene(Scene from, Scene to)
		{
			Dialogs.DropStale();
		}

		internal static void Trace(string text)
		{
			if (Verbose != null && Verbose.Value)
			{
				Log.LogInfo((object)text);
			}
		}

		internal static void Warn(string text)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)text);
			}
		}

		internal static void Fail(string where, Exception e)
		{
			Log.LogWarning((object)(where + ": " + e.GetType().Name + ": " + e.Message));
		}

		private static string PluginName(Assembly asm)
		{
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				if (Object.op_Implicit((Object)(object)value.Instance) && ((object)value.Instance).GetType().Assembly == asm)
				{
					return value.Metadata.Name;
				}
			}
			return asm.GetName().Name;
		}
	}
	public sealed class ModSetting
	{
		public string Owner { get; }

		public string Mod { get; }

		public ConfigEntryBase Entry { get; }

		public Func<string> Label { get; set; }

		public Func<string> Tip { get; set; }

		public Func<string[]> Options { get; set; }

		public Func<bool> Visible { get; set; }

		public string Group { get; set; }

		public bool Hold { get; set; }

		public string Text
		{
			get
			{
				try
				{
					string text = Label?.Invoke();
					return string.IsNullOrEmpty(text) ? Entry.Definition.Key : text;
				}
				catch (Exception e)
				{
					Plugin.Fail("ModSetting label " + Owner, e);
					return Entry.Definition.Key;
				}
			}
		}

		public bool Shown
		{
			get
			{
				try
				{
					return Visible == null || Visible();
				}
				catch (Exception e)
				{
					Plugin.Fail("ModSetting visible " + Owner, e);
					return true;
				}
			}
		}

		internal ModSetting(string owner, string mod, ConfigEntryBase entry)
		{
			Owner = owner;
			Mod = mod;
			Entry = entry;
		}
	}
	public static class ModSettings
	{
		private static readonly List<ModSetting> Items = new List<ModSetting>(16);

		public static IReadOnlyList<ModSetting> All => Items;

		public static event Action Changed;

		public static ModSetting Add(string owner, string mod, ConfigEntryBase entry, Func<string> label = null, Func<string> tip = null)
		{
			if (string.IsNullOrEmpty(owner))
			{
				throw new ArgumentException("owner is required", "owner");
			}
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			for (int num = Items.Count - 1; num >= 0; num--)
			{
				if (Items[num].Owner == owner && Items[num].Entry == entry)
				{
					Items.RemoveAt(num);
				}
			}
			ModSetting modSetting = new ModSetting(owner, string.IsNullOrEmpty(mod) ? owner : mod, entry)
			{
				Label = label,
				Tip = tip
			};
			Items.Add(modSetting);
			Raise();
			return modSetting;
		}

		public static int RemoveAll(string owner)
		{
			int num = Items.RemoveAll((ModSetting s) => s.Owner == owner);
			if (num > 0)
			{
				Raise();
			}
			return num;
		}

		public static void Refresh()
		{
			Raise();
		}

		private static void Raise()
		{
			Action changed = ModSettings.Changed;
			if (changed == null)
			{
				return;
			}
			Delegate[] invocationList = changed.GetInvocationList();
			foreach (Delegate obj in invocationList)
			{
				try
				{
					((Action)obj)();
				}
				catch (Exception e)
				{
					Plugin.Fail("ModSettings.Changed", e);
				}
			}
		}
	}
	public static class CustomSlots
	{
		private sealed class Entry
		{
			public SlotDef Def;

			public int Serial;

			public ItemData Seen;
		}

		private static readonly Slot<SlotProvider> Providers = new Slot<SlotProvider>();

		private static readonly List<Entry> Items = new List<Entry>(8);

		private static SlotProvider _fed;

		internal static Func<Player> LocalPlayer = () => Player.m_localPlayer;

		private static int _serial;

		public static bool HasProvider => Providers.Current != null;

		public static bool AzuManaged { get; set; }

		public static event Action<SlotChange> Changed;

		public static void Register(string owner, SlotProvider provider, int priority = 0)
		{
			Providers.Set(owner, provider, priority);
			Feed();
		}

		public static bool Unregister(string owner)
		{
			if (!Providers.Remove(owner))
			{
				return false;
			}
			Feed();
			return true;
		}

		public static SlotDef Add(string owner, SlotDef slot)
		{
			if (string.IsNullOrEmpty(owner))
			{
				throw new ArgumentException("owner is required", "owner");
			}
			if (slot == null)
			{
				throw new ArgumentNullException("slot");
			}
			Remove(owner, slot.Id);
			if (slot.Mode == SlotMode.Linked && slot.Source == null)
			{
				throw new ArgumentException("a Linked slot needs a Source", "slot");
			}
			slot.Owner = owner;
			Items.Add(new Entry
			{
				Def = slot,
				Serial = ++_serial
			});
			Notify(Providers.Current, slot, added: true);
			return slot;
		}

		public static bool Remove(string owner, string id)
		{
			for (int i = 0; i < Items.Count; i++)
			{
				SlotDef def = Items[i].Def;
				if (!(def.Owner != owner) && !(def.Id != id))
				{
					Items.RemoveAt(i);
					Notify(Providers.Current, def, added: false);
					return true;
				}
			}
			return false;
		}

		public static int RemoveAll(string owner)
		{
			int num = 0;
			for (int num2 = Items.Count - 1; num2 >= 0; num2--)
			{
				if (!(Items[num2].Def.Owner != owner))
				{
					SlotDef def = Items[num2].Def;
					Items.RemoveAt(num2);
					Notify(Providers.Current, def, added: false);
					num++;
				}
			}
			return num;
		}

		public static string OriginOf(string slotName)
		{
			return SlotOrigins.Get(slotName);
		}

		public static SlotDef Find(string owner, string id)
		{
			for (int i = 0; i < Items.Count; i++)
			{
				SlotDef def = Items[i].Def;
				if (def.Owner == owner && def.Id == id)
				{
					return def;
				}
			}
			return null;
		}

		public static SlotDef[] Snapshot()
		{
			List<Entry> list = new List<Entry>(Items);
			list.Sort(Compare);
			SlotDef[] array = new SlotDef[list.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = list[i].Def;
			}
			return array;
		}

		public static ItemData Get(string owner, string id)
		{
			SlotDef slotDef = Find(owner, id);
			SlotProvider current = Providers.Current;
			if (slotDef == null)
			{
				return null;
			}
			if (slotDef.Mode == SlotMode.Linked)
			{
				return Read(slotDef, LocalPlayer());
			}
			if (current == null)
			{
				return null;
			}
			try
			{
				return current.Get(slotDef);
			}
			catch (Exception e)
			{
				Plugin.Fail("SlotProvider " + Providers.Owner, e);
				return null;
			}
		}

		public static bool Set(string owner, string id, ItemData item)
		{
			SlotDef slotDef = Find(owner, id);
			SlotProvider current = Providers.Current;
			if (slotDef == null)
			{
				return false;
			}
			if (slotDef.Mode == SlotMode.Linked)
			{
				return Wear(slotDef, item);
			}
			if (current == null)
			{
				return false;
			}
			try
			{
				return current.Set(slotDef, item);
			}
			catch (Exception e)
			{
				Plugin.Fail("SlotProvider " + Providers.Owner, e);
				return false;
			}
		}

		internal static ItemData Read(SlotDef d, Player player)
		{
			try
			{
				return d.Source(player);
			}
			catch (Exception e)
			{
				Plugin.Fail("slot source " + d.Key, e);
				return null;
			}
		}

		private static bool Wear(SlotDef d, ItemData item)
		{
			Player val = LocalPlayer();
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			ItemData val2 = Read(d, val);
			if (item == null)
			{
				if (val2 == null)
				{
					return true;
				}
				if (d.Take != null)
				{
					return Hand(d, d.Take, val, val2, "take");
				}
				((Humanoid)val).UnequipItem(val2, true);
				return true;
			}
			if (item == val2)
			{
				return true;
			}
			if (!d.Allows(item))
			{
				return false;
			}
			if (d.Put != null)
			{
				return Hand(d, d.Put, val, item, "put");
			}
			if (val2 != null)
			{
				((Humanoid)val).UnequipItem(val2, true);
			}
			return ((Humanoid)val).EquipItem(item, true);
		}

		private static bool Hand(SlotDef d, Func<Player, ItemData, bool> f, Player player, ItemData item, string what)
		{
			try
			{
				return f(player, item);
			}
			catch (Exception e)
			{
				Plugin.Fail("slot " + what + " " + d.Key, e);
				return false;
			}
		}

		internal static void Poll(Player player)
		{
			for (int i = 0; i < Items.Count; i++)
			{
				Entry entry = Items[i];
				if (entry.Def.Mode == SlotMode.Linked)
				{
					ItemData val = Read(entry.Def, player);
					if (val != entry.Seen)
					{
						ItemData seen = entry.Seen;
						entry.Seen = val;
						Report(new SlotChange(entry.Def, seen, val));
					}
				}
			}
		}

		public static void Report(SlotChange change)
		{
			if (change == null)
			{
				throw new ArgumentNullException("change");
			}
			Call(change.Slot.Changed, change);
			Action<SlotChange> changed = CustomSlots.Changed;
			if (changed != null)
			{
				Delegate[] invocationList = changed.GetInvocationList();
				for (int i = 0; i < invocationList.Length; i++)
				{
					Call((Action<SlotChange>)invocationList[i], change);
				}
			}
		}

		private static void Call(Action<SlotChange> a, SlotChange c)
		{
			if (a == null)
			{
				return;
			}
			try
			{
				a(c);
			}
			catch (Exception e)
			{
				Plugin.Fail("slot callback " + c.Slot.Key, e);
			}
		}

		private static int Compare(Entry a, Entry b)
		{
			int num = a.Def.Order.CompareTo(b.Def.Order);
			if (num == 0)
			{
				return a.Serial.CompareTo(b.Serial);
			}
			return num;
		}

		private static void Feed()
		{
			SlotProvider current = Providers.Current;
			if (current == _fed)
			{
				return;
			}
			_fed = current;
			if (current != null)
			{
				SlotDef[] array = Snapshot();
				foreach (SlotDef d in array)
				{
					Notify(current, d, added: true);
				}
			}
		}

		private static void Notify(SlotProvider p, SlotDef d, bool added)
		{
			if (p == null)
			{
				return;
			}
			try
			{
				if (added)
				{
					p.Added(d);
				}
				else
				{
					p.Removed(d);
				}
			}
			catch (Exception e)
			{
				Plugin.Fail("SlotProvider " + Providers.Owner, e);
			}
		}
	}
	public enum SlotMode
	{
		Stored,
		Worn,
		Linked
	}
	public enum SlotPlace
	{
		Auto,
		Row,
		Doll
	}
	public sealed class SlotDef
	{
		public bool EquipOnUse { get; set; } = true;

		public float EquipTime { get; set; } = -1f;

		public SlotPlace Place { get; set; }

		public string Id { get; }

		public string Owner { get; internal set; }

		public string Key => Owner + "/" + Id;

		public string Title { get; set; }

		public string Hint { get; set; }

		public string Origin { get; set; }

		public Sprite Icon { get; set; }

		public string IconName { get; set; }

		public SlotMode Mode { get; set; }

		public int Order { get; set; }

		public Func<Player, ItemData> Source { get; set; }

		public Func<ItemData, bool> Accepts { get; set; }

		public Func<bool> Visible { get; set; }

		public Func<Player, ItemData, bool> Put { get; set; }

		public Func<Player, ItemData, bool> Take { get; set; }

		public Action<SlotChange> Changed { get; set; }

		public SlotDef(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("id is required", "id");
			}
			Id = id;
		}

		public bool Allows(ItemData item)
		{
			if (item != null)
			{
				if (Accepts != null)
				{
					return Accepts(item);
				}
				return true;
			}
			return false;
		}
	}
	public sealed class SlotChange
	{
		public SlotDef Slot { get; }

		public ItemData Old { get; }

		public ItemData New { get; }

		public SlotChange(SlotDef slot, ItemData old, ItemData next)
		{
			Slot = slot;
			Old = old;
			New = next;
		}
	}
	public abstract class SlotProvider
	{
		public abstract void Added(SlotDef slot);

		public virtual void Removed(SlotDef slot)
		{
		}

		public abstract ItemData Get(SlotDef slot);

		public abstract bool Set(SlotDef slot, ItemData item);
	}
	public enum ThemePart
	{
		Window,
		Inner,
		Card,
		Button,
		ButtonHover,
		Primary,
		PrimaryHover,
		Field,
		FieldFocus,
		Row,
		RowHover,
		RowOn,
		Tab,
		TabHover,
		TabOn,
		Track,
		Chip,
		Separator
	}
	public enum ThemeFont
	{
		Body,
		Strong,
		Title
	}
	public enum ThemeColor
	{
		Text,
		Muted,
		Accent,
		Good,
		Bad,
		Warn,
		OnPrimary,
		Shade
	}
	public abstract class ThemeProvider
	{
		public virtual float PixelsPerUnit => 0f;

		public virtual Sprite Sprite(ThemePart part)
		{
			return null;
		}

		public virtual TMP_FontAsset Font(ThemeFont font)
		{
			return null;
		}

		public virtual Sprite Icon(string name)
		{
			return null;
		}

		public virtual bool TryColor(ThemeColor color, out Color value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			value = default(Color);
			return false;
		}
	}
	public static class Themes
	{
		private static readonly Slot<ThemeProvider> Providers = new Slot<ThemeProvider>();

		public static bool HasProvider => Providers.Current != null;

		public static string Owner => Providers.Owner;

		public static float PixelsPerUnit
		{
			get
			{
				ThemeProvider current = Providers.Current;
				if (current == null)
				{
					return 0f;
				}
				try
				{
					return current.PixelsPerUnit;
				}
				catch (Exception e)
				{
					Plugin.Fail("ThemeProvider " + Providers.Owner, e);
					return 0f;
				}
			}
		}

		public static event Action Changed;

		public static void Register(string owner, ThemeProvider provider, int priority = 0)
		{
			ThemeProvider current = Providers.Current;
			Providers.Set(owner, provider, priority);
			if (current != Providers.Current || provider == Providers.Current)
			{
				Refresh();
			}
		}

		public static bool Unregister(string owner)
		{
			ThemeProvider current = Providers.Current;
			if (!Providers.Remove(owner))
			{
				return false;
			}
			if (current != Providers.Current)
			{
				Refresh();
			}
			return true;
		}

		public static void Refresh()
		{
			Action changed = Themes.Changed;
			if (changed == null)
			{
				return;
			}
			Delegate[] invocationList = changed.GetInvocationList();
			foreach (Delegate obj in invocationList)
			{
				try
				{
					((Action)obj)();
				}
				catch (Exception e)
				{
					Plugin.Fail("Themes.Changed", e);
				}
			}
		}

		public static Sprite Sprite(ThemePart part)
		{
			ThemeProvider current = Providers.Current;
			if (current == null)
			{
				return null;
			}
			try
			{
				return current.Sprite(part);
			}
			catch (Exception e)
			{
				Plugin.Fail("ThemeProvider " + Providers.Owner, e);
				return null;
			}
		}

		public static Sprite Icon(string name)
		{
			ThemeProvider current = Providers.Current;
			if (current == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			try
			{
				return current.Icon(name);
			}
			catch (Exception e)
			{
				Plugin.Fail("ThemeProvider " + Providers.Owner, e);
				return null;
			}
		}

		public static TMP_FontAsset Font(ThemeFont font)
		{
			ThemeProvider current = Providers.Current;
			if (current == null)
			{
				return null;
			}
			try
			{
				return current.Font(font);
			}
			catch (Exception e)
			{
				Plugin.Fail("ThemeProvider " + Providers.Owner, e);
				return null;
			}
		}

		public static bool TryColor(ThemeColor color, out Color value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			value = default(Color);
			ThemeProvider current = Providers.Current;
			if (current == null)
			{
				return false;
			}
			try
			{
				return current.TryColor(color, out value);
			}
			catch (Exception e)
			{
				Plugin.Fail("ThemeProvider " + Providers.Owner, e);
				return false;
			}
		}
	}
}