Decompiled source of A Little to the Left Archipelago v1.0.0

ALTTLArchipelago.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ALTTLArchipelago.Core;
using ALTTLModKit;
using Archipelago.MultiClient.Net;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Exceptions;
using Archipelago.MultiClient.Net.Helpers;
using Archipelago.MultiClient.Net.MessageLog.Messages;
using Archipelago.MultiClient.Net.MessageLog.Parts;
using Archipelago.MultiClient.Net.Models;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Reflection;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Localization.Components;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ALTTLArchipelago")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+9071b21c6adcb522865716b090945e6ed707583f")]
[assembly: AssemblyProduct("ALTTLArchipelago")]
[assembly: AssemblyTitle("ALTTLArchipelago")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ALTTLArchipelago
{
	[HarmonyPatch]
	internal static class AbilityLocks
	{
		private static readonly Color Locked = new Color(0.55f, 0.55f, 0.55f, 0.6f);

		private static readonly Dictionary<int, Color> _original = new Dictionary<int, Color>();

		private static readonly HashSet<int> _tinted = new HashSet<int>();

		private static string _lastSummary = "";

		private static readonly AbilityState Released = new AbilityState(new SlotData
		{
			AbilityLocks = false
		});

		private static bool _releasing;

		private static bool _eventsAttached;

		private static readonly List<Action<GameEventData>> KeepAlive = new List<Action<GameEventData>>();

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

		private static float _holdUntil;

		private static bool _applying;

		private static bool _again;

		private static int _reentered;

		private static readonly Dictionary<int, Collider2D> _heldButtons = new Dictionary<int, Collider2D>();

		internal static readonly HashSet<int> HeldIndexes = new HashSet<int>();

		private static readonly HashSet<int> _heldArt = new HashSet<int>();

		private static int _indexLevelCopy;

		private const string HandleVote = "Handle";

		private static List<ClearingObject>? _clearers;

		private static int _clearersFor;

		private static List<Match>? _matches;

		private static int _matchesFor;

		private static string _handlesSaid = "";

		private static readonly Dictionary<string, (Type?, PropertyInfo[])> _extraLists = new Dictionary<string, (Type, PropertyInfo[])>();

		private static readonly Dictionary<int, bool> _colliders = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> _bodies = new Dictionary<int, bool>();

		private static readonly HashSet<int> _flagged = new HashSet<int>();

		private static readonly Dictionary<int, bool> _gameInteractable = new Dictionary<int, bool>();

		private static readonly HashSet<int> _scrubRefusalSaid = new HashSet<int>();

		private static readonly Dictionary<int, (bool Open, Action? Callback)> _drawerMoveRefused = new Dictionary<int, (bool, Action)>();

		private static readonly HashSet<int> _drawerRefusalSaid = new HashSet<int>();

		private static readonly Dictionary<int, Drawer> _savedWhileLocked = new Dictionary<int, Drawer>();

		private static readonly (string Name, bool WhenLocked)[] Gates = new(string, bool)[3]
		{
			("PreventSelection", true),
			("Interactable", false),
			("Selectable", false)
		};

		private static readonly List<(MemberInfo, bool)> NoGates = new List<(MemberInfo, bool)>();

		private static readonly PropertyInfo[] NoNested = new PropertyInfo[0];

		private static readonly Dictionary<string, (Type?, List<(MemberInfo, bool)>, PropertyInfo[])> _shadowed = new Dictionary<string, (Type, List<(MemberInfo, bool)>, PropertyInfo[])>();

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

		private static readonly Dictionary<int, string> _classes = new Dictionary<int, string>();

		private static readonly HashSet<int> _fadingIn = new HashSet<int>();

		internal static void Reset()
		{
			if (_flagged.Count > 0 || _colliders.Count > 0 || _bodies.Count > 0 || _tinted.Count > 0)
			{
				_releasing = true;
				try
				{
					Apply(Released);
				}
				finally
				{
					_releasing = false;
				}
			}
			_lastSummary = "";
			_original.Clear();
			_tinted.Clear();
			_fadingIn.Clear();
			_classes.Clear();
			_colliders.Clear();
			_bodies.Clear();
			_flagged.Clear();
			_savedWhileLocked.Clear();
			_gameInteractable.Clear();
			HeldIndexes.Clear();
			_heldArt.Clear();
			_heldButtons.Clear();
			_clearers = null;
			_matches = null;
			_handlesSaid = "";
			ApplyNow();
		}

		[HarmonyPatch(typeof(ObjectController), "RegisterObjectController")]
		[HarmonyPostfix]
		private static void AfterRegister()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			try
			{
				AbilityState abilities = Inventory.Abilities;
				if (abilities != null && abilities.LocksEnabled)
				{
					HoldDim();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(33, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("abilities: register hook failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
		}

		internal static void Tick(float dt)
		{
			if (!(Time.unscaledTime >= _holdUntil))
			{
				AbilityState abilities = Inventory.Abilities;
				if (abilities != null && abilities.LocksEnabled)
				{
					Apply(abilities);
				}
			}
		}

		internal static void AttachGameEvents()
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			if (_eventsAttached)
			{
				return;
			}
			try
			{
				Redim<GameEvent_ObjectDrawerChanged>("DrawerChanged", (Func<bool>?)null);
				Redim<GameEvent_ObjectControllerEnteredPhase>("EnteredPhase", (Func<bool>?)null);
				Redim<GameEvent_LevelReset>("LevelReset", (Func<bool>?)null);
				Redim<GameEvent_LevelRandomized>("LevelRandomized", (Func<bool>?)null);
				Redim<GameEvent_LevelTransitionInComplete>("TransitionInComplete", (Func<bool>?)null);
				Redim<GameEvent_InterludeEnd>("InterludeEnd", (Func<bool>?)LevelHasRag);
				OnLevelEnd<GameEvent_LevelComplete>();
				OnLevelEnd<GameEvent_LevelExited>();
				_eventsAttached = true;
				Plugin.Logger.LogInfo((object)"abilities: listening for rebuilds");
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("abilities: could not attach listeners: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
		}

		private static void Redim<T>(string label, Func<bool>? only = null) where T : GameEvent
		{
			Action<GameEventData> val = Action<GameEventData>.op_Implicit((Action<GameEventData>)delegate
			{
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0071: Expected O, but got Unknown
				try
				{
					_eventCounts[label] = ((!_eventCounts.TryGetValue(label, out var value)) ? 1 : (value + 1));
					AbilityState abilities = Inventory.Abilities;
					if (abilities != null && abilities.LocksEnabled && (only == null || only()))
					{
						HoldDim();
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logger = Plugin.Logger;
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(28, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("abilities: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(label);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" handler failed: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val2);
				}
			});
			KeepAlive.Add(val);
			GameEventManager.AddEventListener<T>(val);
		}

		private static void OnLevelEnd<T>() where T : GameEvent
		{
			Action<GameEventData> val = Action<GameEventData>.op_Implicit((Action<GameEventData>)delegate
			{
				try
				{
					GameManager instance = GameManager.Instance;
					object obj;
					if (instance == null)
					{
						obj = null;
					}
					else
					{
						LevelManager levelManager = instance.levelManager;
						if (levelManager == null)
						{
							obj = null;
						}
						else
						{
							LevelInterface activeLevelInterface = levelManager.ActiveLevelInterface;
							obj = ((activeLevelInterface != null) ? activeLevelInterface.LevelId : null);
						}
					}
					if (obj == null)
					{
						obj = "?";
					}
					ReportEvents((string)obj);
				}
				catch
				{
				}
			});
			KeepAlive.Add(val);
			GameEventManager.AddEventListener<T>(val);
		}

		private static void ReportEvents(string levelId)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			bool flag = default(bool);
			if (_reentered > 0)
			{
				ManualLogSource logger = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(53, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("abilities: a lock pass re-entered itself ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_reentered);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" time(s) on ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(levelId);
				}
				logger.LogWarning(val);
				_reentered = 0;
			}
			if (_eventCounts.Count == 0)
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, int> eventCount in _eventCounts)
			{
				list.Add($"{eventCount.Key}={eventCount.Value}");
			}
			ManualLogSource logger2 = Plugin.Logger;
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(30, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("abilities: re-dim events on ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(levelId);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(string.Join(", ", list));
			}
			logger2.LogInfo(val2);
			_eventCounts.Clear();
		}

		internal static void ApplyNow()
		{
			AbilityState abilities = Inventory.Abilities;
			if (abilities != null && abilities.LocksEnabled)
			{
				Apply(abilities);
			}
		}

		internal static void AbilitiesChanged()
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			int count = _flagged.Count;
			ApplyNow();
			try
			{
				GameManager instance = GameManager.Instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					LevelManager levelManager = instance.levelManager;
					if (levelManager == null)
					{
						obj = null;
					}
					else
					{
						LevelInterface activeLevelInterface = levelManager.ActiveLevelInterface;
						obj = ((activeLevelInterface != null) ? activeLevelInterface.LevelId : null);
					}
				}
				if (obj == null)
				{
					obj = "";
				}
				string text = (string)obj;
				if (ObjectLock.ResetOnUnlock(text, count, _flagged.Count))
				{
					Traps.ResetQuietly($"{text}: {count - _flagged.Count} piece(s) unlocked mid-level, " + "reloaded so the game sets its pieces up again");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(35, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("abilities: reset on unlock failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
		}

		internal static void HoldDim(float seconds = 0.5f)
		{
			_holdUntil = Time.unscaledTime + seconds;
			ApplyNow();
		}

		private static void Apply(AbilityState state)
		{
			if (_applying)
			{
				_again = true;
				_reentered++;
				return;
			}
			_applying = true;
			try
			{
				int num = 0;
				do
				{
					_again = false;
					ApplyOnce(state);
				}
				while (_again && ++num < 2);
			}
			finally
			{
				_applying = false;
			}
		}

		private static void ApplyOnce(AbilityState state)
		{
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Expected O, but got Unknown
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			bool flag2 = default(bool);
			try
			{
				GameManager instance = GameManager.Instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					LevelManager levelManager = instance.levelManager;
					obj = ((levelManager != null) ? levelManager.ActiveLevelInterface : null);
				}
				LevelInterface val = (LevelInterface)obj;
				Level val2 = ((val != null) ? val.Level : null);
				List<ObjectController> val3 = ((val2 != null) ? val2.objectControllers : null);
				string text = ((val != null) ? val.LevelId : null) ?? "";
				if (val3 == null || val3.Count == 0)
				{
					_lastSummary = "";
					return;
				}
				int num = 0;
				int num2 = 0;
				SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.Ordinal);
				Dictionary<int, ObjectLock> votes = new Dictionary<int, ObjectLock>();
				Dictionary<int, LevelObject> byId = new Dictionary<int, LevelObject>();
				for (int i = 0; i < val3.Count; i++)
				{
					ObjectController val4 = val3[i];
					if ((Object)(object)val4 == (Object)null)
					{
						continue;
					}
					GameObject gameObject = ((Component)val4).gameObject;
					string text2 = ObjectLock.LockClass(text, ((gameObject != null) ? ((Object)gameObject).name : null) ?? "", ClassOf(val4));
					bool flag = state.IsClassLocked(text2);
					if (flag)
					{
						num++;
						string text3 = state.AbilityFor(text2);
						if (!string.IsNullOrEmpty(text3))
						{
							sortedSet.Add(text3);
						}
					}
					else
					{
						num2++;
					}
					Collect(val4, text2, flag, text, votes, byId);
				}
				NoteHandles(val2, text, votes, byId);
				int value = ApplyWanted(votes, byId);
				HoldIndexes(val, val3, text, state);
				string text4 = $"{num} locked, {num2} open, {value} objects" + ((sortedSet.Count > 0) ? (", waiting on " + string.Join(", ", sortedSet)) : "");
				if (!(text4 == _lastSummary))
				{
					_lastSummary = text4;
					ManualLogSource logger = Plugin.Logger;
					BepInExInfoLogInterpolatedStringHandler val5 = new BepInExInfoLogInterpolatedStringHandler(11, 1, ref flag2);
					if (flag2)
					{
						((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("abilities: ");
						((BepInExLogInterpolatedStringHandler)val5).AppendFormatted<string>(text4);
					}
					logger.LogInfo(val5);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val6 = new BepInExWarningLogInterpolatedStringHandler(52, 1, ref flag2);
				if (flag2)
				{
					((BepInExLogInterpolatedStringHandler)val6).AppendLiteral("abilities: pass failed, leaving everything movable: ");
					((BepInExLogInterpolatedStringHandler)val6).AppendFormatted<string>(ex.Message);
				}
				logger2.LogWarning(val6);
			}
		}

		private static void HoldIndexes(LevelInterface? active, List<ObjectController> controllers, string levelId, AbilityState state)
		{
			int num = ((!((Object)(object)((active != null) ? active.Level : null) == (Object)null)) ? ((Object)active.Level).GetInstanceID() : 0);
			if (num != _indexLevelCopy)
			{
				_indexLevelCopy = num;
				HeldIndexes.Clear();
				_heldArt.Clear();
				_heldButtons.Clear();
			}
			if (!ObjectLock.IndexHoldLevels.Contains(levelId))
			{
				HeldIndexes.Clear();
				_heldArt.Clear();
				_heldButtons.Clear();
				return;
			}
			bool flag = (Object)(object)active != (Object)null && active.LevelIsLoaded && !active.IsTransitioning;
			ObjectLock.IndexHoldArt.TryGetValue(levelId, out var value);
			int count = HeldIndexes.Count;
			for (int i = 0; i < controllers.Count; i++)
			{
				ObjectController val = controllers[i];
				Indexables obj = (((Object)(object)val == (Object)null) ? null : ((Il2CppObjectBase)val).TryCast<Indexables>());
				List<IndexedAttribute> val2 = ((obj != null) ? obj.AllIndexes : null);
				if (val2 == null)
				{
					continue;
				}
				GameObject gameObject = ((Component)val).gameObject;
				string text = ObjectLock.LockClass(levelId, ((gameObject != null) ? ((Object)gameObject).name : null) ?? "", ClassOf(val));
				bool flag2 = ObjectLock.HoldsIndexes(levelId, state.IsClassLocked(text));
				for (int j = 0; j < val2.Count; j++)
				{
					IndexedAttribute val3 = val2[j];
					if (!((Object)(object)val3 == (Object)null))
					{
						if (flag2 && flag)
						{
							HeldIndexes.Add(((Object)val3).GetInstanceID());
						}
						else if (!flag2)
						{
							HeldIndexes.Remove(((Object)val3).GetInstanceID());
						}
						if (value != null && (flag || !flag2))
						{
							HoldArt(val3, value, flag2);
						}
					}
				}
			}
			HoldButtons(active);
			if (HeldIndexes.Count != count)
			{
				Plugin.Logger.LogInfo((object)((HeldIndexes.Count > 0) ? $"abilities: holding {HeldIndexes.Count} indexed state(s) on {levelId} where they are" : ("abilities: indexed states on " + levelId + " given back")));
			}
		}

		private static void HoldButtons(LevelInterface? active)
		{
			Level val = ((active != null) ? active.Level : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Il2CppArrayBase<IndexIncrementTrigger> componentsInChildren = ((Component)val).GetComponentsInChildren<IndexIncrementTrigger>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				IndexIncrementTrigger val2 = componentsInChildren[i];
				IndexedAttribute val3 = (((Object)(object)val2 == (Object)null) ? null : val2.IndexedAttribute);
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				bool flag = HeldIndexes.Contains(((Object)val3).GetInstanceID());
				Il2CppArrayBase<Collider2D> components = ((Component)val2).GetComponents<Collider2D>();
				for (int j = 0; j < components.Length; j++)
				{
					Collider2D val4 = components[j];
					if ((Object)(object)val4 == (Object)null)
					{
						continue;
					}
					int instanceID = ((Object)val4).GetInstanceID();
					if (flag)
					{
						if (((Behaviour)val4).enabled)
						{
							((Behaviour)val4).enabled = false;
							_heldButtons[instanceID] = val4;
						}
					}
					else if (_heldButtons.Remove(instanceID))
					{
						((Behaviour)val4).enabled = true;
					}
				}
			}
		}

		private static void HoldArt(IndexedAttribute attr, IReadOnlyList<string> names, bool hold)
		{
			Transform val = (((Object)(object)attr.attachedObject != (Object)null) ? ((Component)attr.attachedObject).transform : ((Component)attr).transform);
			foreach (string name in names)
			{
				Transform val2 = val.Find(name);
				SpriteRenderer val3 = (((Object)(object)val2 == (Object)null) ? null : ((Component)val2).GetComponent<SpriteRenderer>());
				if (!((Object)(object)val3 == (Object)null))
				{
					int instanceID = ((Object)val3).GetInstanceID();
					if (hold)
					{
						_heldArt.Add(instanceID);
					}
					else if (!_heldArt.Remove(instanceID))
					{
						continue;
					}
					Paint(val3, hold);
				}
			}
		}

		private static void Collect(ObjectController controller, string cls, bool isLocked, string levelId, Dictionary<int, ObjectLock> votes, Dictionary<int, LevelObject> byId)
		{
			Note(controller.ManagedObjects, cls, isLocked, levelId, votes, byId);
			var (concrete, array) = ExtraLists(controller);
			if (array.Length == 0)
			{
				return;
			}
			object obj = Recast((Il2CppObjectBase)(object)controller, concrete);
			if (obj == null)
			{
				return;
			}
			PropertyInfo[] array2 = array;
			foreach (PropertyInfo propertyInfo in array2)
			{
				try
				{
					Note(propertyInfo.GetValue(obj), cls, isLocked, levelId, votes, byId);
				}
				catch
				{
				}
			}
		}

		private static void Note(object? list, string cls, bool isLocked, string levelId, Dictionary<int, ObjectLock> votes, Dictionary<int, LevelObject> byId)
		{
			//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
			if (list == null)
			{
				return;
			}
			foreach (LevelObject item in Walk(list))
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				try
				{
					int instanceID = ((Object)item).GetInstanceID();
					byId[instanceID] = item;
					if (!votes.TryGetValue(instanceID, out ObjectLock value))
					{
						ObjectLock val = new ObjectLock(levelId);
						value = val;
						votes[instanceID] = val;
					}
					value.Add(cls, isLocked);
				}
				catch
				{
				}
			}
		}

		private static void NoteHandles(Level level, string levelId, Dictionary<int, ObjectLock> votes, Dictionary<int, LevelObject> byId)
		{
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Expected O, but got Unknown
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Expected O, but got Unknown
			//IL_01ef: Expected O, but got Unknown
			List<(LevelObject, List<int>)> list = new List<(LevelObject, List<int>)>();
			List<int> list2 = new List<int>();
			List<int> list3 = new List<int>();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			foreach (KeyValuePair<int, LevelObject> item3 in byId)
			{
				try
				{
					if ((Object)(object)((Il2CppObjectBase)item3.Value).TryCast<ClearableObject>() != (Object)null)
					{
						list2.Add(item3.Key);
						continue;
					}
					if ((Object)(object)((Il2CppObjectBase)item3.Value).TryCast<Candle>() != (Object)null)
					{
						list3.Add(item3.Key);
						continue;
					}
					StickerObject obj = ((Il2CppObjectBase)item3.Value).TryCast<StickerObject>();
					PluckObject val = ((obj != null) ? obj.pluckObj : null);
					if (!((Object)(object)val == (Object)null))
					{
						list.Add(((LevelObject)(object)val, new List<int> { item3.Key }));
						num++;
					}
				}
				catch
				{
				}
			}
			foreach (ClearingObject item4 in Clearers(level))
			{
				list.Add(((LevelObject)(object)item4, list2));
				num2++;
			}
			foreach (Match item5 in Matches(level))
			{
				list.Add(((LevelObject)(object)item5, list3));
				num3++;
			}
			int num4 = ((num2 > 0) ? list2.Count : 0);
			int num5 = ((num3 > 0) ? list3.Count : 0);
			foreach (var item6 in list)
			{
				LevelObject item = item6.Item1;
				List<int> item2 = item6.Item2;
				int instanceID = ((Object)item).GetInstanceID();
				foreach (int item7 in item2)
				{
					if (votes.TryGetValue(item7, out ObjectLock value))
					{
						if (!votes.TryGetValue(instanceID, out ObjectLock value2))
						{
							ObjectLock val2 = new ObjectLock(levelId);
							value2 = val2;
							votes[instanceID] = val2;
						}
						byId[instanceID] = item;
						value2.Add("Handle", !value.Unlocked);
					}
				}
			}
			string text = $"{levelId}|{num}|{num2}|{num4}|{num3}|{num5}";
			if (list.Count > 0 && text != _handlesSaid)
			{
				_handlesSaid = text;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(114, 5, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("abilities: handles vote with what they act on: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" peel ");
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("handle(s), ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(num2);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" rag(s) over ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(num4);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" piece(s), ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(num3);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" match(es) over ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(num5);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" candle(s)");
				}
				logger.LogInfo(val3);
			}
		}

		private static List<ClearingObject> Clearers(Level level)
		{
			int instanceID = ((Object)level).GetInstanceID();
			if (_clearers != null && _clearersFor == instanceID)
			{
				return _clearers;
			}
			_clearersFor = instanceID;
			_clearers = new List<ClearingObject>();
			try
			{
				foreach (ClearingObject componentsInChild in ((Component)level).GetComponentsInChildren<ClearingObject>(true))
				{
					if ((Object)(object)componentsInChild != (Object)null)
					{
						_clearers.Add(componentsInChild);
					}
				}
			}
			catch
			{
			}
			return _clearers;
		}

		private static List<Match> Matches(Level level)
		{
			int instanceID = ((Object)level).GetInstanceID();
			if (_matches != null && _matchesFor == instanceID)
			{
				return _matches;
			}
			_matchesFor = instanceID;
			_matches = new List<Match>();
			try
			{
				foreach (Match componentsInChild in ((Component)level).GetComponentsInChildren<Match>(true))
				{
					if ((Object)(object)componentsInChild != (Object)null)
					{
						_matches.Add(componentsInChild);
					}
				}
			}
			catch
			{
			}
			return _matches;
		}

		private static bool LevelHasRag()
		{
			try
			{
				GameManager instance = GameManager.Instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					LevelManager levelManager = instance.levelManager;
					if (levelManager == null)
					{
						obj = null;
					}
					else
					{
						LevelInterface activeLevelInterface = levelManager.ActiveLevelInterface;
						obj = ((activeLevelInterface != null) ? activeLevelInterface.Level : null);
					}
				}
				Level val = (Level)obj;
				return (Object)(object)val != (Object)null && _clearers != null && _clearers.Count > 0 && _clearersFor == ((Object)val).GetInstanceID();
			}
			catch
			{
				return false;
			}
		}

		private static IEnumerable<LevelObject> Walk(object list)
		{
			if (list is IEnumerable<LevelObject> enumerable)
			{
				foreach (LevelObject item in enumerable)
				{
					yield return item;
				}
				yield break;
			}
			Type type = list.GetType();
			PropertyInfo property = type.GetProperty("Count");
			PropertyInfo itemProp = type.GetProperty("Item");
			if (property == null || itemProp == null)
			{
				yield break;
			}
			int count;
			try
			{
				count = (int)(property.GetValue(list) ?? ((object)0));
			}
			catch
			{
				yield break;
			}
			for (int i = 0; i < count; i++)
			{
				LevelObject val = null;
				try
				{
					object? value = itemProp.GetValue(list, new object[1] { i });
					val = (LevelObject)((value is LevelObject) ? value : null);
				}
				catch
				{
				}
				if ((Object)(object)val != (Object)null)
				{
					yield return val;
				}
			}
		}

		private static (Type?, PropertyInfo[]) ExtraLists(ObjectController controller)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			string text = ClassOf(controller);
			if (_extraLists.TryGetValue(text, out (Type, PropertyInfo[]) value))
			{
				return value;
			}
			Type type = WrapperFor(text, typeof(ObjectController));
			List<PropertyInfo> list = new List<PropertyInfo>();
			if (type != null)
			{
				try
				{
					PropertyInfo[] properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (PropertyInfo propertyInfo in properties)
					{
						if (propertyInfo.GetIndexParameters().Length == 0 && HoldsLevelObjects(propertyInfo.PropertyType))
						{
							list.Add(propertyInfo);
						}
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logger = Plugin.Logger;
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(64, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("abilities: could not inspect ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", locking only its ");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("ManagedObjects: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val);
				}
			}
			if (list.Count > 0)
			{
				Plugin.Logger.LogInfo((object)("abilities: " + text + " keeps objects outside ManagedObjects: " + string.Join(", ", list.ConvertAll((PropertyInfo p) => p.Name))));
			}
			(Type, PropertyInfo[]) tuple = (type, list.ToArray());
			_extraLists[text] = tuple;
			return tuple;
		}

		private static Type? WrapperFor(string cls, Type mustDeriveFrom)
		{
			if (string.IsNullOrEmpty(cls))
			{
				return null;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type[] types;
				try
				{
					types = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					types = ex.Types;
				}
				catch
				{
					continue;
				}
				Type[] array = types;
				foreach (Type type in array)
				{
					if (type != null && type.Name == cls && mustDeriveFrom.IsAssignableFrom(type))
					{
						return type;
					}
				}
			}
			return null;
		}

		private static object? Recast(Il2CppObjectBase controller, Type? concrete)
		{
			if (concrete == null)
			{
				return null;
			}
			try
			{
				return (typeof(Il2CppObjectBase).GetMethod("TryCast")?.MakeGenericMethod(concrete))?.Invoke(controller, null);
			}
			catch
			{
				return null;
			}
		}

		private static bool HoldsLevelObjects(Type type)
		{
			if (typeof(IEnumerable<LevelObject>).IsAssignableFrom(type))
			{
				return true;
			}
			if (!type.IsGenericType)
			{
				return false;
			}
			Type[] genericArguments = type.GetGenericArguments();
			if (genericArguments.Length == 1)
			{
				return typeof(LevelObject).IsAssignableFrom(genericArguments[0]);
			}
			return false;
		}

		private static void Freeze(LevelObject obj, bool isLocked, bool solid = false, bool inDrawer = false)
		{
			Collider2D collider = obj.collider;
			Rigidbody2D rigidbody = obj.rigidbody;
			if (isLocked && !solid)
			{
				if ((Object)(object)rigidbody != (Object)null)
				{
					int instanceID = ((Object)rigidbody).GetInstanceID();
					if (!_bodies.ContainsKey(instanceID) || rigidbody.simulated)
					{
						_bodies[instanceID] = rigidbody.simulated;
					}
					rigidbody.simulated = false;
				}
				if ((Object)(object)collider != (Object)null)
				{
					int instanceID2 = ((Object)collider).GetInstanceID();
					if (!_colliders.ContainsKey(instanceID2) || ((Behaviour)collider).enabled)
					{
						_colliders[instanceID2] = ((Behaviour)collider).enabled;
					}
					((Behaviour)collider).enabled = false;
				}
				return;
			}
			if ((Object)(object)collider != (Object)null && _colliders.TryGetValue(((Object)collider).GetInstanceID(), out var value))
			{
				if (!((Behaviour)collider).enabled)
				{
					((Behaviour)collider).enabled = value;
				}
				_colliders.Remove(((Object)collider).GetInstanceID());
			}
			if ((Object)(object)rigidbody != (Object)null && _bodies.TryGetValue(((Object)rigidbody).GetInstanceID(), out var value2))
			{
				if (!rigidbody.simulated)
				{
					rigidbody.simulated = inDrawer || value2;
				}
				_bodies.Remove(((Object)rigidbody).GetInstanceID());
			}
		}

		internal static bool IsLocked(LevelObject? obj)
		{
			try
			{
				return (Object)(object)obj != (Object)null && _flagged.Contains(((Object)obj).GetInstanceID());
			}
			catch
			{
				return false;
			}
		}

		[HarmonyPatch(typeof(AnimScrubObject), "ScrubPickedUp")]
		[HarmonyPrefix]
		private static bool BeforeScrubPickedUp(AnimScrubObject __instance)
		{
			return !RefuseScrub(__instance);
		}

		[HarmonyPatch(typeof(AnimScrubObject), "Scrubbing")]
		[HarmonyPrefix]
		private static bool BeforeScrubbing(AnimScrubObject __instance)
		{
			return !RefuseScrub(__instance);
		}

		[HarmonyPatch(typeof(AnimScrubObject), "ScrubReleased")]
		[HarmonyPrefix]
		private static bool BeforeScrubReleased(AnimScrubObject __instance)
		{
			return !RefuseScrub(__instance);
		}

		private static bool RefuseScrub(AnimScrubObject scrub)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			try
			{
				if (!IsLocked((LevelObject?)(object)scrub))
				{
					return false;
				}
				if (_scrubRefusalSaid.Add(((Object)scrub).GetInstanceID()))
				{
					ManualLogSource logger = Plugin.Logger;
					bool flag = default(bool);
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(46, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("abilities: '");
						GameObject gameObject = ((Component)scrub).gameObject;
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((gameObject != null) ? ((Object)gameObject).name : null) ?? "?");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' is locked - its scrub is refused");
					}
					logger.LogInfo(val);
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		[HarmonyPatch(typeof(Drawer), "OpenDrawer")]
		[HarmonyPrefix]
		private static bool BeforeOpenDrawer(Drawer __instance, Action __0)
		{
			return !RefuseDrawerMove(__instance, open: true, __0);
		}

		[HarmonyPatch(typeof(Drawer), "CloseDrawer")]
		[HarmonyPrefix]
		private static bool BeforeCloseDrawer(Drawer __instance, Action __0)
		{
			return !RefuseDrawerMove(__instance, open: false, __0);
		}

		private static bool RefuseDrawerMove(Drawer drawer, bool open, Action? callback)
		{
			try
			{
				if (!IsLocked((LevelObject?)(object)drawer))
				{
					return false;
				}
				int instanceID = ((Object)drawer).GetInstanceID();
				_drawerMoveRefused[instanceID] = (open, callback);
				if (_drawerRefusalSaid.Add(instanceID))
				{
					ManualLogSource logger = Plugin.Logger;
					GameObject gameObject = ((Component)drawer).gameObject;
					logger.LogInfo((object)("abilities: drawer '" + (((gameObject != null) ? ((Object)gameObject).name : null) ?? "?") + "' is locked - the game's own open or close waits for the ability"));
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void ReplayRefusedDrawer(int id, LevelObject obj)
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			if (!_drawerMoveRefused.TryGetValue(id, out (bool, Action) value))
			{
				return;
			}
			_drawerMoveRefused.Remove(id);
			bool flag = default(bool);
			try
			{
				Drawer val = ((Il2CppObjectBase)obj).TryCast<Drawer>();
				if (!((Object)(object)val == (Object)null) && val.Open != value.Item1)
				{
					ManualLogSource logger = Plugin.Logger;
					BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(54, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("abilities: drawer '");
						GameObject gameObject = ((Component)obj).gameObject;
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(((gameObject != null) ? ((Object)gameObject).name : null) ?? "?");
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' unlocked - ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(value.Item1 ? "opening" : "closing");
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" it, as the game asked");
					}
					logger.LogInfo(val2);
					if (value.Item1)
					{
						val.OpenDrawer(value.Item2);
					}
					else
					{
						val.CloseDrawer(value.Item2);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(45, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("abilities: moving a drawer on unlock failed: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message);
				}
				logger2.LogWarning(val3);
			}
		}

		[HarmonyPatch(typeof(Drawer), "SetContainedObjectsInteractable")]
		[HarmonyPostfix]
		private static void AfterDrawerSetsContents(Drawer __instance)
		{
			try
			{
				if (_flagged.Count == 0)
				{
					return;
				}
				Dictionary<LevelObject, bool> contentsInitialInteractableStates = __instance.m_contentsInitialInteractableStates;
				if (contentsInitialInteractableStates == null || contentsInitialInteractableStates.Count == 0)
				{
					return;
				}
				Enumerator<LevelObject, bool> enumerator = contentsInitialInteractableStates.GetEnumerator();
				while (enumerator.MoveNext())
				{
					KeyValuePair<LevelObject, bool> current = enumerator.Current;
					LevelObject key = current.Key;
					if (!((Object)(object)key == (Object)null) && !current.Value)
					{
						int instanceID = ((Object)key).GetInstanceID();
						if (_flagged.Contains(instanceID))
						{
							_savedWhileLocked[instanceID] = __instance;
						}
					}
				}
			}
			catch
			{
			}
		}

		private static void FreeSavedState(int id, LevelObject obj)
		{
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			if (!_savedWhileLocked.TryGetValue(id, out Drawer value))
			{
				return;
			}
			_savedWhileLocked.Remove(id);
			bool flag = default(bool);
			try
			{
				if ((Object)(object)value == (Object)null)
				{
					return;
				}
				Dictionary<LevelObject, bool> contentsInitialInteractableStates = value.m_contentsInitialInteractableStates;
				if (contentsInitialInteractableStates != null && contentsInitialInteractableStates.ContainsKey(obj))
				{
					contentsInitialInteractableStates[obj] = true;
					ManualLogSource logger = Plugin.Logger;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(90, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("abilities: '");
						GameObject gameObject = ((Component)obj).gameObject;
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((gameObject != null) ? ((Object)gameObject).name : null) ?? "?");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' was shut in drawer ");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'");
						GameObject gameObject2 = ((Component)value).gameObject;
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((gameObject2 != null) ? ((Object)gameObject2).name : null) ?? "?");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' while locked - it comes out free when the drawer opens");
					}
					logger.LogInfo(val);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(50, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("abilities: freeing a drawer's saved state failed: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				logger2.LogWarning(val2);
			}
		}

		private static void SetFlagsOnOwnClass(LevelObject obj, bool isLocked, int depth = 0)
		{
			var (concrete, list, array) = Shadowed(obj);
			if (list.Count == 0 && array.Length == 0)
			{
				return;
			}
			object obj2 = Recast((Il2CppObjectBase)(object)obj, concrete);
			if (obj2 == null)
			{
				return;
			}
			foreach (var item3 in list)
			{
				MemberInfo item = item3.Item1;
				bool item2 = item3.Item2;
				bool flag = (isLocked ? item2 : (!item2));
				try
				{
					if (item is PropertyInfo propertyInfo)
					{
						propertyInfo.SetValue(obj2, flag);
					}
					else if (item is MethodInfo methodInfo)
					{
						methodInfo.Invoke(obj2, new object[1] { flag });
					}
				}
				catch
				{
				}
			}
		}

		private static (Type?, List<(MemberInfo, bool)>, PropertyInfo[]) Shadowed(LevelObject obj)
		{
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Expected O, but got Unknown
			string text;
			try
			{
				Type il2CppType = ((Object)obj).GetIl2CppType();
				text = ((il2CppType != null) ? ((MemberInfo)il2CppType).Name : null) ?? "";
			}
			catch
			{
				return (null, NoGates, NoNested);
			}
			if (_shadowed.TryGetValue(text, out (Type, List<(MemberInfo, bool)>, PropertyInfo[]) value))
			{
				return value;
			}
			Type type = null;
			List<(MemberInfo, bool)> list = new List<(MemberInfo, bool)>();
			List<PropertyInfo> list2 = new List<PropertyInfo>();
			bool flag = default(bool);
			try
			{
				type = WrapperFor(text, typeof(LevelObject));
				if (type != null && type != typeof(LevelObject))
				{
					Type[] types = new Type[1] { typeof(bool) };
					(string, bool)[] gates = Gates;
					for (int i = 0; i < gates.Length; i++)
					{
						(string, bool) tuple = gates[i];
						string item = tuple.Item1;
						bool item2 = tuple.Item2;
						PropertyInfo property = type.GetProperty(item, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (property != null && property.PropertyType == typeof(bool) && property.CanWrite)
						{
							list.Add((property, item2));
							continue;
						}
						MethodInfo method = type.GetMethod("Set" + item, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null);
						if (method != null)
						{
							list.Add((method, item2));
						}
					}
					PropertyInfo[] properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (PropertyInfo propertyInfo in properties)
					{
						if (propertyInfo.GetIndexParameters().Length == 0 && propertyInfo.CanRead && typeof(LevelObject).IsAssignableFrom(propertyInfo.PropertyType))
						{
							list2.Add(propertyInfo);
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(58, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("abilities: could not inspect ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", using the base flags only: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
			if (list.Count > 0)
			{
				List<string> list3 = new List<string>();
				foreach (var item4 in list)
				{
					MemberInfo item3 = item4.Item1;
					list3.Add(item3.Name);
				}
				Plugin.Logger.LogInfo((object)($"abilities: {text} redeclares {string.Join(", ", list3)}; " + "setting them on the object's own class"));
			}
			else
			{
				Plugin.Logger.LogInfo((object)("abilities: " + text + " -> base flags only" + ((type == null) ? " (no wrapper type found)" : "")));
			}
			if (list2.Count > 0)
			{
				List<string> list4 = new List<string>();
				foreach (PropertyInfo item5 in list2)
				{
					list4.Add(item5.Name);
				}
				ManualLogSource logger2 = Plugin.Logger;
				BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(42, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("abilities: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(text);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" holds its own LevelObject(s): ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(string.Join(", ", list4));
				}
				logger2.LogInfo(val2);
			}
			(Type, List<(MemberInfo, bool)>, PropertyInfo[]) tuple2 = (type, list, list2.ToArray());
			_shadowed[text] = tuple2;
			return tuple2;
		}

		private static int ApplyWanted(Dictionary<int, ObjectLock> votes, Dictionary<int, LevelObject> byId)
		{
			int num = 0;
			bool value2 = default(bool);
			foreach (KeyValuePair<int, ObjectLock> vote in votes)
			{
				if (!byId.TryGetValue(vote.Key, out LevelObject value) || (Object)(object)value == (Object)null)
				{
					continue;
				}
				bool flag = !vote.Value.Unlocked;
				bool flag2 = !flag && vote.Value.DrawerSetLocked && Slides(value);
				if (flag2)
				{
					flag = true;
				}
				if (flag && vote.Value.IsDrawerSetPart && !_flagged.Contains(vote.Key) && GameHoldsStill(value))
				{
					continue;
				}
				try
				{
					if (flag)
					{
						if (_flagged.Add(vote.Key))
						{
							_gameInteractable[vote.Key] = value.m_interactable;
						}
						else if (value.m_interactable)
						{
							_gameInteractable[vote.Key] = true;
						}
						value.SetInteractable(false);
						value.SetPreventSelection(true);
						SetFlagsOnOwnClass(value, isLocked: true);
					}
					else if (_flagged.Remove(vote.Key))
					{
						bool interactable = value.m_interactable || !_gameInteractable.TryGetValue(vote.Key, out value2) || value2;
						_gameInteractable.Remove(vote.Key);
						value.SetInteractable(interactable);
						value.SetPreventSelection(false);
						SetFlagsOnOwnClass(value, isLocked: false);
						FreeSavedState(vote.Key, value);
					}
					Freeze(value, flag, vote.Value.IsCover || flag2, vote.Value.HeldByDrawerSet);
					Tint(value, flag);
					if (!flag && !_releasing)
					{
						ReplayRefusedDrawer(vote.Key, value);
					}
					num++;
				}
				catch
				{
				}
			}
			return num;
		}

		private static bool GameHoldsStill(LevelObject obj)
		{
			try
			{
				Drawer val = ((Il2CppObjectBase)obj).TryCast<Drawer>();
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				float num = Travel(val);
				bool flag = !(num > 0.05f) && (!val.IsTray || obj.PreventSelection || !obj.Interactable);
				GameObject gameObject = ((Component)obj).gameObject;
				string value = ((gameObject != null) ? ((Object)gameObject).name : null) ?? "?";
				if (_drawersSaid.Add($"{value}|{flag}"))
				{
					Plugin.Logger.LogInfo((object)($"abilities: drawer '{value}' travel {num:0.00} (sliding={val.IsSlidingDrawer} tray={val.IsTray} fixedBox={val.IsFixedBox})" + (flag ? " - cannot be moved at all; left alone and not greyed" : " - locked")));
				}
				return flag;
			}
			catch
			{
				return false;
			}
		}

		private static float Travel(Drawer drawer)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			Transform fullyOpenPosition = drawer.FullyOpenPosition;
			Transform fullyClosedPosition = drawer.FullyClosedPosition;
			if (!((Object)(object)fullyOpenPosition == (Object)null) && !((Object)(object)fullyClosedPosition == (Object)null))
			{
				return Vector3.Distance(fullyOpenPosition.position, fullyClosedPosition.position);
			}
			return 0f;
		}

		private static bool Slides(LevelObject obj)
		{
			try
			{
				Drawer val = ((Il2CppObjectBase)obj).TryCast<Drawer>();
				if ((Object)(object)val == (Object)null || Travel(val) <= 0.05f)
				{
					return false;
				}
				GameObject gameObject = ((Component)obj).gameObject;
				string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? "?";
				if (_drawersSaid.Add(text + "|follows"))
				{
					Plugin.Logger.LogInfo((object)("abilities: drawer '" + text + "' slides - it follows its drawer lock although another group holds it"));
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static string ClassOf(ObjectController controller)
		{
			int instanceID = ((Object)controller).GetInstanceID();
			if (_classes.TryGetValue(instanceID, out string value))
			{
				return value;
			}
			Type il2CppType = ((Object)controller).GetIl2CppType();
			string text = ((il2CppType != null) ? ((MemberInfo)il2CppType).Name : null) ?? "";
			if (_classes.Count > 512)
			{
				_classes.Clear();
			}
			_classes[instanceID] = text;
			return text;
		}

		private static void Tint(LevelObject obj, bool isLocked)
		{
			Paint(obj.renderer, isLocked);
			List<SpriteRenderer> subrenderers = obj.subrenderers;
			int num = subrenderers?.Count ?? 0;
			for (int i = 0; i < num; i++)
			{
				Paint(subrenderers[i], isLocked);
			}
			if ((Object)(object)obj.renderer == (Object)null && num == 0)
			{
				PaintScrubPicture(obj, isLocked);
			}
			Paint(((Component)obj).GetComponent<SpriteRenderer>(), isLocked);
			PaintArtUnder(((Component)obj).transform, isLocked);
		}

		private static void PaintArtUnder(Transform t, bool isLocked)
		{
			for (int i = 0; i < t.childCount; i++)
			{
				Transform child = t.GetChild(i);
				if (!((Object)(object)child == (Object)null) && !((Object)(object)((Component)child).GetComponent<LevelObject>() != (Object)null) && !((Object)((Component)child).gameObject).name.Contains("Shadow"))
				{
					SpriteRenderer component = ((Component)child).GetComponent<SpriteRenderer>();
					if ((Object)(object)component != (Object)null && (!isLocked || ((Renderer)component).enabled) && (isLocked || !_heldArt.Contains(((Object)component).GetInstanceID())))
					{
						Paint(component, isLocked);
					}
					PaintArtUnder(child, isLocked);
				}
			}
		}

		private static void PaintScrubPicture(LevelObject obj, bool isLocked)
		{
			AnimScrubObject val = ((Il2CppObjectBase)obj).TryCast<AnimScrubObject>();
			Animator val2 = (((Object)(object)val == (Object)null) ? null : val.animationToScrub);
			if (!((Object)(object)val2 == (Object)null))
			{
				Paint(((Component)val2).GetComponent<SpriteRenderer>(), isLocked);
			}
		}

		private static void Paint(SpriteRenderer? renderer, bool isLocked)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)renderer == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)renderer).GetInstanceID();
			Color color = renderer.color;
			if (!isLocked)
			{
				if (_tinted.Remove(instanceID) && _original.TryGetValue(instanceID, out var value) && OurGrey(color))
				{
					renderer.color = value;
				}
				_original.Remove(instanceID);
				_fadingIn.Remove(instanceID);
				return;
			}
			if (!_tinted.Contains(instanceID) || !OurGrey(color))
			{
				if (color.a < 0.05f)
				{
					_tinted.Remove(instanceID);
					_original.Remove(instanceID);
					_fadingIn.Add(instanceID);
					return;
				}
				if (!_original.ContainsKey(instanceID))
				{
					_original[instanceID] = (Color)(_fadingIn.Remove(instanceID) ? new Color(color.r, color.g, color.b, 1f) : color);
				}
			}
			_tinted.Add(instanceID);
			if (!(renderer.color == Locked))
			{
				renderer.color = Locked;
			}
		}

		private static bool OurGrey(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (Math.Abs(c.r - Locked.r) < 0.01f && Math.Abs(c.g - Locked.g) < 0.01f)
			{
				return Math.Abs(c.b - Locked.b) < 0.01f;
			}
			return false;
		}
	}
	internal static class Backgrounds
	{
		private sealed class LevelColours
		{
			internal LevelInterface Face;

			internal Color Own;

			internal Color LevelOwn;

			internal Color Written;
		}

		private static Color[]? _palette;

		private static string? _chosenFor;

		private static int _chosenCount = -1;

		private static Color? _chosen;

		private const float MinContrast = 0.34f;

		private static readonly Dictionary<IntPtr, LevelColours> _levelOwn = new Dictionary<IntPtr, LevelColours>();

		private static readonly Dictionary<IntPtr, (Image Image, Color Own)> _menuOwn = new Dictionary<IntPtr, (Image, Color)>();

		private static int _writes;

		private static float _writesReportedAt;

		internal static int EffectiveTraps => BackgroundResets.EffectiveTraps(Inventory.BackgroundTraps, RunState.BackgroundResetAt);

		internal static int ResetsAvailable => BackgroundResets.Available(Inventory.BackgroundResetsHeld, RunState.BackgroundResetsUsed);

		private static Color[] Palette()
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (_palette != null)
			{
				return _palette;
			}
			bool flag = default(bool);
			try
			{
				foreach (Object item in (Il2CppArrayBase<Object>)(object)Object.FindObjectsOfTypeAll(Il2CppType.Of<ColorSchemesData>()))
				{
					ColorSchemesData val = ((item == (Object)null) ? null : ((Il2CppObjectBase)item).TryCast<ColorSchemesData>());
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Il2CppReferenceArray<LevelColorScheme> levelColorSchemes = val.levelColorSchemes;
					if (levelColorSchemes == null || ((Il2CppArrayBase<LevelColorScheme>)(object)levelColorSchemes).Length == 0)
					{
						continue;
					}
					Color[] array = (Color[])(object)new Color[((Il2CppArrayBase<LevelColorScheme>)(object)levelColorSchemes).Length];
					for (int i = 0; i < ((Il2CppArrayBase<LevelColorScheme>)(object)levelColorSchemes).Length; i++)
					{
						LevelColorScheme val2 = ((Il2CppArrayBase<LevelColorScheme>)(object)levelColorSchemes)[i];
						if (val2 != null)
						{
							array[i] = val2.backgroundColor;
						}
					}
					_palette = array;
					ManualLogSource logger = Plugin.Logger;
					BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(32, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("backgrounds: palette of ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(array.Length);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" colours");
					}
					logger.LogInfo(val3);
					return _palette;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val4 = new BepInExWarningLogInterpolatedStringHandler(41, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("backgrounds: could not read the palette: ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message);
				}
				logger2.LogWarning(val4);
			}
			_palette = Array.Empty<Color>();
			return _palette;
		}

		private static Color? ColourFor(int count)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (count <= 0)
			{
				return null;
			}
			Color[] array = Palette();
			if (array.Length == 0)
			{
				return null;
			}
			return array[(count - 1) % array.Length];
		}

		private static float Distance(Color a, Color b)
		{
			//IL_0000: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			float num = a.r - b.r;
			float num2 = a.g - b.g;
			float num3 = a.b - b.b;
			return Mathf.Sqrt(2f * num * num + 4f * num2 * num2 + 3f * num3 * num3);
		}

		private static List<Color> PieceColours(LevelInterface li)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			List<Color> list = new List<Color>();
			try
			{
				Level level = li.Level;
				List<ObjectController> val = ((level != null) ? level.objectControllers : null);
				if (val == null)
				{
					return list;
				}
				for (int i = 0; i < val.Count; i++)
				{
					if (list.Count >= 200)
					{
						break;
					}
					ObjectController val2 = val[i];
					GameObject val3 = (((Object)(object)val2 == (Object)null) ? null : ((Component)val2).gameObject);
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					foreach (SpriteRenderer componentsInChild in val3.GetComponentsInChildren<SpriteRenderer>(false))
					{
						if (!((Object)(object)componentsInChild == (Object)null))
						{
							list.Add(componentsInChild.color);
							if (list.Count >= 200)
							{
								break;
							}
						}
					}
				}
			}
			catch
			{
			}
			return list;
		}

		private static Color Legible(Color wanted, LevelInterface li, int count)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			Color[] array = Palette();
			if (array.Length == 0)
			{
				return wanted;
			}
			List<Color> list = PieceColours(li);
			if (list.Count == 0)
			{
				return wanted;
			}
			for (int i = 0; i < array.Length; i++)
			{
				Color val = array[(count - 1 + i) % array.Length];
				float num = float.MaxValue;
				foreach (Color item in list)
				{
					if (!(item.a < 0.2f))
					{
						float num2 = Distance(val, item);
						if (num2 < num)
						{
							num = num2;
						}
					}
				}
				if (num >= 0.34f)
				{
					if (i > 0)
					{
						Plugin.Logger.LogInfo((object)($"backgrounds: skipped {i} palette colour(s) the pieces " + "would have vanished into"));
					}
					return val;
				}
			}
			Plugin.Logger.LogWarning((object)"backgrounds: no palette colour clears this level's pieces, keeping the first");
			return wanted;
		}

		internal static Color? ForLevel()
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			int effectiveTraps = EffectiveTraps;
			Color? result = ColourFor(effectiveTraps);
			if (!result.HasValue)
			{
				return null;
			}
			GameManager instance = GameManager.Instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				LevelManager levelManager = instance.levelManager;
				obj = ((levelManager != null) ? levelManager.ActiveLevelInterface : null);
			}
			LevelInterface val = (LevelInterface)obj;
			if ((Object)(object)val == (Object)null)
			{
				return result;
			}
			string levelId = val.LevelId;
			if (_chosen.HasValue && _chosenCount == effectiveTraps && _chosenFor == levelId)
			{
				return _chosen;
			}
			_chosenFor = levelId;
			_chosenCount = effectiveTraps;
			_chosen = Legible(result.Value, val, effectiveTraps);
			return _chosen;
		}

		internal static Color? ForMenu()
		{
			return ColourFor(EffectiveTraps);
		}

		internal static void RememberMenuImage(Image image)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)image == (Object)null) && !_menuOwn.ContainsKey(((Il2CppObjectBase)image).Pointer))
			{
				_menuOwn[((Il2CppObjectBase)image).Pointer] = (image, ((Graphic)image).color);
			}
		}

		internal static void RestoreOwnColours()
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			foreach (LevelColours value in _levelOwn.Values)
			{
				try
				{
					LevelInterface face = value.Face;
					if (!((Object)(object)face == (Object)null))
					{
						if (face.BackgroundColor == value.Written)
						{
							face.BackgroundColor = value.Own;
						}
						Level level = face.Level;
						if ((Object)(object)level != (Object)null && level.backgroundColor == value.Written)
						{
							level.backgroundColor = value.LevelOwn;
						}
					}
				}
				catch
				{
				}
			}
			_levelOwn.Clear();
			foreach (var (val, color) in _menuOwn.Values)
			{
				try
				{
					if ((Object)(object)val != (Object)null)
					{
						((Graphic)val).color = color;
					}
				}
				catch
				{
				}
			}
			_menuOwn.Clear();
			try
			{
				GameManager instance = GameManager.Instance;
				object obj3;
				if (instance == null)
				{
					obj3 = null;
				}
				else
				{
					LevelManager levelManager = instance.levelManager;
					obj3 = ((levelManager != null) ? levelManager.ActiveLevelInterface : null);
				}
				LevelInterface val2 = (LevelInterface)obj3;
				Camera main = Camera.main;
				if ((Object)(object)val2 != (Object)null && (Object)(object)main != (Object)null)
				{
					main.backgroundColor = val2.ActiveBackgroundColor;
				}
			}
			catch
			{
			}
			_chosen = null;
			_chosenFor = null;
			_chosenCount = -1;
			Track.RepaintSections();
		}

		internal static void ApplyToLevel()
		{
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Expected O, but got Unknown
			//IL_00b1: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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_0097: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Track.Active)
				{
					return;
				}
				Color? val = ForLevel();
				if (!val.HasValue)
				{
					return;
				}
				GameManager instance = GameManager.Instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					LevelManager levelManager = instance.levelManager;
					obj = ((levelManager != null) ? levelManager.ActiveLevelInterface : null);
				}
				LevelInterface val2 = (LevelInterface)obj;
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
				Level level = val2.Level;
				if (!_levelOwn.TryGetValue(((Il2CppObjectBase)val2).Pointer, out LevelColours value))
				{
					value = new LevelColours
					{
						Face = val2,
						Own = val2.BackgroundColor,
						LevelOwn = (((Object)(object)level != (Object)null) ? level.backgroundColor : val2.BackgroundColor)
					};
					_levelOwn[((Il2CppObjectBase)val2).Pointer] = value;
				}
				else
				{
					if (val2.BackgroundColor != value.Written)
					{
						value.Own = val2.BackgroundColor;
					}
					if ((Object)(object)level != (Object)null && level.backgroundColor != value.Written)
					{
						value.LevelOwn = level.backgroundColor;
					}
				}
				value.Written = val.Value;
				val2.BackgroundColor = val.Value;
				Level level2 = val2.Level;
				if ((Object)(object)level2 != (Object)null)
				{
					level2.backgroundColor = val.Value;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(36, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("backgrounds: level recolour failed: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val3);
			}
		}

		internal static void Tick()
		{
			//IL_007b: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Track.Active)
				{
					return;
				}
				GameManager instance = GameManager.Instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					LevelManager levelManager = instance.levelManager;
					obj = ((levelManager != null) ? levelManager.ActiveLevelInterface : null);
				}
				LevelInterface val = (LevelInterface)obj;
				if (((Object)(object)val != (Object)null && (val.IsTransitioning || !val.LevelIsLoaded)) || ((Object)(object)val != (Object)null && val.IsCredits))
				{
					return;
				}
				Color? val2 = ForLevel();
				if (val2.HasValue)
				{
					Camera main = Camera.main;
					if (!((Object)(object)main == (Object)null) && !(main.backgroundColor == val2.Value))
					{
						main.backgroundColor = val2.Value;
						CountWrite();
					}
				}
			}
			catch
			{
			}
		}

		private static void CountWrite()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			_writes++;
			float unscaledTime = Time.unscaledTime;
			if (!(unscaledTime - _writesReportedAt < 1f))
			{
				_writesReportedAt = unscaledTime;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(42, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("backgrounds: repainted the camera ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_writes);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" time(s)");
				}
				logger.LogInfo(val);
				_writes = 0;
			}
		}
	}
	internal static class Badges
	{
		private static float _sincePills;

		private static RectTransform? _pills;

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

		private static readonly List<RectTransform> _pillTiles = new List<RectTransform>();

		private static readonly Color PillHeld = new Color(0.25f, 0.7f, 0.3f, 0.85f);

		private static readonly Color PillLocked = new Color(0.16f, 0.16f, 0.19f, 0.75f);

		private static readonly Color IconLocked = new Color(0.42f, 0.42f, 0.46f, 0.55f);

		private static readonly Dictionary<string, string> PillText = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			["Swapping"] = "SWP",
			["Stacking"] = "STK",
			["Ordering"] = "ORD",
			["Gadgets"] = "GAD",
			["Rotating"] = "ROT",
			["Grids"] = "GRD",
			["Tidying"] = "TDY",
			["Containers"] = "CON",
			["Drawer"] = "DRW",
			["Sticking"] = "STI",
			["Symmetry"] = "SYM",
			["Jigsaw"] = "JIG",
			["Distributing"] = "DST"
		};

		private static readonly Dictionary<string, string> PillSprite = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			["Swapping"] = "Books",
			["Stacking"] = "Cartridges",
			["Ordering"] = "Pencils",
			["Gadgets"] = "Lightbulb",
			["Rotating"] = "Record",
			["Grids"] = "GridTile",
			["Tidying"] = "Breadtag",
			["Containers"] = "EggCarton",
			["Drawer"] = "Drawer",
			["Sticking"] = "Stickers",
			["Symmetry"] = "Wreath",
			["Jigsaw"] = "Gingerbread",
			["Distributing"] = "Pizza"
		};

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

		private static bool _pillArtLoaded;

		private const float PillWidth = 58f;

		private const float PillHeight = 72f;

		private const float PillIcon = 54f;

		private const float PillGap = 4f;

		private const float PillRowGap = 10f;

		private const int PillsPerRow = 6;

		private const float LabelHeight = 15f;

		private const string BadgeName = "ApTrackerBadge";

		private const string NameLabel = "ApCardName";

		private const float BadgeSize = 30f;

		private static float _sinceRefresh;

		private static readonly Dictionary<int, SlotStatus> _shown = new Dictionary<int, SlotStatus>();

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

		private static bool _repeatedBuilt;

		private static Sprite? _diagonal;

		private static Texture2D? _diagonalTexture;

		private static float _sinceSubtitle;

		private static TextMeshProUGUI? _subtitle;

		private static float _sinceTag;

		private static TextMeshProUGUI? _tag;

		private static readonly Color Green = new Color(0.25f, 0.7f, 0.3f);

		private static readonly Color Red = new Color(0.85f, 0.2f, 0.2f);

		private static readonly Color FallbackStar = new Color(0.98f, 0.8f, 0.25f);

		private static float _sinceCounter;

		private static TextMeshProUGUI? _counter;

		private static Image? _counterStar;

		private static float _sinceDots;

		private static bool _dotsReported;

		private const string DotName = "ApDotState";

		private static Transform? _fittedStrip;

		private static Vector3 _fittedBase = Vector3.one;

		private static Vector2 _fittedPosition;

		private static float _fittedBarWidth;

		private static float _appliedBarWidth = -1f;

		private const float DotAllowance = 64f;

		private const float MinStripScale = 0.55f;

		private static string? _fitReported;

		private static float _sinceWhy;

		private static void LoadPillArt()
		{
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Expected O, but got Unknown
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			if (_pillArtLoaded)
			{
				return;
			}
			_pillArtLoaded = true;
			bool flag = default(bool);
			try
			{
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				string[] manifestResourceNames = executingAssembly.GetManifestResourceNames();
				foreach (string text in manifestResourceNames)
				{
					if (!text.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
					{
						continue;
					}
					using Stream stream = executingAssembly.GetManifestResourceStream(text);
					if (stream == null)
					{
						continue;
					}
					using MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					byte[] array = memoryStream.ToArray();
					if (array.Length == 0)
					{
						continue;
					}
					Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
					if (ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array)))
					{
						((Texture)val).wrapMode = (TextureWrapMode)1;
						((Texture)val).filterMode = (FilterMode)1;
						Sprite value = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
						string text2 = text;
						int num = text2.LastIndexOf('.', text2.Length - 5);
						if (num >= 0)
						{
							text2 = text2.Substring(num + 1);
						}
						text2 = text2.Substring(0, text2.Length - 4);
						_pillArt[text2] = value;
					}
				}
				ManualLogSource logger = Plugin.Logger;
				BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(31, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("badges: loaded ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(_pillArt.Count);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ability icon(s)");
				}
				logger.LogInfo(val2);
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(34, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("badges: could not load the icons: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message);
				}
				logger2.LogWarning(val3);
			}
		}

		private static Vector2 FitIcon(string ability)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (PillSprite.TryGetValue(ability, out string value) && _pillArt.TryGetValue(value, out Sprite value2) && (Object)(object)value2 != (Object)null)
			{
				Rect rect = value2.rect;
				float width = ((Rect)(ref rect)).width;
				rect = value2.rect;
				float height = ((Rect)(ref rect)).height;
				if (width > 0f && height > 0f)
				{
					if (!(width >= height))
					{
						return new Vector2(54f * width / height, 54f);
					}
					return new Vector2(54f, 54f * height / width);
				}
			}
			return new Vector2(54f, 54f);
		}

		internal static void TickAbilityPills()
		{
			//IL_0154: 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_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			_sincePills += Time.unscaledDeltaTime;
			if (_sincePills < 0.5f)
			{
				return;
			}
			_sincePills = 0f;
			LevelsTrack val = (Track.Active ? Track.CampaignTrack() : null);
			bool num = (Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy;
			AbilityState abilities = Inventory.Abilities;
			if (!num || abilities == null || !abilities.LocksEnabled)
			{
				if ((Object)(object)_pills != (Object)null)
				{
					((Component)_pills).gameObject.SetActive(false);
				}
				return;
			}
			LoadPillArt();
			if ((Object)(object)_pills == (Object)null && !BuildPills())
			{
				return;
			}
			((Component)_pills).gameObject.SetActive(true);
			for (int i = 0; i < _pillOrder.Count && i < _pillTiles.Count; i++)
			{
				RectTransform val2 = _pillTiles[i];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				bool flag = abilities.Has(_pillOrder[i]);
				Transform obj = ((Transform)val2).Find("Icon");
				Image val3 = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null);
				if ((Object)(object)val3 != (Object)null)
				{
					if ((Object)(object)val3.sprite == (Object)null && PillSprite.TryGetValue(_pillOrder[i], out string value) && _pillArt.TryGetValue(value, out Sprite value2))
					{
						val3.sprite = value2;
						((Behaviour)val3).enabled = true;
					}
					((Graphic)val3).color = (flag ? Color.white : IconLocked);
				}
				Image component = ((Component)val2).GetComponent<Image>();
				if ((Object)(object)component != (Object)null)
				{
					((Behaviour)component).enabled = (Object)(object)val3 == (Object)null || (Object)(object)val3.sprite == (Object)null;
					if (((Behaviour)component).enabled)
					{
						((Graphic)component).color = (flag ? PillHeld : PillLocked);
					}
				}
				Transform obj2 = ((Transform)val2).Find("Text");
				TextMeshProUGUI val4 = ((obj2 != null) ? ((Component)obj2).GetComponent<TextMeshProUGUI>() : null);
				if ((Object)(object)val4 != (Object)null)
				{
					((Graphic)val4).color = (flag ? new Color(1f, 1f, 1f, 0.95f) : new Color(1f, 1f, 1f, 0.3f));
				}
			}
		}

		private static bool BuildPills()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: 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_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0404: Expected O, but got Unknown
			Transform overlayRoot = Toasts.OverlayRoot;
			SlotData seed = Plugin.Seed;
			if ((Object)(object)overlayRoot == (Object)null || seed == null)
			{
				return false;
			}
			_pillOrder.Clear();
			_pillTiles.Clear();
			foreach (string item in Abilities.StripOrder)
			{
				if (seed.Abilities.Count <= 0 || seed.Abilities.ContainsKey(item))
				{
					_pillOrder.Add(item);
				}
			}
			if (_pillOrder.Count == 0)
			{
				return false;
			}
			GameObject val = new GameObject("ApAbilityStrip");
			val.transform.SetParent(overlayRoot, false);
			_pills = val.AddComponent<RectTransform>();
			_pills.anchorMin = new Vector2(0f, 1f);
			_pills.anchorMax = new Vector2(0f, 1f);
			_pills.pivot = new Vector2(0f, 1f);
			int num = (_pillOrder.Count + 6 - 1) / 6;
			float num2 = (float)Math.Min(_pillOrder.Count, 6) * 62f;
			float num3 = 438.5f - num2 * 0.5f;
			_pills.anchoredPosition = new Vector2(Mathf.Max(187f, num3), -18f);
			_pills.sizeDelta = new Vector2(num2, (float)num * 82f);
			for (int i = 0; i < _pillOrder.Count; i++)
			{
				GameObject val2 = new GameObject("ApPill_" + _pillOrder[i]);
				val2.transform.SetParent((Transform)(object)_pills, false);
				RectTransform val3 = val2.AddComponent<RectTransform>();
				val3.anchorMin = new Vector2(0f, 1f);
				val3.anchorMax = new Vector2(0f, 1f);
				val3.pivot = new Vector2(0f, 1f);
				val3.anchoredPosition = new Vector2((float)(i % 6) * 62f, (float)(-(i / 6)) * 82f);
				val3.sizeDelta = new Vector2(58f, 72f);
				Image obj = val2.AddComponent<Image>();
				((Graphic)obj).raycastTarget = false;
				((Graphic)obj).color = PillLocked;
				GameObject val4 = new GameObject("Icon");
				val4.transform.SetParent((Transform)(object)val3, false);
				RectTransform obj2 = val4.AddComponent<RectTransform>();
				obj2.anchorMin = new Vector2(0.5f, 0f);
				obj2.anchorMax = new Vector2(0.5f, 0f);
				obj2.pivot = new Vector2(0.5f, 0f);
				obj2.anchoredPosition = new Vector2(0f, 17f);
				obj2.sizeDelta = FitIcon(_pillOrder[i]);
				Image obj3 = val4.AddComponent<Image>();
				((Graphic)obj3).raycastTarget = false;
				((Graphic)obj3).color = IconLocked;
				((Behaviour)obj3).enabled = false;
				GameObject val5 = new GameObject("Text");
				val5.transform.SetParent((Transform)(object)val3, false);
				RectTransform obj4 = val5.AddComponent<RectTransform>();
				obj4.anchorMin = new Vector2(0f, 0f);
				obj4.anchorMax = new Vector2(1f, 0f);
				obj4.pivot = new Vector2(0.5f, 0f);
				obj4.anchoredPosition = Vector2.zero;
				obj4.sizeDelta = new Vector2(0f, 15f);
				_pillTiles.Add(val3);
				TextMeshProUGUI obj5 = val5.AddComponent<TextMeshProUGUI>();
				((TMP_Text)obj5).text = (PillText.TryGetValue(_pillOrder[i], out string value) ? value : _pillOrder[i].Substring(0, Math.Min(3, _pillOrder[i].Length)).ToUpperInvariant());
				((TMP_Text)obj5).alignment = (TextAlignmentOptions)514;
				((TMP_Text)obj5).fontSize = 12f;
				((Graphic)obj5).raycastTarget = false;
			}
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val6 = new BepInExInfoLogInterpolatedStringHandler(41, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val6).AppendLiteral("badges: ability strip built with ");
				((BepInExLogInterpolatedStringHandler)val6).AppendFormatted<int>(_pillOrder.Count);
				((BepInExLogInterpolatedStringHandler)val6).AppendLiteral(" pill(s)");
			}
			logger.LogInfo(val6);
			return true;
		}

		internal static void Tick(float dt)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if (!Track.Active)
			{
				return;
			}
			_sinceRefresh += dt;
			if (_sinceRefresh < 1f)
			{
				return;
			}
			_sinceRefresh = 0f;
			try
			{
				Refresh();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(24, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("badges: refresh failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
		}

		private static void Refresh()
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			SlotProgress progress = Checks.Progress;
			AbilityState abilities = Inventory.Abilities;
			TrackState state = Track.State;
			if (progress == null || abilities == null || state == null)
			{
				return;
			}
			LevelsTrack val = Track.CampaignTrack();
			List<LevelIcon> val2 = (((Object)(object)val == (Object)null) ? null : val.trackItems);
			if (val2 == null || !((Component)val).gameObject.activeInHierarchy)
			{
				return;
			}
			for (int i = 0; i < val2.Count; i++)
			{
				LevelIcon val3 = val2[i];
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				int num = Track.SlotAt(i);
				if (num < 0)
				{
					Remove(val3, "ApTrackerBadge");
					Remove(val3, "ApCardName");
					RenumberDivider(val3, Track.PackNumberAt(i));
					continue;
				}
				SlotStatus val4 = progress.StatusOf(num, (Func<string, bool>)Checks.Ledger.IsCollected, state.PacksHeld, abilities);
				CardStars.Apply(val3, i);
				if (!_shown.TryGetValue(num, out var value) || value != val4 || !((Object)(object)((Component)val3).transform.Find("ApTrackerBadge") != (Object)null))
				{
					SetBadge(val3, val4);
					SetName(val3, num);
					_shown[num] = val4;
				}
			}
			SectionStars.Apply(Track.CampaignSelect());
		}

		private static void RenumberDivider(LevelIcon icon, int pack)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			if (pack <= 0)
			{
				return;
			}
			try
			{
				string text = pack.ToString(CultureInfo.InvariantCulture);
				foreach (TextMeshProUGUI componentsInChild in ((Component)icon).GetComponentsInChildren<TextMeshProUGUI>(true))
				{
					if ((Object)(object)componentsInChild == (Object)null)
					{
						continue;
					}
					string text2 = ((TMP_Text)componentsInChild).text;
					if (!string.IsNullOrWhiteSpace(text2) && !(text2 == text))
					{
						string text3 = text2.Trim();
						if (text3.Length <= 2 && int.TryParse(text3, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _))
						{
							((TMP_Text)componentsInChild).text = text;
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(38, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("badges: could not renumber a divider: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
		}

		private static void SetBadge(LevelIcon icon, SlotStatus state)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected I4, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			Remove(icon, "ApTrackerBadge");
			GameObject val = new GameObject("ApTrackerBadge");
			val.transform.SetParent(((Component)icon).transform, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(1f, 1f);
			obj.anchorMax = new Vector2(1f, 1f);
			obj.pivot = new Vector2(1f, 1f);
			obj.anchoredPosition = new Vector2(-6f, -6f);
			obj.sizeDelta = new Vector2(30f, 30f);
			switch ((int)state)
			{
			case 0:
				AddFill(val, Green, 0f, 1f);
				break;
			case 2:
				AddFill(val, Red, 0f, 1f);
				break;
			case 1:
				AddDiagonal(val);
				break;
			case 3:
				AddFill(val, Red, 0f, 1f);
				break;
			case 4:
				AddStar(val, icon);
				break;
			}
			val.transform.SetAsLastSibling();
		}

		private static void AddDiagonal(GameObject parent)
		{
			//IL_0005: 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_001c: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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)
			GameObject val = new GameObject("Diagonal");
			val.transform.SetParent(parent.transform, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			Image obj2 = val.AddComponent<Image>();
			obj2.sprite = DiagonalSprite();
			((Graphic)obj2).color = Color.white;
			((Graphic)obj2).raycastTarget = false;
		}

		private static Sprite DiagonalSprite()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_diagonal != (Object)null)
			{
				return _diagonal;
			}
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					float num = Mathf.Clamp01((float)(i - j) / 1.5f + 0.5f);
					val.SetPixel(j, i, Color.Lerp(Red, Green, num));
				}
			}
			val.Apply();
			_diagonalTexture = val;
			_diagonal = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f));
			return _diagonal;
		}

		private static void AddFill(GameObject parent, Color colour, float from, float to)
		{
			//IL_0005: 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_001c: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Fill");
			val.transform.SetParent(parent.transform, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(from, 0f);
			obj.anchorMax = new Vector2(to, 1f);
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			Image obj2 = val.AddComponent<Image>();
			((Graphic)obj2).color = colour;
			((Graphic)obj2).raycastTarget = false;
		}

		private static void AddStar(GameObject parent, LevelIcon icon)
		{
			//IL_0005: 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_001c: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Star");
			val.transform.SetParent(parent.transform, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).raycastTarget = false;
			var (val3, color) = FindStar(icon);
			if ((Object)(object)val3 != (Object)null)
			{
				val2.sprite = val3;
				val2.preserveAspect = true;
				((Graphic)val2).color = color;
			}
			else
			{
				((Graphic)val2).color = color;
			}
		}

		private static (Sprite?, Color) FindStar(LevelIcon icon)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			Sprite val = null;
			foreach (Image componentsInChild in ((Component)icon).GetComponentsInChildren<Image>(true))
			{
				Sprite sprite = (((Object)(object)componentsInChild == (Object)null) ? null : componentsInChild.sprite);
				if ((Object)(object)sprite == (Object)null)
				{
					continue;
				}
				string te

ALTTLArchipelago.Core.dll

Decompiled 4 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using Archipelago.MultiClient.Net.Enums;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ALTTLArchipelago.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+9071b21c6adcb522865716b090945e6ed707583f")]
[assembly: AssemblyProduct("ALTTLArchipelago.Core")]
[assembly: AssemblyTitle("ALTTLArchipelago.Core")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ALTTLArchipelago.Core
{
	public static class Abilities
	{
		public const string Swapping = "Swapping";

		public const string Stacking = "Stacking";

		public const string Ordering = "Ordering";

		public const string Gadgets = "Gadgets";

		public const string Rotating = "Rotating";

		public const string Grids = "Grids";

		public const string Tidying = "Tidying";

		public const string Containers = "Containers";

		public const string Drawer = "Drawer";

		public const string Sticking = "Sticking";

		public const string Symmetry = "Symmetry";

		public const string Jigsaw = "Jigsaw";

		public const string Distributing = "Distributing";

		public static readonly IReadOnlySet<string> Baseline = new HashSet<string> { "Draggables", "GenericLevelObjects" };

		public static readonly IReadOnlySet<string> NotPuzzles = new HashSet<string> { "Pannables" };

		private static readonly Dictionary<string, string> ClassToAbility = new Dictionary<string, string>
		{
			["Shuffleables"] = "Swapping",
			["DLC2NanopetsShuffleables"] = "Swapping",
			["ShuffleablesRelative"] = "Swapping",
			["ShuffleablesRepeatingPattern"] = "Swapping",
			["CrackersShuffleables"] = "Swapping",
			["StackablesY"] = "Stacking",
			["StackablesZ"] = "Stacking",
			["DraggablesStacked"] = "Stacking",
			["TupperwareTower"] = "Stacking",
			["DraggablesOrdered"] = "Ordering",
			["Indexables"] = "Ordering",
			["Distributables"] = "Distributing",
			["RecordPlayer"] = "Gadgets",
			["ComputerErrorsController"] = "Gadgets",
			["HourglassController"] = "Gadgets",
			["MatchboxesObjectController"] = "Gadgets",
			["Toggleables"] = "Gadgets",
			["AnimScrubbables"] = "Gadgets",
			["Groupables"] = "Gadgets",
			["ScrollFieldGroupsController"] = "Gadgets",
			["TelescopeDraggables"] = "Gadgets",
			["CandlesObjectController"] = "Gadgets",
			["CatEyesController"] = "Gadgets",
			["Rotateables"] = "Rotating",
			["Frame_Rotateables"] = "Rotating",
			["RadialDance"] = "Rotating",
			["GridPuzzle"] = "Grids",
			["GridPuzzleBase"] = "Grids",
			["StackableGrid"] = "Grids",
			["Removables"] = "Tidying",
			["Pluckables"] = "Tidying",
			["Dirtyables"] = "Tidying",
			["Clearables"] = "Tidying",
			["CleanablesController"] = "Tidying",
			["Containables"] = "Containers",
			["TupperwareLids"] = "Containers",
			["TupperwareNesting"] = "Containers",
			["DrawerController"] = "Drawer",
			["DrawerExpandableController"] = "Drawer",
			["Cupboard"] = "Drawer",
			["HangingToolsController"] = "Drawer",
			["Stickables"] = "Sticking",
			["SortingItems_Draggables"] = "Sticking",
			["SymmetricalPlaceables"] = "Symmetry",
			["DraggablesJigsaw"] = "Jigsaw"
		};

		public static readonly IReadOnlyList<string> All = new string[12]
		{
			"Swapping", "Stacking", "Ordering", "Gadgets", "Rotating", "Grids", "Tidying", "Containers", "Drawer", "Sticking",
			"Symmetry", "Jigsaw"
		};

		public static readonly IReadOnlyDictionary<string, IReadOnlyList<string>> Dlc = new Dictionary<string, IReadOnlyList<string>>(StringComparer.Ordinal)
		{
			["DLC1"] = new string[0],
			["DLC2"] = new string[1] { "Distributing" }
		};

		public static readonly IReadOnlyList<string> AllWithDlc = All.Concat(Dlc.OrderBy<KeyValuePair<string, IReadOnlyList<string>>, string>((KeyValuePair<string, IReadOnlyList<string>> kv) => kv.Key, StringComparer.Ordinal).SelectMany((KeyValuePair<string, IReadOnlyList<string>> kv) => kv.Value)).ToList();

		public static readonly IReadOnlyList<string> StripOrder = All.OrderBy<string, string>((string a) => a, StringComparer.Ordinal).Concat(AllWithDlc.Skip(All.Count)).ToList();

		public static string? ForClass(string controllerClass)
		{
			if (!ClassToAbility.TryGetValue(controllerClass, out string value))
			{
				return null;
			}
			return value;
		}

		public static IReadOnlyList<string> ClassesFor(string ability)
		{
			return (from kv in ClassToAbility
				where kv.Value == ability
				select kv.Key).OrderBy<string, string>((string k) => k, StringComparer.Ordinal).ToList();
		}

		public static bool IsPuzzleController(ControllerInfo c)
		{
			return !NotPuzzles.Contains(c.Type);
		}
	}
	public sealed class AbilityState
	{
		private readonly Dictionary<string, string> _classToAbility = new Dictionary<string, string>(StringComparer.Ordinal);

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

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

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

		private bool _locksEnabled = true;

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

		public IReadOnlyCollection<string> Held
		{
			get
			{
				HashSet<string> hashSet = new HashSet<string>(_starting, StringComparer.Ordinal);
				hashSet.UnionWith(_received);
				return hashSet;
			}
		}

		public bool LocksEnabled => _locksEnabled;

		public int Version { get; private set; }

		public IReadOnlyCollection<string> Withheld => _withheld;

		public AbilityState(SlotData slot)
		{
			_locksEnabled = slot.AbilityLocks;
			foreach (var (text2, list2) in slot.Abilities)
			{
				_known.Add(text2);
				foreach (string item in list2)
				{
					if (!string.IsNullOrEmpty(item))
					{
						_classToAbility[item] = text2;
					}
				}
			}
			foreach (string startingAbility in slot.StartingAbilities)
			{
				if (!string.IsNullOrEmpty(startingAbility))
				{
					_starting.Add(startingAbility);
				}
			}
		}

		public bool IsAbility(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return _known.Contains(name);
			}
			return false;
		}

		public void SetHeld(IEnumerable<string> abilities)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (string ability in abilities)
			{
				if (!string.IsNullOrEmpty(ability))
				{
					hashSet.Add(ability);
				}
			}
			if (!hashSet.SetEquals(_received))
			{
				_received.Clear();
				_received.UnionWith(hashSet);
				Version++;
			}
		}

		public void Grant(string ability)
		{
			if (!string.IsNullOrEmpty(ability) && _received.Add(ability))
			{
				Version++;
			}
		}

		public bool Has(string ability)
		{
			if (!_withheld.Contains(ability))
			{
				if (!_starting.Contains(ability))
				{
					return _received.Contains(ability);
				}
				return true;
			}
			return false;
		}

		public bool Withhold(IEnumerable<string> abilities)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (string ability in abilities)
			{
				if (!string.IsNullOrEmpty(ability))
				{
					hashSet.Add(ability);
				}
			}
			if (hashSet.SetEquals(_withheld))
			{
				return false;
			}
			_withheld.Clear();
			_withheld.UnionWith(hashSet);
			Version++;
			return true;
		}

		public bool IsClassLocked(string controllerClass)
		{
			if (!_locksEnabled)
			{
				return false;
			}
			if (string.IsNullOrEmpty(controllerClass))
			{
				return false;
			}
			if (!_classToAbility.TryGetValue(controllerClass, out string value))
			{
				return false;
			}
			return !Has(value);
		}

		public string? AbilityFor(string controllerClass)
		{
			if (!_classToAbility.TryGetValue(controllerClass, out string value))
			{
				return null;
			}
			return value;
		}

		public bool HasAll(IEnumerable<string> abilities)
		{
			foreach (string ability in abilities)
			{
				if (!Has(ability))
				{
					return false;
				}
			}
			return true;
		}
	}
	public static class AchievementChecks
	{
		public sealed class Entry
		{
			public string LevelId { get; }

			public string AchievementId { get; }

			public string Display { get; }

			public Entry(string levelId, string achievementId, string display)
			{
				LevelId = levelId;
				AchievementId = achievementId;
				Display = display;
			}
		}

		public static readonly IReadOnlyList<Entry> All = new Entry[17]
		{
			new Entry("Cat Toys", "UNIQUE_FUN_FOR_HUMANS", "Fun for Humans Too"),
			new Entry("Eggs", "UNIQUE_EXACTING_EGGS", "Exacting Eggs"),
			new Entry("Place Setting", "UNIQUE_BAD_KITTY", "Bad Kitty"),
			new Entry("Junk Drawer", "UNIQUE_DRAW_RAINBOW", "Draw Me A Rainbow"),
			new Entry("Radial Dance Party", "UNIQUE_PERFECT_PET_CAT", "Harmonized Purr"),
			new Entry("TupperwareTower", "UNIQUE_TUPPERWARE_RAINBOW", "Rainbow To The Moon"),
			new Entry("TupperwareTower", "UNIQUE_STABLE_STACKER", "Unstable Stacker"),
			new Entry("DLC1 Fountain Pens", "DLC1_UNIQUE_PEN_CAPS", "Where Is My Cap?"),
			new Entry("DLC1 Lunch Tray", "DLC1_UNIQUE_BALANCED_MEAL", "A Balanced Meal"),
			new Entry("DLC1 Pantry", "DLC1_UNIQUE_CAN_DO_ALTITUDE", "Can Do Altitude"),
			new Entry("DLC1 Media Cabinet", "DLC1_UNIQUE_PLAYING_WITH_POWER", "Now You're Playing With Power"),
			new Entry("DLC1 Trophy Cabinet", "DLC1_UNIQUE_SHOW_OFF", "Show Off"),
			new Entry("DLC2 Water Glasses", "DLC2_UNIQUE_WATER", "I'll Take My Water Neat"),
			new Entry("DLC2 Pizza", "DLC2_UNIQUE_TOP_HEAVY", "Top Heavy Slice"),
			new Entry("DLC2 Sticky Drawer", "DLC2_UNIQUE_STICKY", "Sticky Wand"),
			new Entry("DLC2 Junk Drawer Transforming", "DLC2_UNIQUE_WRONG_END", "Grabbed the Wrong End"),
			new Entry("DLC2 Music Box", "DLC2_UNIQUE_KEEPING_COUNT", "Keeping Count")
		};

		public static IReadOnlyList<Entry> For(string? levelId)
		{
			List<Entry> list = new List<Entry>();
			if (string.IsNullOrEmpty(levelId))
			{
				return list;
			}
			foreach (Entry item in All)
			{
				if (item.LevelId == levelId)
				{
					list.Add(item);
				}
			}
			return list;
		}

		public static Entry? Find(string? levelId, string? achievementId)
		{
			if (string.IsNullOrEmpty(levelId) || string.IsNullOrEmpty(achievementId))
			{
				return null;
			}
			foreach (Entry item in All)
			{
				if (item.LevelId == levelId && item.AchievementId == achievementId)
				{
					return item;
				}
			}
			return null;
		}
	}
	public enum AfterPuzzle
	{
		Panel,
		MoveOn,
		TheGames
	}
	public static class AfterPuzzleRoute
	{
		public static AfterPuzzle For(bool solutionsLeft, bool skipping = false, bool gameShowsPanel = true)
		{
			if (!skipping || gameShowsPanel)
			{
				if (!solutionsLeft)
				{
					return AfterPuzzle.MoveOn;
				}
				return AfterPuzzle.Panel;
			}
			return AfterPuzzle.TheGames;
		}
	}
	public static class ApPalette
	{
		[Flags]
		public enum ItemFlags
		{
			None = 0,
			Progression = 1,
			Useful = 2,
			Trap = 4
		}

		public const string Black = "000000";

		public const string Red = "EE0000";

		public const string Green = "00FF7F";

		public const string Yellow = "FAFAD2";

		public const string Blue = "6495ED";

		public const string Magenta = "EE00EE";

		public const string Cyan = "00EEEE";

		public const string SlateBlue = "6D8BE8";

		public const string Plum = "AF99EF";

		public const string Salmon = "FA8072";

		public const string White = "FFFFFF";

		public const string Orange = "FF7700";

		public static string ForItem(ItemFlags flags)
		{
			if (flags == ItemFlags.None)
			{
				return "00EEEE";
			}
			if ((flags & ItemFlags.Progression) != ItemFlags.None)
			{
				return "AF99EF";
			}
			if ((flags & ItemFlags.Useful) != ItemFlags.None)
			{
				return "6D8BE8";
			}
			if ((flags & ItemFlags.Trap) != ItemFlags.None)
			{
				return "FA8072";
			}
			return "00EEEE";
		}

		public static string ForLocation()
		{
			return "00FF7F";
		}

		public static string ForPlayer(bool isSelf)
		{
			if (!isSelf)
			{
				return "FAFAD2";
			}
			return "EE00EE";
		}

		public static string Paint(string text, string hex)
		{
			return $"<color=#{hex}>{Escape(text)}</color>";
		}

		public static string Escape(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			if (text.IndexOf('<') < 0)
			{
				return text;
			}
			string text2 = Regex.Replace(text, "</noparse>", "", RegexOptions.IgnoreCase);
			return "<noparse>" + text2 + "</noparse>";
		}
	}
	public enum BackgroundResetRefusal
	{
		None,
		NoneHeld,
		NothingToReset
	}
	public static class BackgroundResets
	{
		public static int Available(int received, int used)
		{
			return Math.Max(0, received - used);
		}

		public static int EffectiveTraps(int traps, int resetAt)
		{
			return Math.Max(0, traps - resetAt);
		}

		public static BackgroundResetRefusal Check(int available, int traps, int resetAt)
		{
			if (available <= 0)
			{
				return BackgroundResetRefusal.NoneHeld;
			}
			if (EffectiveTraps(traps, resetAt) <= 0)
			{
				return BackgroundResetRefusal.NothingToReset;
			}
			return BackgroundResetRefusal.None;
		}
	}
	public sealed class CachedSession
	{
		public static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
		{
			ReadCommentHandling = JsonCommentHandling.Skip,
			AllowTrailingCommas = true,
			WriteIndented = false
		};

		[JsonPropertyName("slot_name")]
		public string SlotName { get; set; } = "";

		[JsonPropertyName("seed")]
		public string Seed { get; set; } = "";

		[JsonPropertyName("saved_at")]
		public string SavedAt { get; set; } = "";

		[JsonPropertyName("items")]
		public List<string> Items { get; set; } = new List<string>();

		[JsonPropertyName("slot_data")]
		public SlotData Slot { get; set; } = new SlotData();

		[JsonPropertyName("checked")]
		public List<string> Checked { get; set; } = new List<string>();

		public string ToJson()
		{
			return JsonSerializer.Serialize(this, JsonOptions);
		}

		public static CachedSession? FromJson(string json)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				return null;
			}
			try
			{
				return JsonSerializer.Deserialize<CachedSession>(json, JsonOptions);
			}
			catch (JsonException)
			{
				return null;
			}
		}

		public IReadOnlyList<string> Problems()
		{
			List<string> list = new List<string>();
			if (string.IsNullOrWhiteSpace(SlotName))
			{
				list.Add("no slot name");
			}
			if (string.IsNullOrWhiteSpace(Seed))
			{
				list.Add("no seed");
			}
			foreach (string item in Slot.Problems())
			{
				list.Add(item);
			}
			return list;
		}

		public bool IsFor(string slotName)
		{
			if (!string.IsNullOrWhiteSpace(slotName))
			{
				return string.Equals(SlotName, slotName, StringComparison.Ordinal);
			}
			return false;
		}

		public static CachedSession Of(string slotName, string seed, SlotData slot, IReadOnlyList<string> items, DateTime now, IEnumerable<string>? serverChecked = null)
		{
			return new CachedSession
			{
				SlotName = slotName,
				Seed = seed,
				Slot = slot,
				Items = new List<string>(items),
				Checked = ((serverChecked == null) ? new List<string>() : new List<string>(serverChecked)),
				SavedAt = now.ToString("yyyy-MM-dd HH:mm:ss")
			};
		}
	}
	public sealed class CheckLedger
	{
		private readonly HashSet<string> _collected = new HashSet<string>(StringComparer.Ordinal);

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

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

		public IReadOnlyCollection<string> Collected => _collected;

		public IReadOnlyCollection<string> Owed => _owed;

		public bool IsCollected(string name)
		{
			return _collected.Contains(name);
		}

		public bool Check(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (!_collected.Add(name))
			{
				return false;
			}
			_owed.Add(name);
			return true;
		}

		public bool RecordLocal(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			_local.Add(name);
			return _collected.Add(name);
		}

		public IReadOnlyList<string> LocalForSaving()
		{
			List<string> list = new List<string>(_local);
			list.Sort(StringComparer.Ordinal);
			return list;
		}

		public void RestoreLocal(IEnumerable<string> names)
		{
			foreach (string name in names)
			{
				if (!string.IsNullOrEmpty(name))
				{
					_local.Add(name);
					_collected.Add(name);
				}
			}
		}

		public void AdoptServerChecks(IEnumerable<string> names)
		{
			foreach (string name in names)
			{
				if (!string.IsNullOrEmpty(name))
				{
					_collected.Add(name);
				}
			}
		}

		public void Acknowledge(IEnumerable<string> names)
		{
			foreach (string name in names)
			{
				_owed.Remove(name);
			}
		}

		public void RestoreOwed(IEnumerable<string> names)
		{
			foreach (string name in names)
			{
				if (!string.IsNullOrEmpty(name))
				{
					_collected.Add(name);
					_owed.Add(name);
				}
			}
		}

		public IReadOnlyList<string> OwedForSaving()
		{
			List<string> list = new List<string>(_owed);
			list.Sort(StringComparer.Ordinal);
			return list;
		}

		public IReadOnlyList<string> ServerCheckedForSaving()
		{
			List<string> list = new List<string>();
			foreach (string item in _collected)
			{
				if (!_owed.Contains(item) && !_local.Contains(item))
				{
					list.Add(item);
				}
			}
			list.Sort(StringComparer.Ordinal);
			return list;
		}

		public void Clear()
		{
			_collected.Clear();
			_owed.Clear();
			_local.Clear();
		}
	}
	public sealed class CheckRouter
	{
		private readonly SlotData _slot;

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

		private readonly Dictionary<int, IReadOnlyList<string>> _forSlot = new Dictionary<int, IReadOnlyList<string>>();

		public CheckRouter(SlotData slot)
		{
			_slot = slot ?? throw new ArgumentNullException("slot");
			foreach (SlotEntry slot2 in _slot.Slots)
			{
				string text = LocationNames.Beaten(slot2.LevelId, slot2.Instance);
				if (_slot.Requirements.ContainsKey(text))
				{
					_events.Add(text);
				}
			}
		}

		public bool IsLocalEvent(string? name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return _events.Contains(name);
			}
			return false;
		}

		public int BeatenCount(Func<string, bool> isCollected)
		{
			int num = 0;
			foreach (string @event in _events)
			{
				if (isCollected(@event))
				{
					num++;
				}
			}
			return num;
		}

		public (int Lit, int Total) RunStars(Func<string, bool> isCollected)
		{
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < _slot.Slots.Count; i++)
			{
				(int Lit, int Total) tuple = SolutionStars(i, isCollected);
				int item = tuple.Lit;
				int item2 = tuple.Total;
				num += item;
				num2 += item2;
			}
			return (Lit: num, Total: num2);
		}

		public (int Lit, int Total) StarsOf(IEnumerable<int> slots, Func<string, bool> isCollected)
		{
			int num = 0;
			int num2 = 0;
			foreach (int slot in slots)
			{
				(int Lit, int Total) tuple = SolutionStars(slot, isCollected);
				int item = tuple.Lit;
				int item2 = tuple.Total;
				num += item;
				num2 += item2;
			}
			return (Lit: num, Total: num2);
		}

		public bool HasWorkLeft(int slotIndex, Func<string, bool> isCollected)
		{
			foreach (string item in ForSlot(slotIndex))
			{
				if (!isCollected(item))
				{
					return true;
				}
			}
			return false;
		}

		public (int Lit, int Total) SolutionStars(int slotIndex, Func<string, bool> isCollected)
		{
			int num = 0;
			int num2 = 0;
			foreach (string item in SolutionsOf(slotIndex))
			{
				num2++;
				if (isCollected(item))
				{
					num++;
				}
			}
			return (Lit: num, Total: num2);
		}

		public IReadOnlyList<string> SolutionsOf(int slotIndex)
		{
			List<string> list = new List<string>();
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null)
			{
				return list;
			}
			if (_slot.Endings.TryGetValue(slotEntry.LevelId, out List<EndingEntry> value) && value.Count > 0)
			{
				foreach (EndingEntry item in value)
				{
					string text = LocationNames.Ending(slotEntry.LevelId, slotEntry.Instance, item.Location);
					if (Exists(text))
					{
						list.Add(text);
					}
				}
				return list;
			}
			int num = 1;
			while (true)
			{
				string text2 = LocationNames.Solution(slotEntry.LevelId, slotEntry.Instance, num);
				if (!Exists(text2))
				{
					break;
				}
				list.Add(text2);
				num++;
			}
			return list;
		}

		public string? ForEnding(int slotIndex, string? solutionId, IReadOnlyList<string> foundInOrder)
		{
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null)
			{
				return null;
			}
			string text = solutionId ?? "";
			int num = -1;
			for (int i = 0; i < foundInOrder.Count; i++)
			{
				if (foundInOrder[i] == text)
				{
					num = i;
					break;
				}
			}
			if (num < 0)
			{
				return null;
			}
			if (!_slot.Endings.TryGetValue(slotEntry.LevelId, out List<EndingEntry> value) || value.Count == 0)
			{
				return ForSolution(slotIndex, num + 1);
			}
			bool[] array = new bool[value.Count];
			int num2 = -1;
			for (int j = 0; j <= num; j++)
			{
				num2 = Claim(value, foundInOrder[j], array);
				if (num2 >= 0)
				{
					array[num2] = true;
				}
			}
			if (num2 < 0)
			{
				num2 = 0;
			}
			return Present(LocationNames.Ending(slotEntry.LevelId, slotEntry.Instance, value[num2].Location));
		}

		private static int Claim(IReadOnlyList<EndingEntry> endings, string id, bool[] taken)
		{
			for (int i = 0; i < endings.Count; i++)
			{
				if (!taken[i] && Endings.Alternatives(endings[i].Id).Contains(id))
				{
					return i;
				}
			}
			for (int j = 0; j < endings.Count; j++)
			{
				if (!taken[j] && endings[j].Id == null)
				{
					return j;
				}
			}
			for (int k = 0; k < endings.Count; k++)
			{
				if (!taken[k])
				{
					return k;
				}
			}
			return -1;
		}

		private string? Present(string name)
		{
			if (!Exists(name))
			{
				return null;
			}
			return name;
		}

		public IReadOnlyList<string> BeatenBySolutions(Func<string, bool> isCollected)
		{
			List<string> list = new List<string>();
			for (int i = 0; i < _slot.Slots.Count; i++)
			{
				string text = ForBeaten(i);
				if (text != null && !isCollected(text) && SolutionStars(i, isCollected).Lit > 0)
				{
					list.Add(text);
				}
			}
			return list;
		}

		public bool Exists(string? name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return _slot.Requirements.ContainsKey(name);
			}
			return false;
		}

		public string? ForController(int slotIndex, string? controllerName)
		{
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null || string.IsNullOrEmpty(controllerName))
			{
				return null;
			}
			if (!_slot.ControllerGroups.TryGetValue(slotEntry.LevelId, out Dictionary<string, string> value))
			{
				return null;
			}
			if (!value.TryGetValue(controllerName, out var value2))
			{
				return null;
			}
			string text = LocationNames.Part(slotEntry.LevelId, slotEntry.Instance, value2);
			if (!Exists(text))
			{
				return null;
			}
			return text;
		}

		public IReadOnlyList<string> GroupMembers(int slotIndex, string? controllerName)
		{
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null || string.IsNullOrEmpty(controllerName))
			{
				return Array.Empty<string>();
			}
			if (!_slot.ControllerGroups.TryGetValue(slotEntry.LevelId, out Dictionary<string, string> value))
			{
				return Array.Empty<string>();
			}
			if (!value.TryGetValue(controllerName, out var group))
			{
				return Array.Empty<string>();
			}
			return (from kv in value
				where kv.Value == @group
				select kv.Key).ToList();
		}

		public static bool GroupSolved(IEnumerable<string> members, IReadOnlyDictionary<string, bool> solved)
		{
			bool result = false;
			foreach (string member in members)
			{
				if (solved.TryGetValue(member, out var value))
				{
					if (!value)
					{
						return false;
					}
					result = true;
				}
			}
			return result;
		}

		public IReadOnlyList<string> PartsForSolution(int slotIndex, string? solutionId)
		{
			List<string> list = new List<string>();
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null)
			{
				return list;
			}
			if (!_slot.ControllerGroups.TryGetValue(slotEntry.LevelId, out Dictionary<string, string> value))
			{
				return list;
			}
			foreach (string item in SolutionParts.ControllersFor(solutionId, value.Keys))
			{
				string text = ForController(slotIndex, item);
				if (text != null && !list.Contains(text))
				{
					list.Add(text);
				}
			}
			return list;
		}

		public string? ForSolution(int slotIndex, int solutionNumber)
		{
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null || solutionNumber < 1)
			{
				return null;
			}
			string text = LocationNames.Solution(slotEntry.LevelId, slotEntry.Instance, solutionNumber);
			if (!Exists(text))
			{
				return null;
			}
			return text;
		}

		public string? ForAchievement(int slotIndex, string? achievementId)
		{
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null)
			{
				return null;
			}
			AchievementChecks.Entry entry = AchievementChecks.Find(slotEntry.LevelId, achievementId);
			if (entry == null)
			{
				return null;
			}
			string text = LocationNames.Achievement(slotEntry.LevelId, slotEntry.Instance, entry.Display);
			if (!Exists(text))
			{
				return null;
			}
			return text;
		}

		public IReadOnlyList<string> ForAchievements(int slotIndex)
		{
			List<string> list = new List<string>();
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null)
			{
				return list;
			}
			foreach (AchievementChecks.Entry item in AchievementChecks.For(slotEntry.LevelId))
			{
				string text = LocationNames.Achievement(slotEntry.LevelId, slotEntry.Instance, item.Display);
				if (Exists(text))
				{
					list.Add(text);
				}
			}
			return list;
		}

		public string? ForBeaten(int slotIndex)
		{
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null)
			{
				return null;
			}
			string text = LocationNames.Beaten(slotEntry.LevelId, slotEntry.Instance);
			if (!Exists(text))
			{
				return null;
			}
			return text;
		}

		public IReadOnlyList<string> ForSlot(int slotIndex)
		{
			if (_forSlot.TryGetValue(slotIndex, out IReadOnlyList<string> value))
			{
				return value;
			}
			IReadOnlyList<string> readOnlyList = BuildForSlot(slotIndex);
			_forSlot[slotIndex] = readOnlyList;
			return readOnlyList;
		}

		private IReadOnlyList<string> BuildForSlot(int slotIndex)
		{
			SlotEntry slotEntry = SlotAt(slotIndex);
			if (slotEntry == null)
			{
				return Array.Empty<string>();
			}
			List<string> list = new List<string>(SolutionsOf(slotIndex));
			if (_slot.ControllerGroups.TryGetValue(slotEntry.LevelId, out Dictionary<string, string> value))
			{
				foreach (string item in value.Values.Distinct<string>(StringComparer.Ordinal))
				{
					string text = LocationNames.Part(slotEntry.LevelId, slotEntry.Instance, item);
					if (Exists(text))
					{
						list.Add(text);
					}
				}
			}
			string text2 = ForBeaten(slotIndex);
			if (text2 != null)
			{
				list.Add(text2);
			}
			list.AddRange(ForAchievements(slotIndex));
			return list;
		}

		private SlotEntry? SlotAt(int slotIndex)
		{
			if (slotIndex < 0 || slotIndex >= _slot.Slots.Count)
			{
				return null;
			}
			return _slot.Slots[slotIndex];
		}
	}
	public static class CompletionText
	{
		public static string Of(int lit, int total)
		{
			int value = ((total > 0) ? ((int)Math.Round(100.0 * (double)lit / (double)total, MidpointRounding.AwayFromZero)) : 0);
			IFormatProvider invariantCulture = CultureInfo.InvariantCulture;
			DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(5, 3, invariantCulture);
			handler.AppendFormatted(lit);
			handler.AppendLiteral("/");
			handler.AppendFormatted(total);
			handler.AppendLiteral(" (");
			handler.AppendFormatted(value);
			handler.AppendLiteral("%)");
			return string.Create(invariantCulture, ref handler);
		}
	}
	public sealed class ConnectVerdict
	{
		public string? Refusal { get; }

		public string? Warning { get; }

		public bool Accepted => Refusal == null;

		private ConnectVerdict(string? refusal, string? warning)
		{
			Refusal = refusal;
			Warning = warning;
		}

		internal static ConnectVerdict Refuse(string why)
		{
			return new ConnectVerdict(why, null);
		}

		internal static ConnectVerdict Allow(string? warning = null)
		{
			return new ConnectVerdict(null, warning);
		}
	}
	public static class ConnectGuard
	{
		public const string UnknownVersionWarning = "this seed predates the version check, so the mod cannot tell whether it matches. If the run misbehaves, regenerate it with the apworld from this release.";

		public static ConnectVerdict Evaluate(SlotData slot, string modVersion)
		{
			return Evaluate(slot, modVersion, null);
		}

		public static ConnectVerdict Evaluate(SlotData slot, string modVersion, IReadOnlyCollection<string>? installedDlc)
		{
			string text = slot.VersionMismatch(modVersion);
			if (text != null)
			{
				return ConnectVerdict.Refuse(text);
			}
			IReadOnlyList<string> readOnlyList = slot.Problems();
			if (readOnlyList.Count > 0)
			{
				return ConnectVerdict.Refuse("this seed cannot be played: " + string.Join("; ", readOnlyList));
			}
			if (installedDlc != null)
			{
				List<string> list = slot.RequiredDlc.Where((string key) => !installedDlc.Contains(key)).Select(DlcName).ToList();
				if (list.Count > 0)
				{
					return ConnectVerdict.Refuse("this seed was built with " + string.Join(" and ", list) + ", which is not installed. Install it, or ask for a seed generated without it.");
				}
			}
			if (!string.IsNullOrEmpty(slot.WorldVersion))
			{
				return ConnectVerdict.Allow();
			}
			return ConnectVerdict.Allow("this seed predates the version check, so the mod cannot tell whether it matches. If the run misbehaves, regenerate it with the apworld from this release.");
		}

		private static string DlcName(string key)
		{
			if (!(key == "DLC1"))
			{
				if (key == "DLC2")
				{
					return "the Seeing Stars DLC";
				}
				return key;
			}
			return "the Cupboards and Drawers DLC";
		}
	}
	public sealed class ControllerGroup
	{
		public IReadOnlyList<string> Members { get; init; } = Array.Empty<string>();

		public string Name
		{
			get
			{
				if (Members.Count <= 0)
				{
					return "";
				}
				return Members[0];
			}
		}

		public string DisplayName { get; init; } = "";

		public IReadOnlySet<string> Abilities { get; init; } = new HashSet<string>();
	}
	public static class ControllerGroups
	{
		public static IReadOnlyList<ControllerGroup> For(LevelInfo level)
		{
			List<ControllerInfo> source = level.Controllers.Where(Abilities.IsPuzzleController).ToList();
			List<ControllerInfo> list = source.Where((ControllerInfo c) => !c.NotALocation).ToList();
			Dictionary<string, List<ControllerInfo>> byName = new Dictionary<string, List<ControllerInfo>>(StringComparer.Ordinal);
			foreach (ControllerInfo item in list)
			{
				if (!byName.TryGetValue(item.Name, out List<ControllerInfo> value))
				{
					value = new List<ControllerInfo>();
					byName[item.Name] = value;
				}
				value.Add(item);
			}
			List<string> list2 = byName.Keys.OrderBy<string, string>((string n) => n, StringComparer.Ordinal).ToList();
			Dictionary<string, string> parent = list2.ToDictionary<string, string, string>((string n) => n, (string n) => n, StringComparer.Ordinal);
			foreach (string item2 in list2)
			{
				foreach (string item3 in list2)
				{
					if (!(item2 == item3) && DependsOn(item2, item3) && DependsOn(item3, item2))
					{
						Union(item2, item3);
					}
				}
			}
			Dictionary<string, List<ControllerInfo>> gatingByName = source.GroupBy<ControllerInfo, string>((ControllerInfo c) => c.Name, StringComparer.Ordinal).ToDictionary<IGrouping<string, ControllerInfo>, string, List<ControllerInfo>>((IGrouping<string, ControllerInfo> g) => g.Key, (IGrouping<string, ControllerInfo> g) => g.ToList(), StringComparer.Ordinal);
			Dictionary<string, List<ControllerInfo>> allByName = new Dictionary<string, List<ControllerInfo>>(StringComparer.Ordinal);
			foreach (ControllerInfo controller in level.Controllers)
			{
				if (!allByName.TryGetValue(controller.Name, out List<ControllerInfo> value2))
				{
					value2 = new List<ControllerInfo>();
					allByName[controller.Name] = value2;
				}
				value2.Add(controller);
			}
			IReadOnlyDictionary<string, string> display = PartNames.ForLevel(level);
			Dictionary<string, string> mergedName = new Dictionary<string, string>(StringComparer.Ordinal);
			foreach (KeyValuePair<string, List<string>> mergedPart in level.MergedParts)
			{
				List<string> list3 = mergedPart.Value.Where(parent.ContainsKey).ToList();
				for (int num = 1; num < list3.Count; num++)
				{
					Union(list3[0], list3[num]);
				}
				foreach (string item4 in list3)
				{
					mergedName[item4] = mergedPart.Key;
				}
			}
			return list2.GroupBy<string, string>(Find, StringComparer.Ordinal).Select(delegate(IGrouping<string, string> g)
			{
				List<string> list4 = g.OrderBy<string, string>((string n) => n, StringComparer.Ordinal).ToList();
				string text = list4[0];
				string value3;
				string value4;
				return new ControllerGroup
				{
					Members = list4,
					Abilities = WithDependencies(list4),
					DisplayName = (mergedName.TryGetValue(text, out value3) ? value3 : (display.TryGetValue(text, out value4) ? value4 : text))
				};
			}).OrderBy<ControllerGroup, string>((ControllerGroup g) => g.Name, StringComparer.Ordinal)
				.ToList();
			bool DependsOn(string from, string to)
			{
				if (byName.TryGetValue(from, out List<ControllerInfo> value3))
				{
					return value3.Any((ControllerInfo c) => c.DependsOn.Contains<string>(to, StringComparer.Ordinal));
				}
				return false;
			}
			string Find(string x)
			{
				while (parent[x] != x)
				{
					parent[x] = parent[parent[x]];
					x = parent[x];
				}
				return x;
			}
			IEnumerable<string> Own(string name)
			{
				return (from c in gatingByName[name]
					select Abilities.ForClass(c.Type) into a
					where a != null
					select a).Cast<string>();
			}
			void Union(string a, string b)
			{
				string text = Find(a);
				string text2 = Find(b);
				string text3 = text;
				if (text3 != text2)
				{
					parent[text2] = text3;
				}
			}
			IReadOnlySet<string> WithDependencies(IEnumerable<string> members)
			{
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				HashSet<string> hashSet2 = new HashSet<string>(members, StringComparer.Ordinal);
				Queue<string> queue = new Queue<string>(hashSet2);
				while (queue.Count > 0)
				{
					string text = queue.Dequeue();
					if (gatingByName.ContainsKey(text))
					{
						foreach (string item5 in Own(text))
						{
							hashSet.Add(item5);
						}
					}
					if (allByName.TryGetValue(text, out List<ControllerInfo> value3))
					{
						foreach (ControllerInfo item6 in value3)
						{
							foreach (string item7 in item6.DependsOn)
							{
								if (hashSet2.Add(item7))
								{
									queue.Enqueue(item7);
								}
							}
						}
					}
				}
				return hashSet;
			}
		}

		public static IReadOnlySet<string> AbilitiesForLevel(LevelInfo level)
		{
			HashSet<string> hashSet = new HashSet<string>(AbilitiesTaughtBy(level), StringComparer.Ordinal);
			foreach (string extraAbility in level.ExtraAbilities)
			{
				hashSet.Add(extraAbility);
			}
			return hashSet;
		}

		public static IReadOnlySet<string> AbilitiesTaughtBy(LevelInfo level)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (ControllerGroup item in For(level))
			{
				foreach (string ability in item.Abilities)
				{
					hashSet.Add(ability);
				}
			}
			foreach (ControllerInfo controller in level.Controllers)
			{
				if (controller.NotALocation && Abilities.IsPuzzleController(controller))
				{
					string text = Abilities.ForClass(controller.Type);
					if (text != null)
					{
						hashSet.Add(text);
					}
				}
			}
			return hashSet;
		}
	}
	public static class ControllerTypes
	{
		public static readonly IReadOnlyCollection<string> NeverScored = new HashSet<string>(StringComparer.Ordinal) { "Pannables" };

		public static bool Scores(string? runtimeType)
		{
			if (!string.IsNullOrEmpty(runtimeType))
			{
				return !NeverScored.Contains<string>(runtimeType);
			}
			return true;
		}
	}
	public static class DisplayNames
	{
		private static readonly (string Prefix, string Pack)[] Packs = new(string, string)[8]
		{
			("GoodTidings_", "Good Tidings"),
			("TrickOrTidy_", "Trick or Tidy"),
			("MerryMess_", "Merry Mess"),
			("NeatStreak_", "Drawer Chores"),
			("SomethingEggstra ", "Something Eggstra"),
			("SnackPack ", "Snack Pack"),
			("DLC1 ", "Cupboards and Drawers"),
			("DLC2 ", "Seeing Stars")
		};

		private static readonly Dictionary<string, string> Overrides = new Dictionary<string, string>(StringComparer.Ordinal) { ["JackOLanterns"] = "Jack O'Lanterns" };

		private static readonly Regex CamelBoundary = new Regex("(?<=[a-z])(?=[A-Z])", RegexOptions.Compiled);

		private static readonly ConcurrentDictionary<string, string> Cache = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);

		public static string For(string levelId)
		{
			if (string.IsNullOrWhiteSpace(levelId))
			{
				return "";
			}
			if (Cache.TryGetValue(levelId, out string value))
			{
				return value;
			}
			string text = Build(levelId);
			Cache[levelId] = text;
			return text;
		}

		private static string Build(string levelId)
		{
			(string, string)[] packs = Packs;
			for (int i = 0; i < packs.Length; i++)
			{
				var (text, text2) = packs[i];
				if (levelId.StartsWith(text, StringComparison.Ordinal))
				{
					string text3 = levelId.Substring(text.Length);
					text3 = (Overrides.TryGetValue(text3, out string value) ? value : Split(text3));
					text3 = text3.Replace("(", "").Replace(")", "");
					return Collapse(text3) + " (" + text2 + ")";
				}
			}
			string value2;
			return Collapse(Overrides.TryGetValue(levelId, out value2) ? value2 : Split(levelId));
		}

		private static string Split(string s)
		{
			return CamelBoundary.Replace(s, " ");
		}

		private static string Collapse(string s)
		{
			return string.Join(" ", s.Split(' ', StringSplitOptions.RemoveEmptyEntries));
		}
	}
	public static class EmptyLevelWatch
	{
		public static bool LoadedEmpty(bool isChapter, bool isCredits, int controllers, int objects)
		{
			if (!isChapter && !isCredits && controllers == 0)
			{
				return objects == 0;
			}
			return false;
		}
	}
	public sealed class EndingInfo
	{
		public string? Id { get; }

		public string Suffix { get; }

		public string? Group { get; }

		public EndingInfo(string? id, string suffix, string? group)
		{
			Id = id;
			Suffix = suffix;
			Group = group;
		}
	}
	public static class Endings
	{
		public static IReadOnlyList<EndingInfo> For(LevelInfo level)
		{
			List<EndingInfo> list = new List<EndingInfo>();
			if (level.Endings == null || level.Endings.Count == 0)
			{
				for (int i = 1; i <= level.SolutionCount; i++)
				{
					list.Add(new EndingInfo(null, Numbered(i), null));
				}
				return list;
			}
			IReadOnlyList<ControllerGroup> groups = ControllerGroups.For(level);
			Dictionary<string, ControllerGroup> memberOf = new Dictionary<string, ControllerGroup>(StringComparer.Ordinal);
			foreach (ControllerGroup item in groups)
			{
				foreach (string member in item.Members)
				{
					memberOf[member] = item;
				}
			}
			if (level.Endings.Count == 1)
			{
				list.Add(new EndingInfo(level.Endings[0], "Solution", null));
				return list;
			}
			List<string> list2 = level.Endings.Select(delegate(string id)
			{
				if (id == null)
				{
					return (string)null;
				}
				string group = GroupOf(id);
				if (group != null)
				{
					ControllerGroup controllerGroup = groups.First((ControllerGroup x) => x.Name == group);
					if (controllerGroup.Members.Count == 1)
					{
						return controllerGroup.DisplayName;
					}
				}
				return Readable(Prefix(Primary(id)));
			}).ToList();
			int num = 0;
			for (int num2 = 0; num2 < level.Endings.Count; num2++)
			{
				string text = level.Endings[num2];
				if (text == null)
				{
					num++;
					list.Add(new EndingInfo(null, "Solution: Other " + num.ToString(CultureInfo.InvariantCulture), null));
					continue;
				}
				string name = list2[num2];
				if (list2.Count((string b) => b == name) > 1)
				{
					name = name + " " + IndexOf(Primary(text), num2).ToString(CultureInfo.InvariantCulture);
				}
				list.Add(new EndingInfo(text, "Solution: " + name, GroupOf(text)));
			}
			return list;
			string? GroupOf(string? id)
			{
				if (id == null)
				{
					return null;
				}
				List<string> list3 = (from n in SolutionParts.ControllersFor(Primary(id), memberOf.Keys)
					select memberOf[n].Name).Distinct<string>(StringComparer.Ordinal).ToList();
				if (list3.Count != 1)
				{
					return null;
				}
				return list3[0];
			}
		}

		public static IReadOnlySet<string> EndingGroups(LevelInfo level)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (EndingInfo item in For(level))
			{
				if (item.Group != null)
				{
					hashSet.Add(item.Group);
				}
			}
			return hashSet;
		}

		public static string Numbered(int n)
		{
			return "Solution " + n.ToString(CultureInfo.InvariantCulture);
		}

		public static IReadOnlyList<string> Alternatives(string? id)
		{
			if (id != null)
			{
				return id.Split('|');
			}
			return Array.Empty<string>();
		}

		public static string Canonical(string levelId, string solutionId, string? firstRule, string? secondRule)
		{
			if (levelId != "Books (Randomized)" || solutionId != "Draggables_0")
			{
				return solutionId;
			}
			if (IsSymmetric(firstRule))
			{
				return "Shuffle_0";
			}
			if (IsSymmetric(secondRule))
			{
				return "Shuffle_1";
			}
			return solutionId;
		}

		private static bool IsSymmetric(string? rule)
		{
			if (rule != null)
			{
				return rule.IndexOf("SYMMETRIC", StringComparison.Ordinal) >= 0;
			}
			return false;
		}

		private static string Primary(string id)
		{
			return Alternatives(id)[0];
		}

		private static int IndexOf(string id, int position)
		{
			int num = id.LastIndexOf('_');
			if (num <= 0 || !int.TryParse(id.Substring(num + 1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 0)
			{
				return position + 1;
			}
			return result + 1;
		}

		private static string Prefix(string id)
		{
			int num = id.LastIndexOf('_');
			if (num <= 0)
			{
				return id;
			}
			string text = id.Substring(num + 1);
			if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _))
			{
				return text;
			}
			return id.Substring(0, num);
		}

		private static string Readable(string prefix)
		{
			string text = string.Join(" - ", from part in prefix.Split(new string[1] { "---" }, StringSplitOptions.None)
				select part.Replace('-', ' '));
			string text2 = PartNames.Tidy(text);
			if (!(text2 == text))
			{
				return text2;
			}
			return SpaceCamel(text);
		}

		private static string SpaceCamel(string text)
		{
			List<char> list = new List<char>();
			for (int i = 0; i < text.Length; i++)
			{
				if (i > 0 && char.IsUpper(text[i]) && char.IsLower(text[i - 1]))
				{
					list.Add(' ');
				}
				list.Add(text[i]);
			}
			return new string(list.ToArray());
		}
	}
	public sealed class GoalLatch
	{
		private bool _announced;

		private bool _reported;

		private bool _played;

		public bool Played => _played;

		public bool Reported => _reported;

		public bool Announced => _announced;

		public GoalLatch(bool creditsAlreadyPlayed = false)
		{
			_played = creditsAlreadyPlayed;
		}

		public bool ShouldAnnounce(int remaining, bool hasCreditsItem)
		{
			if (_announced || _played)
			{
				return false;
			}
			if (!IsWon(remaining, hasCreditsItem))
			{
				return false;
			}
			_announced = true;
			return true;
		}

		public void CreditsPlayed()
		{
			_played = true;
		}

		public bool ShouldReport(int remaining, bool hasCreditsItem)
		{
			if (!_reported && IsWon(remaining, hasCreditsItem))
			{
				return _played;
			}
			return false;
		}

		public void Sent()
		{
			_reported = true;
		}

		private static bool IsWon(int remaining, bool hasCreditsItem)
		{
			return remaining <= 0 && hasCreditsItem;
		}
	}
	public sealed class InventoryCounts
	{
		public int Packs { get; }

		public int Skips { get; }

		public int Traps { get; }

		public int Beaten { get; }

		public int HintPages { get; }

		public int BackgroundTraps { get; }

		public int BackgroundResets { get; }

		public bool HasCredits { get; }

		public IReadOnlyList<string> Abilities { get; }

		private InventoryCounts(int packs, int skips, int traps, int beaten, int hintPages, int backgrounds, int resets, bool credits, IReadOnlyList<string> abilities)
		{
			BackgroundTraps = backgrounds;
			BackgroundResets = resets;
			Packs = packs;
			Skips = skips;
			Traps = traps;
			Beaten = beaten;
			HintPages = hintPages;
			HasCredits = credits;
			Abilities = abilities;
		}

		public static InventoryCounts From(IEnumerable<string> received, Func<string, bool>? isAbility = null)
		{
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			int num7 = 0;
			bool credits = false;
			List<string> list = new List<string>();
			foreach (string item in received)
			{
				if (string.IsNullOrEmpty(item))
				{
					continue;
				}
				switch (item)
				{
				case "Progressive Puzzle Pack":
					num++;
					continue;
				case "Credits":
					credits = true;
					continue;
				case "Skip":
					num2++;
					continue;
				case "Cat Trap":
					num3++;
					continue;
				case "Level Beaten":
					num4++;
					continue;
				case "Hint Page":
					num5++;
					continue;
				case "Background Change Trap":
					num6++;
					continue;
				case "Background Reset Token":
					num7++;
					continue;
				}
				if (isAbility != null && isAbility(item))
				{
					list.Add(item);
				}
			}
			return new InventoryCounts(num, num2, num3, num4, num5, num6, num7, credits, list);
		}
	}
	public static class ItemNames
	{
		public const string Pack = "Progressive Puzzle Pack";

		public const string Credits = "Credits";

		public const string Skip = "Skip";

		public const string CatTrap = "Cat Trap";

		public const string HintPage = "Hint Page";

		public const string BackgroundTrap = "Background Change Trap";

		public const string BackgroundReset = "Background Reset Token";

		public const string BeatenToken = "Level Beaten";
	}
	public static class LaunchRequest
	{
		public static bool AsksForTheRunningLevel(int askedIndex, bool askedReload, bool clickArmed, int runningIndex, bool runningLoaded)
		{
			if (!askedReload && !clickArmed && runningLoaded && runningIndex > 0)
			{
				if (askedIndex > 0)
				{
					return askedIndex == runningIndex;
				}
				return true;
			}
			return false;
		}

		public static int FallbackSeed(int levelIndex)
		{
			int num = (levelIndex * -1640531535) & 0x7FFFFFFF;
			if (num != 0)
			{
				return num;
			}
			return 1;
		}
	}
	public sealed class ControllerInfo
	{
		[JsonPropertyName("name")]
		public string Name { get; set; } = "";

		[JsonPropertyName("type")]
		public string Type { get; set; } = "";

		[JsonPropertyName("objects")]
		public int Objects { get; set; }

		[JsonPropertyName("dependsOn")]
		public List<string> DependsOn { get; set; } = new List<string>();

		[JsonPropertyName("notALocation")]
		public bool NotALocation { get; set; }
	}
	public sealed class DrawerInfo
	{
		[JsonPropertyName("name")]
		public string Name { get; set; } = "";

		[JsonPropertyName("contains")]
		public int Contains { get; set; }

		[JsonPropertyName("containsControllers")]
		public List<string> ContainsControllers { get; set; } = new List<string>();

		[JsonPropertyName("unlockOn")]
		public List<string> UnlockOn { get; set; } = new List<string>();

		[JsonPropertyName("openOn")]
		public List<string> OpenOn { get; set; } = new List<string>();

		[JsonPropertyName("subDrawers")]
		public int SubDrawers { get; set; }
	}
	public sealed class LevelInfo
	{
		[JsonPropertyName("levelIndex")]
		public int LevelIndex { get; set; }

		[JsonPropertyName("levelId")]
		public string LevelId { get; set; } = "";

		[JsonPropertyName("source")]
		public string Source { get; set; } = "";

		[JsonPropertyName("dlc")]
		public string Dlc { get; set; } = "";

		[JsonPropertyName("solutionCount")]
		public int SolutionCount { get; set; }

		[JsonPropertyName("isRandomizable")]
		public bool IsRandomizable { get; set; }

		[JsonPropertyName("isArchived")]
		public bool IsArchived { get; set; }

		[JsonPropertyName("isDailyTidy")]
		public bool IsDailyTidy { get; set; }

		[JsonPropertyName("isHolidayDaily")]
		public bool IsHolidayDaily { get; set; }

		[JsonIgnore]
		public bool InDailyPool
		{
			get
			{
				if (!IsDailyTidy)
				{
					return IsHolidayDaily;
				}
				return true;
			}
		}

		[JsonIgnore]
		public bool Repeatable
		{
			get
			{
				if (Source == "generator")
				{
					return !LevelTable.FixedLayout.Contains(LevelId);
				}
				return false;
			}
		}

		[JsonPropertyName("levelClass")]
		public string LevelClass { get; set; } = "Level";

		[JsonPropertyName("phases")]
		public List<string> Phases { get; set; } = new List<string>();

		[JsonPropertyName("controllers")]
		public List<ControllerInfo> Controllers { get; set; } = new List<ControllerInfo>();

		[JsonPropertyName("drawers")]
		public List<DrawerInfo> Drawers { get; set; } = new List<DrawerInfo>();

		[JsonPropertyName("extraAbilities")]
		public List<string> ExtraAbilities { get; set; } = new List<string>();

		[JsonPropertyName("endings")]
		public List<string?>? Endings { get; set; }

		[JsonPropertyName("mergedParts")]
		public Dictionary<string, List<string>> MergedParts { get; set; } = new Dictionary<string, List<string>>();

		[JsonPropertyName("solutionOnlyParts")]
		public List<string> SolutionOnlyParts { get; set; } = new List<string>();
	}
	public sealed class LevelTable
	{
		public static readonly IReadOnlySet<string> FixedLayout = new HashSet<string>(StringComparer.Ordinal) { "DLC1 Trophy Cabinet", "DLC2 Water Glasses", "DLC2 Figurines", "DLC2 Bread Crusts" };

		[JsonPropertyName("gameVersion")]
		public string GameVersion { get; set; } = "";

		[JsonPropertyName("sweepSeed")]
		public int SweepSeed { get; set; }

		[JsonPropertyName("levels")]
		public List<LevelInfo> Levels { get; set; } = new List<LevelInfo>();

		public static LevelTable FromJson(string json)
		{
			return JsonSerializer.Deserialize<LevelTable>(json) ?? new LevelTable();
		}

		public LevelInfo? ByIndex(int levelIndex)
		{
			return Levels.FirstOrDefault((LevelInfo l) => l.LevelIndex == levelIndex);
		}

		public LevelInfo? ById(string levelId)
		{
			return Levels.FirstOrDefault((LevelInfo l) => l.LevelId == levelId);
		}
	}
	public static class LocationNames
	{
		public const int MaxGeneratorInstances = 8;

		public const string Credits = "Credits";

		public static string Instance(string levelId, int instance)
		{
			string text = DisplayNames.For(levelId);
			if (instance > 1)
			{
				return text + " #" + instance.ToString(CultureInfo.InvariantCulture);
			}
			return text;
		}

		public static string Solution(string levelId, int instance, int solutionNumber)
		{
			return Instance(levelId, instance) + " - Solution " + solutionNumber.ToString(CultureInfo.InvariantCulture);
		}

		public static string Part(string levelId, int instance, string groupName)
		{
			return Instance(levelId, instance) + " - " + groupName;
		}

		public static string Beaten(string levelId, int instance)
		{
			return Instance(levelId, instance) + " - Beaten";
		}

		public static string Achievement(string levelId, int instance, string display)
		{
			return Instance(levelId, instance) + " - Achievement: " + display;
		}

		public static IReadOnlyList<string> ForInstance(LevelInfo level, int instance)
		{
			List<string> list = new List<string>();
			foreach (EndingInfo item in Endings.For(level))
			{
				list.Add(Ending(level.LevelId, instance, item.Suffix));
			}
			IReadOnlyList<ControllerGroup> readOnlyList = ControllerGroups.For(level);
			if (readOnlyList.Count > 1)
			{
				IReadOnlySet<string> readOnlySet = Endings.EndingGroups(level);
				foreach (ControllerGroup item2 in readOnlyList)
				{
					if (!readOnlySet.Contains(item2.Name) && !level.SolutionOnlyParts.Contains(item2.DisplayName))
					{
						list.Add(Part(level.LevelId, instance, item2.DisplayName));
					}
				}
			}
			return list;
		}

		public static string Ending(string levelId, int instance, string suffix)
		{
			return Instance(levelId, instance) + " - " + suffix;
		}

		public static IReadOnlyList<string> AllPossible(LevelTable table)
		{
			List<string> list = new List<string>();
			foreach (LevelInfo item in table.Levels.OrderBy((LevelInfo l) => l.LevelIndex))
			{
				int num = ((!item.Repeatable) ? 1 : 8);
				for (int num2 = 1; num2 <= num; num2++)
				{
					list.AddRange(ForInstance(item, num2));
					list.Add(Beaten(item.LevelId, num2));
				}
			}
			list.Add("Credits");
			foreach (LevelInfo item2 in table.Levels.OrderBy((LevelInfo l) => l.LevelIndex))
			{
				IReadOnlyList<AchievementChecks.Entry> readOnlyList = AchievementChecks.For(item2.LevelId);
				if (readOnlyList.Count == 0)
				{
					continue;
				}
				int num3 = ((!item2.Repeatable) ? 1 : 8);
				for (int num4 = 1; num4 <= num3; num4++)
				{
					foreach (AchievementChecks.Entry item3 in readOnlyList)
					{
						list.Add(Achievement(item2.LevelId, num4, item3.Display));
					}
				}
			}
			return list;
		}
	}
	public sealed class ObjectLock
	{
		public static readonly IReadOnlySet<string> Enclosures = new HashSet<string>(StringComparer.Ordinal) { "DrawerController", "DrawerExpandableController", "Cupboard" };

		public static readonly IReadOnlySet<string> DoorLevels = new HashSet<string>(StringComparer.Ordinal) { "DLC1 Clock Cupboard", "DLC1 Tea Cabinet", "DLC1 Trophy Cabinet" };

		public static readonly IReadOnlySet<string> ScrubPuzzleLevels = new HashSet<string>(StringComparer.Ordinal) { "Wilting Flowers" };

		public static readonly IReadOnlySet<string> ResetOnUnlockLevels = new HashSet<string>(StringComparer.Ordinal) { "Cat Food Cans", "Boxes (Stacked)", "GoodTidings_Presents (Stacked)", "Seed Pods", "Clover" };

		public static readonly IReadOnlySet<string> IndexHoldLevels = new HashSet<string>(StringComparer.Ordinal) { "DLC2 Robots", "DLC2 Math Set" };

		public static readonly IReadOnlyDictionary<string, IReadOnlyList<string>> IndexHoldArt = new Dictionary<string, IReadOnlyList<string>>(StringComparer.Ordinal) { ["DLC2 Robots"] = new string[1] { "Robot3 Chest and Arms" } };

		public static readonly IReadOnlyDictionary<(string Level, string Controller), string> LockedAs = new Dictionary<(string, string), string> { [("Books (Randomized)", "Draggables")] = "Shuffleables" };

		private readonly string _levelId;

		private bool _heldByGroup;

		private bool _groupUnlocked;

		private bool _enclosureUnlocked;

		private bool _heldByCover;

		private bool _heldByOther;

		private bool _heldByDrawerSet;

		private bool _drawerSetLocked;

		private static readonly IReadOnlySet<string> DrawerSets = new HashSet<string>(StringComparer.Ordinal) { "DrawerController", "DrawerExpandableController" };

		public bool IsDrawerSetPart
		{
			get
			{
				if (IsCover)
				{
					return _heldByDrawerSet;
				}
				return false;
			}
		}

		public bool DrawerSetLocked => _drawerSetLocked;

		public bool HeldByDrawerSet => _heldByDrawerSet;

		public bool IsCover
		{
			get
			{
				if (_heldByCover)
				{
					return !_heldByOther;
				}
				return false;
			}
		}

		public bool Unlocked
		{
			get
			{
				if (!_heldByGroup)
				{
					return _enclosureUnlocked;
				}
				return _groupUnlocked;
			}
		}

		public static bool IsCoverClass(string controllerClass, string levelId)
		{
			if (Enclosures.Contains(controllerClass ?? ""))
			{
				return true;
			}
			if (controllerClass == "AnimScrubbables")
			{
				return DoorLevels.Contains(levelId ?? "");
			}
			return false;
		}

		public static bool HoldsIndexes(string levelId, bool indexablesLocked)
		{
			if (indexablesLocked)
			{
				return IndexHoldLevels.Contains(levelId ?? "");
			}
			return false;
		}

		public static bool ResetOnUnlock(string levelId, int lockedBefore, int lockedAfter)
		{
			if (ResetOnUnlockLevels.Contains(levelId ?? "") && lockedBefore > 0)
			{
				return lockedAfter < lockedBefore;
			}
			return false;
		}

		public static string LockClass(string levelId, string controllerName, string controllerClass)
		{
			if (!LockedAs.TryGetValue((levelId ?? "", controllerName ?? ""), out string value))
			{
				return controllerClass;
			}
			return value;
		}

		public ObjectLock(string levelId = "")
		{
			_levelId = levelId ?? "";
		}

		public void Add(string controllerClass, bool isLocked)
		{
			if (IsCoverClass(controllerClass, _levelId))
			{
				_heldByCover = true;
			}
			else
			{
				_heldByOther = true;
			}
			if (DrawerSets.Contains(controllerClass ?? ""))
			{
				_heldByDrawerSet = true;
				_drawerSetLocked |= isLocked;
			}
			if (Enclosures.Contains(controllerClass ?? ""))
			{
				_enclosureUnlocked |= !isLocked;
				return;
			}
			_heldByGroup = true;
			_groupUnlocked |= !isLocked;
		}
	}
	public static class PartNames
	{
		private static readonly string[] TypeWords = new string[16]
		{
			"DraggablesOrdered", "DraggablesStacked", "DraggablesJigsaw", "SymmetricalPlaceables", "ShuffleablesRelative", "Draggables", "Draggable", "Containables", "Shuffleables", "Stackables",
			"Removables", "Pluckables", "Rotateables", "Placeables", "Jigsaw", "Controller"
		};

		private static readonly Regex CamelBoundary = new Regex("(?<=[a-z])(?=[A-Z])", RegexOptions.Compiled);

		private static readonly Regex EmptyBrackets = new Regex("\\s*\\(\\s*\\)", RegexOptions.Compiled);

		public static IReadOnlyDictionary<string, string> ForLevel(LevelInfo level)
		{
			Dictionary<string, string> dictionary = ((IEnumerable<string>)level.Controllers.Select((ControllerInfo c) => c.Name).Distinct<string>(StringComparer.Ordinal).ToList()).ToDictionary((Func<string, string>)((string n) => n), (Func<string, string>)Tidy, (IEqualityComparer<string>?)StringComparer.Ordinal);
			foreach (string item in (from g in dictionary.GroupBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> kv) => kv.Value, StringComparer.Ordinal)
				where g.Count() > 1
				select g).SelectMany((IGrouping<string, KeyValuePair<string, string>> g) => g.Select((KeyValuePair<string, string> kv) => kv.Key)).ToHashSet<string>(StringComparer.Ordinal))
			{
				dictionary[item] = item;
			}
			return dictionary;
		}

		public static string Tidy(string name)
		{
			string input = name;
			string[] typeWords = TypeWords;
			foreach (string text in typeWords)
			{
				input = Regex.Replace(input, "[\\s_-]*" + text + "\\b", "", RegexOptions.IgnoreCase);
			}
			input = EmptyBrackets.Replace(input, "");
			input = CamelBoundary.Replace(input, " ");
			input = string.Join(" ", input.Split(' ', StringSplitOptions.RemoveEmptyEntries)).Trim(' ', '-', '_');
			if (input.Length != 0)
			{
				return input;
			}
			return name;
		}
	}
	public static class Refusals
	{
		public static bool IsTerminal(IEnumerable<ConnectionRefusedError>? codes)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			if (codes == null)
			{
				return false;
			}
			foreach (ConnectionRefusedError code in codes)
			{
				if (code - 1 <= 1 || code - 4 <= 2)
				{
					return true;
				}
			}
			return false;
		}
	}
	public sealed class RetryPolicy
	{
		public const int Unlimited = 0;

		public const int DefaultMaxAttempts = 0;

		private readonly int _maxAttempts;

		private readonly double _firstDelaySeconds;

		private readonly double _maxDelaySeconds;

		public int Attempts { get; private set; }

		public string LastError { get; private set; } = "";

		public bool GaveUp
		{
			get
			{
				if (_maxAttempts != 0)
				{
					return Attempts >= _maxAttempts;
				}
				return false;
			}
		}

		public bool IsUnlimited => _maxAttempts == 0;

		public int MaxAttempts => _maxAttempts;

		public RetryPolicy(int maxAttempts = 0, double firstDelaySeconds = 5.0, double maxDelaySeconds = 60.0)
		{
			_maxAttempts = ((maxAttempts >= 0) ? maxAttempts : 0);
			_firstDelaySeconds = ((firstDelaySeconds <= 0.0) ? 1.0 : firstDelaySeconds);
			_maxDelaySeconds = ((maxDelaySeconds < _firstDelaySeconds) ? _firstDelaySeconds : maxDelaySeconds);
		}

		public double? Failed(string error)
		{
			LastError = error ?? "";
			Attempts++;
			if (!GaveUp)
			{
				return DelayForAttempt(Attempts);
			}
			return null;
		}

		public double DelayForAttempt(int attempt)
		{
			if (attempt < 1)
			{
				attempt = 1;
			}
			double num = _firstDelaySeconds;
			for (int i = 1; i < attempt; i++)
			{
				if (!(num < _maxDelaySeconds))
				{
					break;
				}
				num *= 2.0;
			}
			if (!(num > _maxDelaySeconds))
			{
				return num;
			}
			return _maxDelaySeconds;
		}

		public void Reset()
		{
			Attempts = 0;
			LastError = "";
		}

		public string Describe(bool connected, string slotName)
		{
			if (connected)
			{
				return "Connected as " + slotName;
			}
			if (Attempts == 0)
			{
				return "Not connected";
			}
			if (GaveUp)
			{
				if (LastError.Length > 0)
				{
					return $"Gave up after {Attempts} attempts: {LastError}";
				}
				return $"Gave up after {Attempts} attempts";
			}
			if (IsUnlimited)
			{
				return $"Reconnecting, attempt {Attempts + 1}";
			}
			return $"Reconnecting, attempt {Attempts + 1} of {_maxAttempts}";
		}
	}
	public sealed class RunStateData
	{
		public static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
		{
			ReadCommentHandling = JsonCommentHandling.Skip,
			AllowTrailingCommas = true,
			WriteIndented = false
		};

		[JsonPropertyName("owed")]
		public List<string> Owed { get; set; } = new List<string>();

		[JsonPropertyName("skipsUsed")]
		public int SkipsUsed { get; set; }

		[JsonPropertyName("trapsSprung")]
		public int TrapsSprung { get; set; }

		[JsonPropertyName("beaten")]
		public List<string> Beaten { get; set; } = new List<string>();

		[JsonPropertyName("hintPages")]
		public List<string> HintPages { get; set; } = new List<string>();

		[JsonPropertyName("creditsPlayed")]
		public bool CreditsPlayed { get; set; }

		[JsonPropertyName("withheld")]
		public List<string> Withheld { get; set; } = new List<string>();

		[JsonPropertyName("withheldNeeds")]
		public Dictionary<string, List<string>> WithheldNeeds { get; set; } = new Dictionary<string, List<string>>();

		[JsonPropertyName("backgroundResetsUsed")]
		public int BackgroundResetsUsed { get; set; }

		[JsonPropertyName("backgroundResetAt")]
		public int BackgroundResetAt { get; set; }

		[JsonIgnore]
		public int HintPagesOpened => HintPages.Count;

		[JsonIgnore]
		public bool HasProgress
		{
			get
			{
				if (Owed.Count <= 0 && SkipsUsed <= 0 && TrapsSprung <= 0 && Beaten.Count <= 0 && HintPages.Count <= 0 && Withheld.Count <= 0)
				{
					return BackgroundResetsUsed > 0;
				}
				return true;
			}
		}

		public string ToJson()
		{
			return JsonSerializer.Serialize(this, JsonOptions);
		}

		public static RunStateData? FromJson(string json)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				return null;
			}
			try
			{
				return JsonSerializer.Deserialize<RunStateData>(json, JsonOptions);
			}
			catch (JsonException)
			{
				return null;
			}
		}

		public string Summary()
		{
			return $"{Owed.Count} check(s) still owed, {SkipsUsed} skip(s) used, {TrapsSprung} trap(s) already sprung, {HintPages.Count} hint page(s) opened, {Beaten.Count} puzzle(s) beaten" + ((Withheld.Count > 0) ? $", {Withheld.Count} part check(s) waiting on an ability" : "") + ((BackgroundResetsUsed > 0) ? $", {BackgroundResetsUsed} background reset(s) used" : "");
		}

		public bool NoteCreditsPlayed()
		{
			if (CreditsPlayed)
			{
				return false;
			}
			CreditsPlayed = true;
			return true;
		}

		public bool IsHintPageOpen(string key)
		{
			return HintPages.Contains(key);
		}

		public bool OpenHintPage(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return false;
			}
			if (HintPages.Contains(key))
			{
				return false;
			}
			HintPages.Add(key);
			return true;
		}

		public bool SetOwed(IReadOnlyList<string> owed)
		{
			if (Same(Owed, owed))
			{
				return false;
			}
			Owed = new List<string>(owed);
			return true;
		}

		public bool SetBeaten(IReadOnlyList<string> beaten)
		{
			if (Same(Beaten, beaten))
			{
				return false;
			}
			Beaten = new List<string>(beaten);
			return true;
		}

		public bool AddWithheld(string location, IReadOnlyList<string>? needs = null)
		{
			if (string.IsNullOrEmpty(location) || Withheld.Contains(location))
			{
				return false;
			}
			Withheld.Add(location);
			if (needs != null && needs.Count > 0)
			{
				WithheldNeeds[location] = new List<string>(needs);
			}
			return true;
		}

		public IReadOnlyList<string> NeedsFor(string location)
		{
			if (!WithheldNeeds.TryGetValue(location, out List<string> value))
			{
				return Array.Empty<string>();
			}
			return value;
		}

		public bool RemoveWithheld(string location)
		{
			WithheldNeeds.Remove(location);
			return Withheld.Remove(location);
		}

		public bool SpendSkip()
		{
			SkipsUsed++;
			return true;
		}

		public bool SpendBackgroundReset(int trapsNow)
		{
			if (trapsNow <= BackgroundResetAt)
			{
				return false;
			}
			BackgroundResetsUsed++;
			BackgroundResetAt = trapsNow;
			return true;
		}

		public bool SpendTrap(int count)
		{
			if (count <= 0)
			{
				return false;
			}
			TrapsSprung += count;
			return true;
		}

		private static bool Same(List<string> a, IReadOnlyList<string> b)
		{
			if (a.Count != b.Count)
			{
				return false;
			}
			for (int i = 0; i < a.Count; i++)
			{
				if (!string.Equals(a[i], b[i], StringComparison.Ordinal))
				{
					return false;
				}
			}
			return true;
		}
	}
	public static class SaveNames
	{
		public const string Vanilla = "save1";

		public const string Prefix = "save_ap_";

		public const string Folder = "Archipelago";

		public const string SessionCache = "alttl-last-session.json";

		private const int MaxPartLength = 24;

		public static bool IsLegacyFile(string? fileName)
		{
			if (fileName != null)
			{
				if (!(fileName == "alttl-last-session.json"))
				{
					if (fileName.StartsWith("save_ap_", StringComparison.Ordinal))
					{
						return fileName.EndsWith(".json", StringComparison.Ordinal);
					}
					return false;
				}
				return true;
			}
			return false;
		}

		public static string ForSession(string? slotName, string? seed)
		{
			string text = Sanitise(slotName, "player");
			string text2 = Sanitise(seed, "seed");
			return "save_ap_" + text + "_" + text2;
		}

		public static bool IsArchipelago(string? name)
		{
			return name?.StartsWith("save_ap_", StringComparison.Ordinal) ?? false;
		}

		private static string Sanitise(string? raw, string fallback)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return fallback;
			}
			StringBuilder stringBuilder = new StringBuilder(raw.Length);
			foreach (char c in raw)
			{
				if (char.IsLetterOrDigit(c) && c < '\u0080')
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
				else if (c == '-' || c == '_')
				{
					stringBuilder.Append(c);
				}
				else if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '-')
				{
					stringBuilder.Append('-');
				}
			}
			string text = stringBuilder.ToString().Trim('-');
			if (text.Length == 0)
			{
				return fallback + "-" + ShortHash(raw);
			}
			if (text.Length > 24)
			{
				text = text.Substring(0, 19).TrimEnd('-') + "-" + ShortHash(raw);
			}
			return text;
		}

		private static string ShortHash(string value)
		{
			uint num = 2166136261u;
			foreach (char c in value)
			{
				num = (num ^ c) * 16777619;
			}
			return (num % 65535).ToString("x4", CultureInfo.InvariantCulture);
		}
	}
	public sealed class SessionGate<TItem>
	{
		private readonly Action<Action> _dispatch;

		private readonly object _lock = new object();

		private List<TItem> _items = new List<TItem>();

		private List<string> _lines = new List<string>();

		private bool _open;

		public bool IsOpen
		{
			get
			{
				lock (_lock)
				{
					return _open;
				}
			}
		}

		public SessionGate(Action<Action> dispatch)
		{
			_dispatch = dispatch;
		}

		public void Items(IReadOnlyList<TItem> items, Action<TItem> deliver)
		{
			if (items.Count == 0)
			{
				return;
			}
			lock (_lock)
			{
				if (!_open)
				{
					_items.AddRange(items);
					return;
				}
				List<TItem> batch = items.ToList();
				_dispatch(delegate
				{
					foreach (TItem item in batch)
					{
						deliver(item);
					}
				});
			}
		}

		public void Line(string line, Action<string> deliver)
		{
			lock (_lock)
			{
				if (!_open)
				{
					_lines.Add(line);
					return;
				}
				_dispatch(delegate
				{
					deliver(line);
				});
			}
		}

		public void Locations(IReadOnlyList<string> names, Action<IReadOnlyList<string>> deliver)
		{
			if (names.Count == 0)
			{
				return;
			}
			lock (_lock)
			{
				if (_open)
				{
					List<string> batch = names.ToList();
					_dispatch(delegate
					{
						deliver(batch);
					});
				}
			}
		}

		public void Open(Action<TItem> deliverItem, Action ready, Action<string> deliverLine)
		{
			lock (_lock)
			{
				if (_open)
				{
					return;
				}
				_open = true;
				List<TItem> items = _items;
				List<string> lines = _lines;
				_items = new List<TItem>();
				_lines = new List<string>();
				_dispatch(delegate
				{
					foreach (TItem item in items)
					{
						deliverItem(item);
					}
					ready();
					foreach (string item2 in lines)
					{
						deliverLine(item2);
					}
				});
			}
		}
	}
	public static class SettingsSplice
	{
		public static string? Replace(string document, string key, string replacement)
		{
			if (string.IsNullOrEmpty(document))
			{
				return null;
			}
			if (string.IsNullOrEmpty(key))
			{
				return null;
			}
			if (string.IsNullOrEmpty(replacement))
			{
				return null;
			}
			(int, int)? tuple = Find(document, key);
			if (!tuple.HasValue)
			{
				return null;
			}
			var (length, startIndex) = tuple.Value;
			return document.Substring(0, length) + replacement + document.Substring(startIndex);
		}

		internal static (int Start, int End)? Find(string document, string key)
		{
			string text = "\"" + key + "\"";
			int num = -1;
			for (int num2 = document.IndexOf(text, StringComparison.Ordinal); num2 >= 0; num2 = document.IndexOf(text, num2 + 1, StringComparison.Ordinal))
			{
				int num3 = SkipSpace(document, num2 + text.Length);
				if (num3 < document.Length && document[num3] == ':')
				{
					int num4 = BackSkipSpace(document, num2 - 1);
					if (num4 < 0 || document[num4] == '{' || document[num4] == ',')
					{
						if (num >= 0)
						{
							return null;
						}
						num = num3 + 1;
					}
				}
			}
			if (num < 0)
			{
				return null;
			}
			int num5 = SkipSpace(document, num);
			if (num5 >= document.Length || document[num5] != '{')
			{
				return null;
			}
			int num6 = EndOfObject(document, num5);
			if (num6 >= 0)
			{
				return (num5, num6);
			}
			return null;
		}

		internal static int EndOfObject(string document, int start)
		{
			int num = 0;
			bool flag = false;
			bool flag2 = false;
			for (int i = start; i < document.Length; i++)
			{
				char c = document[i];
				if (flag)
				{
					if (flag2)
					{
						flag2 = false;
						continue;
					}
					switch (c)
					{
					case '\\':
						flag2 = true;
						break;
					case '"':
						flag = false;
						break;
					}
					continue;
				}
				switch (c)
				{
				case '"':
					flag = true;
					break;
				case '{':
					num++;
					break;
				case '}':
					num--;
					if (num == 0)
					{
						return i + 1;
					}
					break;
				}
			}
			return -1;
		}

		private static int SkipSpace(string s, int i)
		{
			while (i < s.Length && char.IsWhiteSpace(s[i]))
			{
				i++;
			}
			return i;
		}

		private static int BackSkipSpace(string s, int i)
		{
			while (i >= 0 && char.IsWhiteSpace(s[i]))
			{
				i--;
			}
			return i;
		}
	}
	public enum SkipRefusal
	{
		None,
		NotARunSlot,
		NoneHeld,
		NothingLeft
	}
	public enum SkipReturn
	{
		Idle,
		Done,
		Fallback,
		StayArmed
	}
	public sealed class SkipFlow
	{
		private int _armedSlot = -1;

		private int _chargedSlot = -1;

		public bool Armed => _armedSlot >= 0;

		public int ArmedSlot => _armedSlot;

		public SkipRefusal Request(int slot, int available, bool workLeft)
		{
			_chargedSlot = -1;
			if (slot < 0)
			{
				return SkipRefusal.NotARunSlot;
			}
			if (available <= 0)
			{
				return SkipRefusal.NoneHeld;
			}
			if (!workLeft)
			{
				return SkipRefusal.NothingLeft;
			}
			_armedSlot = slot;
			return SkipRefusal.None;
		}

		public bool Skipped(int slot)
		{
			if (!Armed || slot != _armedSlot)
			{
				return false;
			}
			_armedSlot = -1;
			_chargedSlot = slot;
			return true;
		}

		public SkipReturn Returned(int slot, bool skippable)
		{
			if (!Armed && slot >= 0 && slot == _chargedSlot)
			{
				_chargedSlot = -1;
				return SkipReturn.Done;
			}
			if (!Armed || slot != _armedSlot)
			{
				return SkipReturn.Idle;
			}
			if (skippable)
			{
				return SkipReturn.StayArmed;
			}
			_armedSlot = -1;
			return SkipReturn.Fallback;
		}

		public void Entered(int slot)
		{
			_armedSlot = -1;
			_chargedSlot = -1;
		}
	}
	public sealed class SlotData
	{
		public static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
		{
			PropertyNameCaseInsensitive = false,
			ReadCommentHandling = JsonCommentHandling.Skip,
			AllowTrailingCommas = true
		};

		[JsonPropertyName("world_version")]
		public string WorldVersion { get; set; } = "";

		[JsonPropertyName("slots")]
		public List<SlotEntry> Slots { get; set; } = new List<SlotEntry>();

		[JsonPropertyName("pack_size")]
		public int PackSize { get; set; } = 5;

		[JsonPropertyName("pack_total")]
		public int PackTotal { get; set; }

		[JsonPropertyName("pack_boundaries")]
		public List<int> PackBoundaries { get; set; } = new List<int>();

		[JsonPropertyName("goal")]
		public string Goal { get; set; } = "beat_levels";

		[JsonIgnore]
		public bool GoalIsStars => string.Equals(Goal, "collect_stars", StringComparison.Ordinal);

		[JsonPropertyName("levels_to_beat")]
		public int LevelsToBeat { get; set; } = 50;

		[JsonPropertyName("stars_to_collect")]
		public int StarsToCollect { get; set; } = 65;

		[JsonIgnore]
		public int StarsTotal
		{
			get
			{
				int num = 0;
				foreach (string key in Requirements.Keys)
				{
					if (key.Contains(" - Solution", StringComparison.Ordinal))
					{
						num++;
					}
				}
				return num;
			}
		}

		[JsonIgnore]
		public int GoalTarget
		{
			get
			{
				if (!GoalIsStars)
				{
					return LevelsToBeat;
				}
				return StarsToCollect;
			}
		}

		[JsonPropertyName("ability_locks")]
		public bool AbilityLocks { get; set; } = true;

		[JsonPropertyName("abilities")]
		public Dictionary<string, List<string>> Abilities { get; set; } = new Dictionary<string, List<string>>();

		[JsonPropertyName("starting_abilities")]
		public List<string> StartingAbilities { get; set; } = new List<string>();

		[JsonPropertyName("requirements")]
		public Dictionary<string, Requirement> Requirements { get; set; } = new Dictionary<string, Requirement>();

		[JsonPropertyName("controller_groups")]
		public Dictionary<string, Dictionary<string, string>> ControllerGroups { get; set; } = new Dictionary<string, Dictionary<string, string>>();

		[JsonPropertyName("endings")]
		public Dictionary<string, List<EndingEntry>> Endings { get; set; } = new Dictionary<string, List<EndingEntry>>();

		[JsonPropertyName("not_locations")]
		public Dictionary<string, List<string>> NotLocations { get; set; } = new Dictionary<string, List<string>>();

		[JsonPropertyName("cat_trap_chance")]
		public int CatTrapChance { get; set; } = 15;

		[JsonPropertyName("cupboards_and_drawers")]
		public bool CupboardsAndDrawers { get; set; }

		[JsonPropertyName("seeing_stars")]
		public bool SeeingStars { get; set; }

		[JsonPropertyName("credits")]
		public string Credits { get; set; } = "";

		[JsonIgnore]
		public IReadOnlyList<string> RequiredDlc
		{
			get
			{
				List<string> list = new List<string>();
				if (CupboardsAndDrawers)
				{
					list.Add("DLC1");
				}
				if (SeeingStars)
				{
					list.Add("DLC2");
				}
				return list;
			}
		}

		public string Fingerprint()
		{
			uint hash = 2166136261u;
			Feed(Slots.Count.ToString());
			Feed(PackTotal.ToString());
			Feed(LevelsToBeat.ToString());
			foreach (SlotEntry slot in Slots)
			{
				Feed(slot.LevelId);
				Feed(slot.Instance.ToString());
				Feed(slot.Seed.ToString());
			}
			return hash.ToString("x8");
			void Feed(string value)
			{
				foreach (char c in value)
				{
					hash = (hash ^ c) * 16777619;
				}
				hash = (hash ^ 0x7C) * 16777619;
			}
		}

		public static SlotData FromJson(string json)
		{
			return JsonSerializer.Deserialize<SlotData>(json, JsonOptions) ?? new SlotData();
		}

		public string? VersionMismatch(string modVersion)
		{
			if (string.IsNullOrEmpty(WorldVersion))
			{
				return null;
			}
			if (string.IsNullOrEmpty(modVersion))
			{
				return null;
			}
			if (string.Equals(WorldVersion, modVersion, StringComparison.Ordinal))
			{
				return null;
			}
			return $"this seed was generated by apworld {WorldVersion} and this mod is {modVersion}. They disagree about the item table, so the run would send the wrong checks. Install the matching pair - both ship in the same release.";
		}

		public IReadOnlyList<string> Problems()
		{
			List<string> list = new List<string>();
			if (Slots.Count == 0)
			{
				list.Add("no slots");
			}
			if (PackSize < 1)
			{
				list.Add($"pack_size is {PackSize}");
			}
			if (PackBoundaries.Count == 0)
			{
				list.Add("no pack_boundaries - the mod cannot tell which puzzles a pack opens");
			}
			else
			{
				if (PackBoundaries[PackBoundaries.Count - 1] != Slots.Count)
				{
					list.Add($"pack_boundaries end at {PackBoundaries[PackBoundaries.Count - 1]} but the run has {Slots.Count} slots");
				}
				if (PackBoundaries.Count - 1 != PackTotal)
				{
					list.Add($"{PackBoundaries.Count - 1} pack steps but pack_total is {PackTotal}");
				}
			}
			if (LevelsToBeat > Slots.Count)
			{
				list.Add($"levels_to_beat {LevelsToBeat} exceeds {Slots.Count} slots");
			}
			if (StarsToCollect > StarsTotal)
			{
				list.Add($"stars_to_collect {StarsToCollect} exceeds the run's {StarsTotal} stars");
			}
			if (Goal != "beat_levels" && Goal != "collect_stars")
			{
				list.Add("goal '" + Goal + "' is not one this build knows");
			}
			SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.Ordinal);
			foreach (SlotEntry slot in Slots)
			{
				if (!string.IsNullOrEmpty(slot.LevelId) && !ControllerGroups.ContainsKey(slot.LevelId))
				{
					sortedSet.Add(slot.LevelId);
				}
			}
			if (sortedSet.Count > 0)
			{
				list.Add($"{sortedSet.Count} level(s) have no controller_groups entry: " + string.Join(", ", sortedSet.Take(5)));
			}
			for (int i = 0; i < Slots.Count; i++)
			{
				SlotEntry slotEntry = Slots[i];
				if (string.IsNullOrWhiteSpace(slotEntry.LevelId))
				{
					list.Add($"slot {i} has no levelId");
				}
				if (slotEntry.LevelIndex < 0)
				{
					list.Add($"slot {i} has levelIndex {slotEntry.LevelIndex}");
				}
			}
			foreach (var (text2, requirement2) in Requirements)
			{
				if (requirement2.Packs > PackTotal)
				{
					list.Add($"{text2} needs {requirement2.Packs} packs, seed has {PackTotal}");
				}
				foreach (string item in requirement2.Options.SelectMany((List<string> o) => o))
				{
					if (AbilityLocks && !Abilities.ContainsKey(item))
					{
						list.Add(text2 + " needs unknown ability '" + item + "'");
					}
				}
			}
			return list;
		}
	}
	public sealed class EndingEntry
	{
		[JsonPropertyName("id")]
		public string? Id { get; set; }

		[JsonPropertyName("location")]
		public string Location { get; set; } = "";
	}
	public sealed class SlotEntry
	{
		[JsonPropertyName("levelId")]
		public string LevelId { get; set; } = "";

		[JsonPropertyName("levelIndex")]
		public int LevelIndex { get; set; } = -1;

		[JsonPropertyName("instance")]
		public int Instance { get; set; } = 1;

		[JsonPropertyName("source")]
		public string Source { get; set; } = "";

		[JsonPropertyName("dlc")]
		public string Dlc { get; set; } = "";

		[JsonPropertyName("seed")]
		public long Seed { get; set; } = -1L;

		[JsonIgnore]
		public bool IsGenerator => Source == "generator";
	}
	public sealed class Requirement
	{
		[JsonPropertyName("packs")]
		public int Packs { get; set; }

		[JsonPropertyName("abilities")]
		public List<string> Abilities { get; set; } = new List<string>();

		[JsonPropertyName("orAbilities")]
		public List<List<string>> OrAbilities { get; set; } = new List<List<string>>();

		[JsonIgnore]
		public IEnumerable<List<string>> Options => OrAbilities.Prepend<List<string>>(Abilities);
	}
	public sealed class SlotPicker
	{
		private readonly TrackState _track;

		private readonly CheckRouter _router;

		private readonly Func<string, bool> _isCollected;

		private readonly SlotProgress? _progress;

		private readonly AbilityState? _abilities;

		public SlotPicker(TrackState track, CheckRouter router, Func<string, bool> isCollected, SlotProgress? progress, AbilityState? abilities)
		{
			_track = track ?? throw new ArgumentNullException("track");
			_router = router ?? throw new ArgumentNullException("router");
			_isCollected = isCollected ?? throw new ArgumentNullException("isCollected");
			_progress = progress;
			_abilities = abilities;
		}

		public bool HasPlayableWork(int slot)
		{
			if (_progress == null || _abilities == null)
			{
				return true;
			}
			foreach (string item in _router.ForSlot(slot))
			{
				if (!_isCollected(item) && _progress.IsReachable(item, _track.PacksHeld, _abilities))
				{
					return true;
				}
			}
			return false;
		}

		public bool IsPlayable(int slot)
		{
			if (_track.IsOpen(slot))
			{
				return HasPlayableWork(slot);
			}
			return false;
		}

		public int Next(int current)
		{
			int count = _track.Slots.Count;
			int num = ((current >= 0) ? (current + 1) : 0);
			for (int i = 0; i < count; i++)
			{
				int num2 = (num + i) % count;
				if (IsPlayable(num2))
				{
					return num2;
				}
			}
			return -1;
		}

		public int First()
		{
			for (int i = 0; i < _track.Slots.Count; i++)
			{
				if (IsPlayable(i))
				{
					return i;
				}
			}
			return -1;
		}

		public int Farthest()
		{
			for (int num = _track.Slots.Count - 1; num >= 0; num--)
			{
				if (IsPlayable(num))
				{
					return num;
				}
			}
			return -1;
		}

		public bool AnyPlayableBesides(int except)
		{
			for (int i = 0; i < _track.Slots.Count; i++)
			{
				if (i != except && IsPlayable(i))
				{
					return true;
				}
			}
			return false;
		}
	}
	public enum SlotStatus
	{
		Doable,
		Mixed,
		Locked,
		Beaten,
		Complete
	}
	public sealed class SlotProgress
	{
		private readonly SlotData _slot;

		private readonly CheckRouter _router;

		public SlotProgress(SlotData slot, CheckRouter router)
		{
			_slot = slot ?? throw new ArgumentNullException("slot");
			_router = router ?? throw new ArgumentNullException("router");
		}

		public SlotStatus StatusOf(int slotIndex, Func<string, bool> isCollected, int packsHeld, AbilityState abilities)
		{
			IReadOnlyList<string> readOnlyList = _router.ForSlot(slotIndex);
			int num = 0;
			int num2 = 0;
			foreach (string item in readOnlyList)
			{
				if (!isCollected(item))
				{
					if (IsReachable(item, packsHeld, abilities))
					{
						num++;
					}
					else
					{
						num2++;
					}
				}
			}
			if (num == 0 && num2 == 0)
			{
				return SlotStatus.Complete;
			}
			if (num2 == 0)
			{
				return SlotStatus.Doable;
			}
			if (num == 0)
			{
				string text = _router.ForBeaten(slotIndex);
				if (text != null && isCollected(text))
				{
					return SlotStatus.Beaten;
				}
				return SlotStatus.Locked;
			}
			return SlotStatus.Mixed;
		}

		public bool IsReachable(string name, int packsHeld, AbilityState abilities)
		{
			if (!_slot.Requirements.TryGetValue(name, out Requirement value))
			{
				return true;
			}
			if (packsHeld < value.Packs)
			{
				return false;
			}
			if (!abilities.LocksEnabled)
			{
				return true;
			}
			return value.Options.Any(abilities.HasAll);
		}
	}
	public sealed class SolutionOrdinals
	{
		private readonly Dictionary<int, List<string>> _found = new Dictionary<int, List<string>>();

		public int Record(int slot, string solutionId)
		{
			if (slot < 0)
			{
				return 0;
			}
			if (!_found.TryGetValue(slot, out List<string> value))
			{
				value = new List<string>();
				_found[slot] = value;
			}
			string item = solutionId ?? "";
			if (value.Contains(item))
			{
				return 0;
			}
			value.Add(item);
			return value.Count;
		}

		public int Peek(int slot, string solutionId)
		{
			if (slot < 0)
			{
				return 0;
			}
			if (!_found.TryGetValue(slot, out List<string> value))
			{
				return 1;
			}
			if (!value.Contains(solutionId ?? ""))
			{
				return value.Count + 1;
			}
			return 0;
		}

		public int Seed(int slot, IEnumerable<string> solutionIds)
		{
			if (slot < 0 || solutionIds == null)
			{
				return 0;
			}
			if (!_found.TryGetValue(slot, out List<string> value))
			{
				value = new List<string>();
				_found[slot] = value;
			}
			int num = 0;
			foreach (string solutionId in solutionIds)
			{
				string item = solutionId ?? "";
				if (!value.Contains(item))
				{
					value.Add(item);
					num++;
				}
			}
			return num;
		}

		public string? IdOf(int slot, int n)
		{
			if (!_found.TryGetValue(slot, out List<string> value) || n < 1 || n > value.Count)
			{
				return null;
			}
			return value[n - 1];
		}

		public IReadOnlyList<string> IdsFor(int slot)
		{
			if (!_found.TryGetValue(slot, out List<string> value))
			{
				return new List<string>();
			}
			return value.ToList();
		}

		public int CountFor(int slot)
		{
			if (!_found.TryGetValue(slot, out List<string> value))
			{
				return 0;
			}
			return value.Count;
		}

		public void Clear()
		{
			_found.Clear();
		}
	}
	public static class SolutionParts
	{
		public static IReadOnlyList<string> ControllersFor(string? solutionId, IEnumerable<string> controllerNames)
		{
			List<string> list = new List<string>();
			string text = Normalise(Prefix(solutionId));
			if (text.Length == 0 || controllerNames == null)
			{
				return list;
			}
			foreach (string controllerName in controllerNames)
			{
				if (controllerName != null && Normalise(controllerName) == text)
				{
					list.Add(controllerName);
				}
			}
			return list;
		}

		private static string Prefix(string? solutionId)
		{
			string text = solutionId ?? "";
			int num = text.LastIndexOf('_');
			if (num <= 0 || num == text.Length - 1)
			{
				return text;
			}
			for (int i = num + 1; i < text.Length; i++)
			{
				if (!char.IsDigit(text[i]))
				{
					return text;
				}
			}
			return text.Substring(0, num);
		}

		private static string Normalise(string text)
		{
			List<char> list = new List<char>(text.Length);
			foreach (char c in text)
			{
				if (char.IsLetterOrDigit(c))
				{
					list.Add(char.ToLowerInvariant(c));
				}
			}
			return new string(list.ToArray());
		}
	}
	public sealed class TrackState
	{
		private readonly IReadOnlyList<int> _boundaries;

		public IReadOnlyList<SlotEntry> Slots { get; }

		public int PacksHeld { get; private set; }

		public int PackTotal => Math.Max(0, _boundaries.Count - 1);

		public IReadOnlyList<int> Boundaries => _boundaries;

		public int OpenSlots
		{
			get
			{
				int index = Math.Clamp(PacksHeld, 0, _boundaries.Count - 1);
				return Math.Min(_boundaries[index], Slots.Count);
			}
		}

		public TrackState(SlotData slot)
		{
			Slots = slot.Slots;
			_boundaries = ((slot.PackBoundaries.Count > 0) ? slot.PackBoundaries : new List<int> { Math.Min(slot.PackSize, slot.Slots.Count) });
		}

		public bool IsOpen(int slotIndex)
		{
			if (slotIndex >= 0)
			{
				return slotIndex < OpenSlots;
			}
			return false;
		}

		public void SetPacksHeld(int packs)
		{
			PacksHeld = ((packs >= 0) ? packs : 0);
		}

		public IReadOnlyList<int> SlotsRevealedBy(int previousPacks)
		{
			int num = BoundaryFor(previousPacks);
			int openSlots = OpenSlots;
			List<int> list = new List<int>();
			for (int i = num; i < openSlots; i++)
			{
				list.Add(i);
			}
			return list;
		}

		private int BoundaryFor(int packs)
		{
			int index = Math.Clamp(packs, 0, _boundaries.Count - 1);
			return Math.Min(_boundaries[index], Slots.Count);
		}
	}
	public enum TrapMoment
	{
		Spring,
		Hold,
		NothingToHit,
		AlreadyFinished,
		Credits
	}
	public enum CatReset
	{
		Wait,
		Reset,
		Drop,
		NoCat
	}
	public static class TrapTiming
	{
		public const float CatCap = 4f;

		public const float CatStartWindow = 0.5f;

		public static CatReset AfterCat(bool sameLaunch, bool settling, bool finished, bool catBusy, bool catSeen, float waited)
		{
			if (!sameLaunch || settling || finished)
			{
				return CatReset.Drop;
			}
			if (!catSeen && !catBusy)
			{
				if (!(waited < 0.5f))
				{
					return CatReset.NoCat;
				}
				return CatReset.Wait;
			}
			if (catBusy && waited < 4f)
			{
				return CatReset.Wait;
			}
			return CatReset.Reset;
		}

		public static TrapMoment For(bool inALevel, bool settling, bool hasObjects, bool isCredits, bool finished, float? secondsSinceCompletion, float grace)
		{
			if (!inALevel)
			{
				return TrapMoment.NothingToHit;
			}
			if (isCredits)
			{
				return TrapMoment.Credits;
			}
			if (finished)
			{
				return TrapMoment.AlreadyFinished;
			}
			if (secondsSinceCompletion.HasValue)
			{
				float valueOrDefault = secondsSinceCompletion.GetValueOrDefault();
				if (valueOrDefault < grace)
				{
					return TrapMoment.AlreadyFinished;
				}
			}
			if (settling)
			{
				return TrapMoment.Hold;
			}
			if (!hasObjects)
			{
				return TrapMoment.NothingToHit;
			}
			return TrapMoment.Spring;
		}
	}
}

ALTTLModKit.dll

Decompiled 4 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using Rewired;
using Rewired.Integration.UnityUI;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ALTTLModKit")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+9071b21c6adcb522865716b090945e6ed707583f")]
[assembly: AssemblyProduct("ALTTLModKit")]
[assembly: AssemblyTitle("ALTTLModKit")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ALTTLModKit
{
	public static class Hub
	{
		private static readonly ConcurrentQueue<Action> Pending = new ConcurrentQueue<Action>();

		public static Action<string>? OnError { get; set; }

		public static void OnMainThread(Action action)
		{
			if (action != null)
			{
				Pending.Enqueue(action);
			}
		}

		public static void Tick()
		{
			int count = Pending.Count;
			Action result;
			while (count-- > 0 && Pending.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch (Exception value)
				{
					OnError?.Invoke($"queued action threw: {value}");
				}
			}
		}
	}
	public static class Toasts
	{
		private sealed class Line
		{
			internal TextMeshProUGUI Label;

			internal float Age;
		}

		private const float HoldSeconds = 8f;

		private const float BusyHoldSeconds = 2.5f;

		private const float FadeSeconds = 1.5f;

		private const int MaxVisible = 10;

		private static GameObject? _canvas;

		private static Transform? _stack;

		private static readonly List<Line> _lines = new List<Line>();

		private static readonly Queue<(string Text, Color Colour)> _pending = new Queue<(string, Color)>();

		private const int MaxPending = 200;

		private static TMP_FontAsset? _font;

		public static Color Plain { get; set; } = Color.white;

		public static Color Notice { get; set; } = new Color(1f, 0.68f, 0.28f);

		public static Action<string>? OnWarning { get; set; }

		public static Action<string>? OnInfo { get; set; }

		public static string CanvasName { get; set; } = "ModKitToasts";

		public static Transform? OverlayRoot
		{
			get
			{
				if (!((Object)(object)_canvas == (Object)null))
				{
					return _canvas.transform;
				}
				return null;
			}
		}

		public static Color HexColor(string hex)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)Convert.ToInt32(hex.Substring(0, 2), 16) / 255f, (float)Convert.ToInt32(hex.Substring(2, 2), 16) / 255f, (float)Convert.ToInt32(hex.Substring(4, 2), 16) / 255f);
		}

		public static void Show(string text, Color colour)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			if ((Object)(object)_stack == (Object)null || _lines.Count >= 10)
			{
				string text2 = (((Object)(object)_stack == (Object)null) ? "the overlay is not up" : "the screen is full");
				if (_pending.Count < 200)
				{
					_pending.Enqueue((text, colour));
					OnInfo?.Invoke("toast queued, " + text2 + ": " + text);
					return;
				}
				OnWarning?.Invoke($"toast DROPPED, {_pending.Count} already waiting and {text2}: {text}");
				return;
			}
			OnInfo?.Invoke("toast: " + text);
			try
			{
				AddLine(text, colour);
			}
			catch (Exception ex)
			{
				Warn("toast: could not show '" + text + "': " + ex.Message);
			}
		}

		public static void Tick(float dt)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)_stack == (Object)null)
				{
					if (_pending.Count > 0)
					{
						Build();
					}
					return;
				}
				while (_pending.Count > 0 && _lines.Count < 10)
				{
					var (text, colour) = _pending.Dequeue();
					AddLine(text, colour);
				}
				float num = ((_pending.Count > 0) ? 2.5f : 8f);
				for (int num2 = _lines.Count - 1; num2 >= 0; num2--)
				{
					Line line = _lines[num2];
					line.Age += dt;
					if (line.Age >= num + 1.5f)
					{
						if ((Object)(object)line.Label != (Object)null)
						{
							Object.Destroy((Object)(object)((Component)line.Label).gameObject);
						}
						_lines.RemoveAt(num2);
					}
					else if (line.Age > num && (Object)(object)line.Label != (Object)null)
					{
						float num3 = 1f - (line.Age - num) / 1.5f;
						Color color = ((Graphic)line.Label).color;
						((Graphic)line.Label).color = new Color(color.r, color.g, color.b, Mathf.Clamp01(num3));
					}
				}
			}
			catch (Exception ex)
			{
				Warn("toast: tick failed: " + ex.Message);
			}
		}

		public static void Destroy()
		{
			try
			{
				_lines.Clear();
				_pending.Clear();
				if ((Object)(object)_canvas != (Object)null)
				{
					Object.Destroy((Object)(object)_canvas);
				}
			}
			catch (Exception ex)
			{
				Warn("toast: could not tear down: " + ex.Message);
			}
			_canvas = null;
			_stack = null;
		}

		private static void Build()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)FindFont() == (Object)null)
			{
				return;
			}
			try
			{
				_canvas = new GameObject(CanvasName);
				Object.DontDestroyOnLoad((Object)(object)_canvas);
				Canvas obj = _canvas.AddComponent<Canvas>();
				obj.renderMode = (RenderMode)0;
				obj.sortingOrder = 5000;
				CanvasScaler obj2 = _canvas.AddComponent<CanvasScaler>();
				obj2.uiScaleMode = (ScaleMode)1;
				obj2.referenceResolution = new Vector2(1920f, 1080f);
				GameObject val = new GameObject("stack");
				val.transform.SetParent(_canvas.transform, false);
				RectTransform obj3 = val.AddComponent<RectTransform>();
				obj3.anchorMin = new Vector2(0f, 0f);
				obj3.anchorMax = new Vector2(0f, 0f);
				obj3.pivot = new Vector2(0f, 0f);
				obj3.anchoredPosition = new Vector2(32f, 32f);
				obj3.sizeDelta = new Vector2(760f, 0f);
				VerticalLayoutGroup obj4 = val.AddComponent<VerticalLayoutGroup>();
				((LayoutGroup)obj4).childAlignment = (TextAnchor)6;
				((HorizontalOrVerticalLayoutGroup)obj4).childForceExpandHeight = false;
				((HorizontalOrVerticalLayoutGroup)obj4).childForceExpandWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj4).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)obj4).childControlWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj4).spacing = 4f;
				val.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
				_stack = val.transform;
				OnInfo?.Invoke("toasts: overlay ready");
			}
			catch (Exception ex)
			{
				Warn("toast: could not build the overlay: " + ex.Message);
				Destroy();
			}
		}

		private static void AddLine(string text, Color colour)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_stack == (Object)null))
			{
				GameObject val = new GameObject("toast");
				val.transform.SetParent(_stack, false);
				TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
				((TMP_Text)val2).font = FindFont();
				((TMP_Text)val2).text = text;
				((Graphic)val2).color = colour;
				((TMP_Text)val2).fontSize = 26f;
				((TMP_Text)val2).alignment = (TextAlignmentOptions)1025;
				((TMP_Text)val2).enableWordWrapping = true;
				((TMP_Text)val2).richText = true;
				((Graphic)val2).raycastTarget = false;
				((TMP_Text)val2).outlineWidth = 0.2f;
				((TMP_Text)val2).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)200);
				_lines.Add(new Line
				{
					Label = val2
				});
				if (_lines.Count > 10)
				{
					OnWarning?.Invoke($"toast: {_lines.Count} lines with a cap of {10} - a caller reached AddLine " + "without checking for room; messages will be crowded off screen");
				}
			}
		}

		private static TMP_FontAsset? FindFont()
		{
			if ((Object)(object)_font != (Object)null)
			{
				return _font;
			}
			foreach (TextMeshProUGUI item in Object.FindObjectsOfType<TextMeshProUGUI>())
			{
				if ((Object)(object)item != (Object)null && (Object)(object)((TMP_Text)item).font != (Object)null)
				{
					return _font = ((TMP_Text)item).font;
				}
			}
			return null;
		}

		private static void Warn(string message)
		{
			OnWarning?.Invoke(message);
		}
	}
	public static class TypingGuard
	{
		public enum Mode
		{
			Off,
			Keyboard,
			All,
			NavigationOnly
		}

		private static bool _suppressed;

		private static uint _ourProcess;

		private static bool _modeReported;

		private static bool _wasUnfocused;

		private static int _flips;

		private static int _flipsReported;

		private static int _horizontal;

		private static int _vertical;

		private static int _submit;

		private static int _cancel;

		private static RewiredStandaloneInputModule? _module;

		private static readonly List<(object Helper, MethodInfo Method, bool KeyboardOnly)> _mapHelpers = new List<(object, MethodInfo, bool)>();

		private static bool _mapsLookedUp;

		private static readonly List<(ControllerMap Map, bool WasEnabled)> _held = new List<(ControllerMap, bool)>();

		private static bool _heldPrecisely;

		private static bool _heldReported;

		public static Mode Suppression { get; set; } = Mode.Keyboard;

		public static bool Suppressed => _suppressed;

		public static bool IsSuppressing => _suppressed;

		public static Action<string>? OnWarning { get; set; }

		public static Action<string>? OnInfo { get; set; }

		public static Action<string>? OnDebug { get; set; }

		private static bool HasFocus()
		{
			try
			{
				IntPtr foregroundWindow = GetForegroundWindow();
				if (foregroundWindow == IntPtr.Zero)
				{
					return true;
				}
				GetWindowThreadProcessId(foregroundWindow, out var processId);
				if (processId == 0)
				{
					return true;
				}
				if (_ourProcess == 0)
				{
					_ourProcess = (uint)Process.GetCurrentProcess().Id;
				}
				return processId == _ourProcess;
			}
			catch
			{
				return true;
			}
		}

		[DllImport("user32.dll")]
		private static extern IntPtr GetForegroundWindow();

		[DllImport("user32.dll")]
		private static extern uint GetWindowThreadProcessId(IntPtr window, out uint processId);

		public static void Probe()
		{
			if (!_mapsLookedUp)
			{
				ResolveMapHelper();
			}
		}

		public static void Suppress()
		{
			if (_suppressed)
			{
				return;
			}
			_suppressed = true;
			if (!_modeReported)
			{
				_modeReported = true;
				Say($"suppression mode is {Suppression}");
			}
			if (Suppression == Mode.Off)
			{
				Warn("suppression is OFF; the cursor will drift while typing");
				return;
			}
			if (Suppression != Mode.NavigationOnly)
			{
				SetRewiredMapsEnabled(enabled: false);
			}
			try
			{
				_module = Object.FindObjectOfType<RewiredStandaloneInputModule>();
				if ((Object)(object)_module != (Object)null)
				{
					_horizontal = _module.HorizontalActionId;
					_vertical = _module.VerticalActionId;
					_submit = _module.SubmitActionId;
					_cancel = _module.CancelActionId;
					_module.HorizontalActionId = -1;
					_module.VerticalActionId = -1;
					_module.SubmitActionId = -1;
					_module.CancelActionId = -1;
				}
			}
			catch (Exception ex)
			{
				Warn("could not unbind UI navigation: " + ex.Message);
			}
		}

		public static void Restore()
		{
			if (!_suppressed)
			{
				return;
			}
			_suppressed = false;
			SetRewiredMapsEnabled(enabled: true);
			try
			{
				if ((Object)(object)_module != (Object)null)
				{
					_module.HorizontalActionId = _horizontal;
					_module.VerticalActionId = _vertical;
					_module.SubmitActionId = _submit;
					_module.CancelActionId = _cancel;
				}
			}
			catch (Exception ex)
			{
				Warn("COULD NOT restore UI navigation: " + ex.Message);
			}
			_module = null;
		}

		private static void SetRewiredMapsEnabled(bool enabled)
		{
			if (enabled)
			{
				RestoreMaps();
			}
			else
			{
				SuppressMaps();
			}
		}

		private static void SuppressMaps()
		{
			try
			{
				if (!_mapsLookedUp)
				{
					ResolveMapHelper();
				}
				_held.Clear();
				_heldPrecisely = false;
				if (Suppression == Mode.All)
				{
					Blanket(enabled: false);
					return;
				}
				int num = 0;
				foreach (var mapHelper in _mapHelpers)
				{
					object item = mapHelper.Helper;
					MethodInfo item2 = mapHelper.Method;
					bool item3 = mapHelper.KeyboardOnly;
					MapHelper val = (MapHelper)((item is MapHelper) ? item : null);
					if (val == null || !item3)
					{
						Warn("Rewired: no typed map helper, falling back to switching every keyboard map off and on again");
						Invoke(item2, item, item3, enabled: false);
						continue;
					}
					List<ControllerMap> val2 = new List<ControllerMap>();
					val.GetAllMaps((ControllerType)0, val2);
					for (int i = 0; i < val2.Count; i++)
					{
						ControllerMap val3 = val2[i];
						if (val3 != null)
						{
							bool enabled = val3.enabled;
							if (!enabled)
							{
								num++;
							}
							_held.Add((val3, enabled));
							val3.enabled = false;
						}
					}
					_heldPrecisely = true;
				}
				if (_heldPrecisely && (!_heldReported || num > 0))
				{
					_heldReported = true;
					Say($"Rewired: {_held.Count} keyboard map(s) held individually, {num} of them already off");
				}
			}
			catch (Exception ex)
			{
				Warn("could not disable Rewired maps: " + ex.Message);
			}
		}

		private static void RestoreMaps()
		{
			try
			{
				if (!_heldPrecisely)
				{
					Blanket(enabled: true);
					return;
				}
				int num = 0;
				foreach (var (val, enabled) in _held)
				{
					try
					{
						if (val != null)
						{
							val.enabled = enabled;
							num++;
						}
					}
					catch
					{
					}
				}
				_held.Clear();
				_heldPrecisely = false;
				if (num == 0)
				{
					Blanket(enabled: true);
				}
			}
			catch (Exception ex)
			{
				Warn("COULD NOT RE-ENABLE Rewired maps: " + ex.Message);
				try
				{
					Blanket(enabled: true);
				}
				catch
				{
				}
			}
		}

		private static void Blanket(bool enabled)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			bool flag = Suppression == Mode.All;
			foreach (var (obj, methodInfo, flag2) in _mapHelpers)
			{
				if (flag2 && flag)
				{
					foreach (ControllerType value in Enum.GetValues(typeof(ControllerType)))
					{
						methodInfo.Invoke(obj, new object[2]
						{
							enabled,
							(object)value
						});
					}
				}
				else
				{
					Invoke(methodInfo, obj, flag2, enabled);
				}
			}
		}

		private static void Invoke(MethodInfo method, object helper, bool keyboardOnly, bool enabled)
		{
			if (keyboardOnly)
			{
				method.Invoke(helper, new object[2]
				{
					enabled,
					(object)(ControllerType)0
				});
			}
			else
			{
				method.Invoke(helper, new object[1] { enabled });
			}
		}

		private static void ResolveMapHelper()
		{
			object obj = Get(typeof(ReInput), null, "players", "Players");
			if (obj == null)
			{
				return;
			}
			_mapHelpers.Clear();
			foreach (object item3 in AllPlayers(obj))
			{
				object controllers = Get(item3.GetType(), item3, "controllers", "Controllers");
				if (controllers == null)
				{
					continue;
				}
				PropertyInfo[] properties = controllers.GetType().GetProperties();
				foreach (PropertyInfo property in properties)
				{
					if (property.GetIndexParameters().Length == 0 && TryBind(SafeGet(() => property.GetValue(controllers))))
					{
						break;
					}
				}
			}
			if (_mapHelpers.Count == 0)
			{
				Warn("Rewired: no per-player map helper found; the mouse will drift while typing");
				return;
			}
			_mapsLookedUp = true;
			foreach (var mapHelper in _mapHelpers)
			{
				MethodInfo item = mapHelper.Method;
				bool item2 = mapHelper.KeyboardOnly;
				ParameterInfo[] parameters = item.GetParameters();
				StringBuilder stringBuilder = new StringBuilder(item.Name).Append('(');
				for (int num = 0; num < parameters.Length; num++)
				{
					if (num > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(parameters[num].ParameterType.Name);
				}
				Say($"Rewired: bound {stringBuilder.Append(')')} " + (item2 ? "- keyboard maps only" : "- ALL maps"));
			}
			Say($"Rewired maps reachable for {_mapHelpers.Count} player(s)");
		}

		private static IEnumerable<object> AllPlayers(object players)
		{
			List<object> list = new List<object>();
			Type type = players.GetType();
			if (Get(type, players, "allPlayers", "AllPlayers", "players", "Players") is IEnumerable enumerable)
			{
				foreach (object item in enumerable)
				{
					if (item != null)
					{
						list.Add(item);
					}
				}
			}
			if (list.Count > 0)
			{
				return list;
			}
			int? num = Get(type, players, "playerCount", "PlayerCount") as int?;
			if ((!num.HasValue || num.GetValueOrDefault() < 1) ? true : false)
			{
				Warn("Rewired: could not determine the player count");
				return list;
			}
			MethodInfo getPlayer = type.GetMethod("GetPlayer", new Type[1] { typeof(int) });
			if (getPlayer == null)
			{
				return list;
			}
			int id;
			for (id = 0; id < num.Value; id++)
			{
				object obj = SafeGet(() => getPlayer.Invoke(players, new object[1] { id }));
				if (obj != null)
				{
					list.Add(obj);
				}
			}
			return list;
		}

		private static object? SafeGet(Func<object?> get)
		{
			try
			{
				return get();
			}
			catch
			{
				return null;
			}
		}

		private static bool TryBind(object? candidate)
		{
			if (candidate == null)
			{
				return false;
			}
			MethodInfo method = candidate.GetType().GetMethod("SetAllMapsEnabled", new Type[2]
			{
				typeof(bool),
				typeof(ControllerType)
			});
			if (method != null)
			{
				_mapHelpers.Add((candidate, method, true));
				return true;
			}
			MethodInfo method2 = candidate.GetType().GetMethod("SetAllMapsEnabled", new Type[1] { typeof(bool) });
			if (method2 == null)
			{
				return false;
			}
			Warn("Rewired: no per-controller-type map switch; falling back to disabling ALL maps, which leaves the mouse stuck while a text box has focus");
			_mapHelpers.Add((candidate, method2, false));
			return true;
		}

		private static object? Get(Type type, object? instance, params string[] names)
		{
			foreach (string name in names)
			{
				Type type2 = type;
				while (type2 != null)
				{
					try
					{
						PropertyInfo property = type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						if (property != null && property.GetIndexParameters().Length == 0)
						{
							return property.GetValue(instance);
						}
						FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						if (field != null)
						{
							return field.GetValue(instance);
						}
					}
					catch
					{
					}
					type2 = type2.BaseType;
				}
			}
			return null;
		}

		public static void Tick(Func<TMP_InputField?> focused, Action focusNext)
		{
			TMP_InputField obj = focused();
			bool flag = !HasFocus();
			if (flag != _wasUnfocused)
			{
				_wasUnfocused = flag;
				Trace(flag ? "the game lost focus" : "the game regained focus");
			}
			bool flag2 = (Object)(object)obj != (Object)null || flag;
			if (flag2 && !_suppressed)
			{
				_flips++;
				Suppress();
			}
			else if (!flag2 && _suppressed)
			{
				_flips++;
				Restore();
			}
			if (_flips >= _flipsReported + 20)
			{
				_flipsReported = _flips;
				Warn($"suppression has flipped {_flips} times - if the game has " + "not been alt-tabbed that often, focus is flickering, which would keep resetting the input state underneath the mouse");
			}
			if (!((Object)(object)obj == (Object)null) && Input.GetKeyDown((KeyCode)9))
			{
				focusNext();
			}
		}

		private static void Warn(string message)
		{
			OnWarning?.Invoke(message);
		}

		private static void Say(string message)
		{
			(OnInfo ?? OnWarning)?.Invoke(message);
		}

		private static void Trace(string message)
		{
			(OnDebug ?? OnInfo ?? OnWarning)?.Invoke(message);
		}
	}
	[SupportedOSPlatform("windows")]
	public static class VirtualDesktop
	{
		private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

		[ComImport]
		[Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		private interface IVirtualDesktopManager
		{
			[PreserveSig]
			int IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow, out int onCurrent);

			[PreserveSig]
			int GetWindowDesktopId(IntPtr topLevelWindow, out Guid desktopId);

			[PreserveSig]
			int MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId);
		}

		private const string DesktopsKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops";

		private const int SW_SHOW = 5;

		public static void MoveGameTo(int oneBasedIndex, Action<string> log)
		{
			if (oneBasedIndex < 1)
			{
				return;
			}
			try
			{
				List<Guid> list = ReadDesktopIds();
				if (list.Count == 0)
				{
					log("virtual desktop: could not read the desktop list from the registry");
					return;
				}
				if (oneBasedIndex > list.Count)
				{
					log($"virtual desktop: asked for #{oneBasedIndex} but only {list.Count} exist - leaving the window alone");
					return;
				}
				Guid guid = list[oneBasedIndex - 1];
				IVirtualDesktopManager virtualDesktopManager = (IVirtualDesktopManager)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("aa509086-5ca9-4c25-8f95-589d3c07b48a")));
				int num = 0;
				foreach (IntPtr item in OwnWindows())
				{
					Guid desktopId = guid;
					int num2 = virtualDesktopManager.MoveWindowToDesktop(item, ref desktopId);
					if (num2 == 0)
					{
						num++;
						continue;
					}
					log($"virtual desktop: move failed for hwnd {item} (hr 0x{num2:X8})");
				}
				log($"virtual desktop: moved {num} window(s) to #{oneBasedIndex} ({guid})");
			}
			catch (Exception ex)
			{
				log("virtual desktop: " + ex.GetType().Name + " - " + ex.Message);
			}
		}

		private static List<Guid> ReadDesktopIds()
		{
			List<Guid> list = new List<Guid>();
			byte[] array = RegBinary("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops", "VirtualDesktopIDs");
			if (array == null)
			{
				return list;
			}
			for (int i = 0; i + 16 <= array.Length; i += 16)
			{
				byte[] array2 = new byte[16];
				Array.Copy(array, i, array2, 0, 16);
				list.Add(new Guid(array2));
			}
			return list;
		}

		public static void FocusGameWindow(Action<string> log)
		{
			try
			{
				IntPtr intPtr = GameWindow();
				if (intPtr == IntPtr.Zero)
				{
					log("could not find the game window to raise");
					return;
				}
				ShowWindow(intPtr, 5);
				BringWindowToTop(intPtr);
				SetForegroundWindow(intPtr);
				log("raised the game window above the console");
			}
			catch (Exception ex)
			{
				log("could not raise the game window: " + ex.Message);
			}
		}

		private static IntPtr GameWindow()
		{
			IntPtr found = IntPtr.Zero;
			uint self = GetCurrentProcessId();
			StringBuilder className = new StringBuilder(256);
			EnumWindows(delegate(IntPtr h, IntPtr _)
			{
				if (!IsWindowVisible(h))
				{
					return true;
				}
				GetWindowThreadProcessId(h, out var pid);
				if (pid != self)
				{
					return true;
				}
				className.Clear();
				GetClassNameW(h, className, className.Capacity);
				if (className.ToString().IndexOf("Unity", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					found = h;
					return false;
				}
				return true;
			}, IntPtr.Zero);
			return found;
		}

		[DllImport("user32.dll")]
		private static extern bool SetForegroundWindow(IntPtr hWnd);

		[DllImport("user32.dll")]
		private static extern bool BringWindowToTop(IntPtr hWnd);

		[DllImport("user32.dll")]
		private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

		[DllImport("user32.dll", CharSet = CharSet.Unicode)]
		private static extern int GetClassNameW(IntPtr hWnd, StringBuilder name, int count);

		private static IEnumerable<IntPtr> OwnWindows()
		{
			List<IntPtr> mine = new List<IntPtr>();
			uint self = GetCurrentProcessId();
			EnumWindows(delegate(IntPtr h, IntPtr _)
			{
				if (!IsWindowVisible(h))
				{
					return true;
				}
				GetWindowThreadProcessId(h, out var pid);
				if (pid == self)
				{
					mine.Add(h);
				}
				return true;
			}, IntPtr.Zero);
			return mine;
		}

		private static byte[]? RegBinary(string subKey, string value)
		{
			int dataSize = 0;
			if (RegGetValueW((IntPtr)(-2147483647), subKey, value, 8, IntPtr.Zero, null, ref dataSize) != 0 || dataSize <= 0)
			{
				return null;
			}
			byte[] array = new byte[dataSize];
			if (RegGetValueW((IntPtr)(-2147483647), subKey, value, 8, IntPtr.Zero, array, ref dataSize) != 0)
			{
				return null;
			}
			return array;
		}

		[DllImport("user32.dll")]
		private static extern bool EnumWindows(EnumWindowsProc cb, IntPtr lParam);

		[DllImport("user32.dll")]
		private static extern bool IsWindowVisible(IntPtr hWnd);

		[DllImport("user32.dll")]
		private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);

		[DllImport("kernel32.dll")]
		private static extern uint GetCurrentProcessId();

		[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
		private static extern int RegGetValueW(IntPtr hkey, string subKey, string value, int flags, IntPtr type, byte[]? data, ref int dataSize);
	}
}

Archipelago.MultiClient.Net.dll

Decompiled 4 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Archipelago.MultiClient.Net.Colors;
using Archipelago.MultiClient.Net.ConcurrentCollection;
using Archipelago.MultiClient.Net.Converters;
using Archipelago.MultiClient.Net.DataPackage;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Exceptions;
using Archipelago.MultiClient.Net.Extensions;
using Archipelago.MultiClient.Net.Helpers;
using Archipelago.MultiClient.Net.MessageLog.Messages;
using Archipelago.MultiClient.Net.MessageLog.Parts;
using Archipelago.MultiClient.Net.Models;
using Archipelago.MultiClient.Net.Packets;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: ComVisible(false)]
[assembly: Guid("35a803ad-85ed-42e9-b1e3-c6b72096f0c1")]
[assembly: InternalsVisibleTo("Archipelago.MultiClient.Net.Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Jarno Westhof, Hussein Farran, Zach Parks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyDescription("A client library for use with .NET based prog-langs for interfacing with Archipelago hosts.")]
[assembly: AssemblyFileVersion("6.7.1.0")]
[assembly: AssemblyInformationalVersion("6.7.1+0c57591db30f2497b0b4fef87164aa2bbe2e51b2")]
[assembly: AssemblyProduct("Archipelago.MultiClient.Net")]
[assembly: AssemblyTitle("Archipelago.MultiClient.Net")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ArchipelagoMW/Archipelago.MultiClient.Net")]
[assembly: AssemblyVersion("6.7.1.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
internal interface IConcurrentHashSet<T>
{
	bool TryAdd(T item);

	bool Contains(T item);

	void UnionWith(T[] otherSet);

	T[] ToArray();

	ReadOnlyCollection<T> AsToReadOnlyCollection();

	ReadOnlyCollection<T> AsToReadOnlyCollectionExcept(IConcurrentHashSet<T> otherSet);
}
public class AttemptingStringEnumConverter : StringEnumConverter
{
	public AttemptingStringEnumConverter()
	{
	}

	public AttemptingStringEnumConverter(Type namingStrategyType)
		: base(namingStrategyType)
	{
	}

	public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
	{
		try
		{
			return ((StringEnumConverter)this).ReadJson(reader, objectType, existingValue, serializer);
		}
		catch (JsonSerializationException)
		{
			return objectType.IsValueType ? Activator.CreateInstance(objectType) : null;
		}
	}
}
namespace Archipelago.MultiClient.Net
{
	[Serializable]
	public abstract class ArchipelagoPacketBase
	{
		[JsonIgnore]
		internal JObject jobject;

		[JsonProperty("cmd")]
		[JsonConverter(typeof(StringEnumConverter))]
		public abstract ArchipelagoPacketType PacketType { get; }

		public JObject ToJObject()
		{
			return jobject;
		}
	}
	public interface IArchipelagoSession : IArchipelagoSessionActions
	{
		IArchipelagoSocketHelper Socket { get; }

		IReceivedItemsHelper Items { get; }

		ILocationCheckHelper Locations { get; }

		IPlayerHelper Players { get; }

		IDataStorageHelper DataStorage { get; }

		IConnectionInfoProvider ConnectionInfo { get; }

		IRoomStateHelper RoomState { get; }

		IMessageLogHelper MessageLog { get; }

		IHintsHelper Hints { get; }

		Task<RoomInfoPacket> ConnectAsync();

		Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true);

		LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true);
	}
	public class ArchipelagoSession : IArchipelagoSession, IArchipelagoSessionActions
	{
		private const int ArchipelagoConnectionTimeoutInSeconds = 4;

		private ConnectionInfoHelper connectionInfo;

		private TaskCompletionSource<LoginResult> loginResultTask = new TaskCompletionSource<LoginResult>();

		private TaskCompletionSource<RoomInfoPacket> roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>();

		public IArchipelagoSocketHelper Socket { get; }

		public IReceivedItemsHelper Items { get; }

		public ILocationCheckHelper Locations { get; }

		public IPlayerHelper Players { get; }

		public IDataStorageHelper DataStorage { get; }

		public IConnectionInfoProvider ConnectionInfo => connectionInfo;

		public IRoomStateHelper RoomState { get; }

		public IMessageLogHelper MessageLog { get; }

		public IHintsHelper Hints { get; }

		internal ArchipelagoSession(IArchipelagoSocketHelper socket, IReceivedItemsHelper items, ILocationCheckHelper locations, IPlayerHelper players, IRoomStateHelper roomState, ConnectionInfoHelper connectionInfoHelper, IDataStorageHelper dataStorage, IMessageLogHelper messageLog, IHintsHelper createHints)
		{
			Socket = socket;
			Items = items;
			Locations = locations;
			Players = players;
			RoomState = roomState;
			connectionInfo = connectionInfoHelper;
			DataStorage = dataStorage;
			MessageLog = messageLog;
			Hints = createHints;
			socket.PacketReceived += Socket_PacketReceived;
		}

		private void Socket_PacketReceived(ArchipelagoPacketBase packet)
		{
			if (!(packet is ConnectedPacket) && !(packet is ConnectionRefusedPacket))
			{
				if (packet is RoomInfoPacket result)
				{
					roomInfoPacketTask.TrySetResult(result);
				}
			}
			else
			{
				loginResultTask.TrySetResult(LoginResult.FromPacket(packet));
			}
		}

		public Task<RoomInfoPacket> ConnectAsync()
		{
			roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>();
			Task.Factory.StartNew(delegate
			{
				try
				{
					Task task = Socket.ConnectAsync();
					task.Wait(TimeSpan.FromSeconds(4.0));
					if (!task.IsCompleted)
					{
						roomInfoPacketTask.TrySetCanceled();
					}
				}
				catch (AggregateException)
				{
					roomInfoPacketTask.TrySetCanceled();
				}
			});
			return roomInfoPacketTask.Task;
		}

		public Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true)
		{
			loginResultTask = new TaskCompletionSource<LoginResult>();
			if (!roomInfoPacketTask.Task.IsCompleted)
			{
				loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first"));
				return loginResultTask.Task;
			}
			connectionInfo.SetConnectionParameters(game, tags, itemsHandlingFlags, uuid);
			try
			{
				Socket.SendPacket(BuildConnectPacket(name, password, version, requestSlotData));
			}
			catch (ArchipelagoSocketClosedException)
			{
				loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first"));
				return loginResultTask.Task;
			}
			SetResultAfterTimeout(loginResultTask, 4, new LoginFailure("Connection timed out."));
			return loginResultTask.Task;
		}

		private static void SetResultAfterTimeout<T>(TaskCompletionSource<T> task, int timeoutInSeconds, T result)
		{
			new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)).Token.Register(delegate
			{
				task.TrySetResult(result);
			});
		}

		public LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true)
		{
			Task<RoomInfoPacket> task = ConnectAsync();
			try
			{
				task.Wait(TimeSpan.FromSeconds(4.0));
			}
			catch (AggregateException ex)
			{
				if (ex.GetBaseException() is OperationCanceledException)
				{
					return new LoginFailure("Connection timed out.");
				}
				return new LoginFailure(ex.GetBaseException().Message);
			}
			if (!task.IsCompleted)
			{
				return new LoginFailure("Connection timed out.");
			}
			return LoginAsync(game, name, itemsHandlingFlags, version, tags, uuid, password, requestSlotData).Result;
		}

		private ConnectPacket BuildConnectPacket(string name, string password, Version version, bool requestSlotData)
		{
			return new ConnectPacket
			{
				Game = ConnectionInfo.Game,
				Name = name,
				Password = password,
				Tags = ConnectionInfo.Tags,
				Uuid = ConnectionInfo.Uuid,
				Version = ((version != null) ? new NetworkVersion(version) : new NetworkVersion(0, 6, 0)),
				ItemsHandling = ConnectionInfo.ItemsHandlingFlags,
				RequestSlotData = requestSlotData
			};
		}

		public void Say(string message)
		{
			Socket.SendPacket(new SayPacket
			{
				Text = message
			});
		}

		public void SetClientState(ArchipelagoClientState state)
		{
			Socket.SendPacket(new StatusUpdatePacket
			{
				Status = state
			});
		}

		public void SetGoalAchieved()
		{
			SetClientState(ArchipelagoClientState.ClientGoal);
		}
	}
	public interface IArchipelagoSessionActions
	{
		void Say(string message);

		void SetClientState(ArchipelagoClientState state);

		void SetGoalAchieved();
	}
	public static class ArchipelagoSessionFactory
	{
		public static ArchipelagoSession CreateSession(Uri uri)
		{
			ArchipelagoSocketHelper socket = new ArchipelagoSocketHelper(uri);
			DataPackageCache cache = new DataPackageCache(socket);
			ConnectionInfoHelper connectionInfoHelper = new ConnectionInfoHelper(socket);
			PlayerHelper playerHelper = new PlayerHelper(socket, connectionInfoHelper);
			ItemInfoResolver itemInfoResolver = new ItemInfoResolver(cache, connectionInfoHelper);
			LocationCheckHelper locationCheckHelper = new LocationCheckHelper(socket, itemInfoResolver, connectionInfoHelper, playerHelper);
			ReceivedItemsHelper items = new ReceivedItemsHelper(socket, locationCheckHelper, itemInfoResolver, connectionInfoHelper, playerHelper);
			RoomStateHelper roomStateHelper = new RoomStateHelper(socket, locationCheckHelper);
			DataStorageHelper dataStorageHelper = new DataStorageHelper(socket, connectionInfoHelper);
			MessageLogHelper messageLog = new MessageLogHelper(socket, itemInfoResolver, playerHelper, connectionInfoHelper);
			HintsHelper createHints = new HintsHelper(socket, playerHelper, locationCheckHelper, roomStateHelper, dataStorageHelper);
			return new ArchipelagoSession(socket, items, locationCheckHelper, playerHelper, roomStateHelper, connectionInfoHelper, dataStorageHelper, messageLog, createHints);
		}

		public static ArchipelagoSession CreateSession(string hostname, int port = 38281)
		{
			return CreateSession(ParseUri(hostname, port));
		}

		internal static Uri ParseUri(string hostname, int port)
		{
			string text = hostname;
			if (!text.StartsWith("ws://") && !text.StartsWith("wss://"))
			{
				text = "unspecified://" + text;
			}
			if (!text.Substring(text.IndexOf("://", StringComparison.Ordinal) + 3).Contains(":"))
			{
				text += $":{port}";
			}
			if (text.EndsWith(":"))
			{
				text += port;
			}
			return new Uri(text);
		}
	}
	public abstract class LoginResult
	{
		public abstract bool Successful { get; }

		public static LoginResult FromPacket(ArchipelagoPacketBase packet)
		{
			if (!(packet is ConnectedPacket connectedPacket))
			{
				if (packet is ConnectionRefusedPacket connectionRefusedPacket)
				{
					return new LoginFailure(connectionRefusedPacket);
				}
				throw new ArgumentOutOfRangeException("packet", "packet is not a connection result packet");
			}
			return new LoginSuccessful(connectedPacket);
		}
	}
	public class LoginSuccessful : LoginResult
	{
		public override bool Successful => true;

		public int Team { get; }

		public int Slot { get; }

		public Dictionary<string, object> SlotData { get; }

		public LoginSuccessful(ConnectedPacket connectedPacket)
		{
			Team = connectedPacket.Team;
			Slot = connectedPacket.Slot;
			SlotData = connectedPacket.SlotData;
		}
	}
	public class LoginFailure : LoginResult
	{
		public override bool Successful => false;

		public ConnectionRefusedError[] ErrorCodes { get; }

		public string[] Errors { get; }

		public LoginFailure(ConnectionRefusedPacket connectionRefusedPacket)
		{
			if (connectionRefusedPacket.Errors != null)
			{
				ErrorCodes = connectionRefusedPacket.Errors.ToArray();
				Errors = ErrorCodes.Select(GetErrorMessage).ToArray();
			}
			else
			{
				ErrorCodes = new ConnectionRefusedError[0];
				Errors = new string[0];
			}
		}

		public LoginFailure(string message)
		{
			ErrorCodes = new ConnectionRefusedError[0];
			Errors = new string[1] { message };
		}

		private static string GetErrorMessage(ConnectionRefusedError errorCode)
		{
			return errorCode switch
			{
				ConnectionRefusedError.InvalidSlot => "The slot name did not match any slot on the server.", 
				ConnectionRefusedError.InvalidGame => "The slot is set to a different game on the server.", 
				ConnectionRefusedError.SlotAlreadyTaken => "The slot already has a connection with a different uuid established.", 
				ConnectionRefusedError.IncompatibleVersion => "The client and server version mismatch.", 
				ConnectionRefusedError.InvalidPassword => "The password is invalid.", 
				ConnectionRefusedError.InvalidItemsHandling => "The item handling flags provided are invalid.", 
				_ => $"Unknown error: {errorCode}.", 
			};
		}
	}
	internal class TwoWayLookup<TA, TB> : IEnumerable<KeyValuePair<TB, TA>>, IEnumerable
	{
		private readonly Dictionary<TA, TB> aToB = new Dictionary<TA, TB>();

		private readonly Dictionary<TB, TA> bToA = new Dictionary<TB, TA>();

		public TA this[TB b] => bToA[b];

		public TB this[TA a] => aToB[a];

		public void Add(TA a, TB b)
		{
			aToB[a] = b;
			bToA[b] = a;
		}

		public void Add(TB b, TA a)
		{
			Add(a, b);
		}

		public bool TryGetValue(TA a, out TB b)
		{
			return aToB.TryGetValue(a, out b);
		}

		public bool TryGetValue(TB b, out TA a)
		{
			return bToA.TryGetValue(b, out a);
		}

		public IEnumerator<KeyValuePair<TB, TA>> GetEnumerator()
		{
			return bToA.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
}
namespace Archipelago.MultiClient.Net.Packets
{
	public class BouncedPacket : BouncePacket
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Bounced;
	}
	public class BouncePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Bounce;

		[JsonProperty("games")]
		public List<string> Games { get; set; } = new List<string>();


		[JsonProperty("slots")]
		public List<int> Slots { get; set; } = new List<int>();


		[JsonProperty("tags")]
		public List<string> Tags { get; set; } = new List<string>();


		[JsonProperty("data")]
		public Dictionary<string, JToken> Data { get; set; }
	}
	public class ConnectedPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Connected;

		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("players")]
		public NetworkPlayer[] Players { get; set; }

		[JsonProperty("missing_locations")]
		public long[] MissingChecks { get; set; }

		[JsonProperty("checked_locations")]
		public long[] LocationsChecked { get; set; }

		[JsonProperty("slot_data")]
		public Dictionary<string, object> SlotData { get; set; }

		[JsonProperty("slot_info")]
		public Dictionary<int, NetworkSlot> SlotInfo { get; set; }

		[JsonProperty("hint_points")]
		public int? HintPoints { get; set; }
	}
	public class ConnectionRefusedPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ConnectionRefused;

		[JsonProperty("errors", ItemConverterType = typeof(AttemptingStringEnumConverter))]
		public ConnectionRefusedError[] Errors { get; set; }
	}
	public class ConnectPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Connect;

		[JsonProperty("password")]
		public string Password { get; set; }

		[JsonProperty("game")]
		public string Game { get; set; }

		[JsonProperty("name")]
		public string Name { get; set; }

		[JsonProperty("uuid")]
		public string Uuid { get; set; }

		[JsonProperty("version")]
		public NetworkVersion Version { get; set; }

		[JsonProperty("tags")]
		public string[] Tags { get; set; }

		[JsonProperty("items_handling")]
		public ItemsHandlingFlags ItemsHandling { get; set; }

		[JsonProperty("slot_data")]
		public bool RequestSlotData { get; set; }
	}
	public class ConnectUpdatePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ConnectUpdate;

		[JsonProperty("tags")]
		public string[] Tags { get; set; }

		[JsonProperty("items_handling")]
		public ItemsHandlingFlags? ItemsHandling { get; set; }
	}
	public class CreateHintsPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.CreateHints;

		[JsonProperty("locations")]
		public long[] Locations { get; set; }

		[JsonProperty("player")]
		public int Player { get; set; }

		[JsonProperty("status")]
		public HintStatus Status { get; set; }
	}
	public class DataPackagePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.DataPackage;

		[JsonProperty("data")]
		public Archipelago.MultiClient.Net.Models.DataPackage DataPackage { get; set; }
	}
	public class GetDataPackagePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.GetDataPackage;

		[JsonProperty("games")]
		public string[] Games { get; set; }
	}
	public class GetPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Get;

		[JsonProperty("keys")]
		public string[] Keys { get; set; }
	}
	public class InvalidPacketPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.InvalidPacket;

		[JsonProperty("type")]
		public InvalidPacketErrorType ErrorType { get; set; }

		[JsonProperty("text")]
		public string ErrorText { get; set; }

		[JsonProperty("original_cmd")]
		public ArchipelagoPacketType OriginalCmd { get; set; }
	}
	public class LocationChecksPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationChecks;

		[JsonProperty("locations")]
		public long[] Locations { get; set; }
	}
	public class LocationInfoPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationInfo;

		[JsonProperty("locations")]
		public NetworkItem[] Locations { get; set; }
	}
	public class LocationScoutsPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationScouts;

		[JsonProperty("locations")]
		public long[] Locations { get; set; }

		[JsonProperty("create_as_hint")]
		public int CreateAsHint { get; set; }
	}
	public class PrintJsonPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.PrintJSON;

		[JsonProperty("data")]
		public JsonMessagePart[] Data { get; set; }

		[JsonProperty("type")]
		[JsonConverter(typeof(AttemptingStringEnumConverter))]
		public JsonMessageType? MessageType { get; set; }
	}
	public class ItemPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("receiving")]
		public int ReceivingPlayer { get; set; }

		[JsonProperty("item")]
		public NetworkItem Item { get; set; }
	}
	public class ItemCheatPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("receiving")]
		public int ReceivingPlayer { get; set; }

		[JsonProperty("item")]
		public NetworkItem Item { get; set; }

		[JsonProperty("team")]
		public int Team { get; set; }
	}
	public class HintPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("receiving")]
		public int ReceivingPlayer { get; set; }

		[JsonProperty("item")]
		public NetworkItem Item { get; set; }

		[JsonProperty("found")]
		public bool? Found { get; set; }
	}
	public class JoinPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("tags")]
		public string[] Tags { get; set; }
	}
	public class LeavePrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }
	}
	public class ChatPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("message")]
		public string Message { get; set; }
	}
	public class ServerChatPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("message")]
		public string Message { get; set; }
	}
	public class TutorialPrintJsonPacket : PrintJsonPacket
	{
	}
	public class TagsChangedPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("tags")]
		public string[] Tags { get; set; }
	}
	public class CommandResultPrintJsonPacket : PrintJsonPacket
	{
	}
	public class AdminCommandResultPrintJsonPacket : PrintJsonPacket
	{
	}
	public class GoalPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }
	}
	public class ReleasePrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }
	}
	public class CollectPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }
	}
	public class CountdownPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("countdown")]
		public int RemainingSeconds { get; set; }
	}
	public class ReceivedItemsPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ReceivedItems;

		[JsonProperty("index")]
		public int Index { get; set; }

		[JsonProperty("items")]
		public NetworkItem[] Items { get; set; }
	}
	public class RetrievedPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Retrieved;

		[JsonProperty("keys")]
		public Dictionary<string, JToken> Data { get; set; }
	}
	public class RoomInfoPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.RoomInfo;

		[JsonProperty("version")]
		public NetworkVersion Version { get; set; }

		[JsonProperty("generator_version")]
		public NetworkVersion GeneratorVersion { get; set; }

		[JsonProperty("tags")]
		public string[] Tags { get; set; }

		[JsonProperty("password")]
		public bool Password { get; set; }

		[JsonProperty("permissions")]
		public Dictionary<string, Permissions> Permissions { get; set; }

		[JsonProperty("hint_cost")]
		public int HintCostPercentage { get; set; }

		[JsonProperty("location_check_points")]
		public int LocationCheckPoints { get; set; }

		[JsonProperty("players")]
		public NetworkPlayer[] Players { get; set; }

		[JsonProperty("games")]
		public string[] Games { get; set; }

		[JsonProperty("datapackage_checksums")]
		public Dictionary<string, string> DataPackageChecksums { get; set; }

		[JsonProperty("seed_name")]
		public string SeedName { get; set; }

		[JsonProperty("time")]
		public double Timestamp { get; set; }
	}
	public class RoomUpdatePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.RoomUpdate;

		[JsonProperty("tags")]
		public string[] Tags { get; set; }

		[JsonProperty("password")]
		public bool? Password { get; set; }

		[JsonProperty("permissions")]
		public Dictionary<string, Permissions> Permissions { get; set; } = new Dictionary<string, Permissions>();


		[JsonProperty("hint_cost")]
		public int? HintCostPercentage { get; set; }

		[JsonProperty("location_check_points")]
		public int? LocationCheckPoints { get; set; }

		[JsonProperty("players")]
		public NetworkPlayer[] Players { get; set; }

		[JsonProperty("hint_points")]
		public int? HintPoints { get; set; }

		[JsonProperty("checked_locations")]
		public long[] CheckedLocations { get; set; }
	}
	public class SayPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Say;

		[JsonProperty("text")]
		public string Text { get; set; }
	}
	public class SetNotifyPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.SetNotify;

		[JsonProperty("keys")]
		public string[] Keys { get; set; }
	}
	public class SetPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Set;

		[JsonProperty("key")]
		public string Key { get; set; }

		[JsonProperty("default")]
		public JToken DefaultValue { get; set; }

		[JsonProperty("operations")]
		public OperationSpecification[] Operations { get; set; }

		[JsonProperty("want_reply")]
		public bool WantReply { get; set; }

		[JsonExtensionData]
		public Dictionary<string, JToken> AdditionalArguments { get; set; }

		[OnDeserialized]
		internal void OnDeserializedMethod(StreamingContext context)
		{
			AdditionalArguments?.Remove("cmd");
		}
	}
	public class SetReplyPacket : SetPacket
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.SetReply;

		[JsonProperty("value")]
		public JToken Value { get; set; }

		[JsonProperty("original_value")]
		public JToken OriginalValue { get; set; }
	}
	public class StatusUpdatePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.StatusUpdate;

		[JsonProperty("status")]
		public ArchipelagoClientState Status { get; set; }
	}
	public class SyncPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Sync;
	}
	internal class UnknownPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Unknown;
	}
	public class UpdateHintPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.UpdateHint;

		[JsonProperty("player")]
		public int Player { get; set; }

		[JsonProperty("location")]
		public long Location { get; set; }

		[JsonProperty("status")]
		public HintStatus Status { get; set; }
	}
}
namespace Archipelago.MultiClient.Net.Models
{
	public struct Color : IEquatable<Color>
	{
		public static Color Red = new Color(byte.MaxValue, 0, 0);

		public static Color Green = new Color(0, 128, 0);

		public static Color Yellow = new Color(byte.MaxValue, byte.MaxValue, 0);

		public static Color Blue = new Color(0, 0, byte.MaxValue);

		public static Color Magenta = new Color(byte.MaxValue, 0, byte.MaxValue);

		public static Color Cyan = new Color(0, byte.MaxValue, byte.MaxValue);

		public static Color Black = new Color(0, 0, 0);

		public static Color White = new Color(byte.MaxValue, byte.MaxValue, byte.MaxValue);

		public static Color SlateBlue = new Color(106, 90, 205);

		public static Color Salmon = new Color(250, 128, 114);

		public static Color Plum = new Color(221, 160, 221);

		public byte R { get; set; }

		public byte G { get; set; }

		public byte B { get; set; }

		public Color(byte r, byte g, byte b)
		{
			R = r;
			G = g;
			B = b;
		}

		public override bool Equals(object obj)
		{
			if (obj is Color color && R == color.R && G == color.G)
			{
				return B == color.B;
			}
			return false;
		}

		public bool Equals(Color other)
		{
			if (R == other.R && G == other.G)
			{
				return B == other.B;
			}
			return false;
		}

		public override int GetHashCode()
		{
			return ((-1520100960 * -1521134295 + R.GetHashCode()) * -1521134295 + G.GetHashCode()) * -1521134295 + B.GetHashCode();
		}

		public static bool operator ==(Color left, Color right)
		{
			return left.Equals(right);
		}

		public static bool operator !=(Color left, Color right)
		{
			return !(left == right);
		}
	}
	public class DataPackage
	{
		[JsonProperty("games")]
		public Dictionary<string, GameData> Games { get; set; } = new Dictionary<string, GameData>();

	}
	public class DataStorageElement
	{
		internal DataStorageElementContext Context;

		internal List<OperationSpecification> Operations = new List<OperationSpecification>(0);

		internal DataStorageHelper.DataStorageUpdatedHandler Callbacks;

		internal Dictionary<string, JToken> AdditionalArguments = new Dictionary<string, JToken>(0);

		private JToken cachedValue;

		public event DataStorageHelper.DataStorageUpdatedHandler OnValueChanged
		{
			add
			{
				Context.AddHandler(Context.Key, value);
			}
			remove
			{
				Context.RemoveHandler(Context.Key, value);
			}
		}

		internal DataStorageElement(DataStorageElementContext context)
		{
			Context = context;
		}

		internal DataStorageElement(OperationType operationType, JToken value)
		{
			Operations = new List<OperationSpecification>(1)
			{
				new OperationSpecification
				{
					OperationType = operationType,
					Value = value
				}
			};
		}

		internal DataStorageElement(DataStorageElement source, OperationType operationType, JToken value)
			: this(source.Context)
		{
			Operations = source.Operations.ToList();
			Callbacks = source.Callbacks;
			AdditionalArguments = source.AdditionalArguments;
			Operations.Add(new OperationSpecification
			{
				OperationType = operationType,
				Value = value
			});
		}

		internal DataStorageElement(DataStorageElement source, Callback callback)
			: this(source.Context)
		{
			Operations = source.Operations.ToList();
			Callbacks = source.Callbacks;
			AdditionalArguments = source.AdditionalArguments;
			Callbacks = (DataStorageHelper.DataStorageUpdatedHandler)Delegate.Combine(Callbacks, callback.Method);
		}

		internal DataStorageElement(DataStorageElement source, AdditionalArgument additionalArgument)
			: this(source.Context)
		{
			Operations = source.Operations.ToList();
			Callbacks = source.Callbacks;
			AdditionalArguments = source.AdditionalArguments;
			AdditionalArguments[additionalArgument.Key] = additionalArgument.Value;
		}

		public static DataStorageElement operator ++(DataStorageElement a)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(1));
		}

		public static DataStorageElement operator --(DataStorageElement a)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(-1));
		}

		public static DataStorageElement operator +(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, string b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, JToken b)
		{
			return new DataStorageElement(a, OperationType.Add, b);
		}

		public static DataStorageElement operator +(DataStorageElement a, IEnumerable b)
		{
			return new DataStorageElement(a, OperationType.Add, (JToken)(object)JArray.FromObject((object)b));
		}

		public static DataStorageElement operator +(DataStorageElement a, OperationSpecification s)
		{
			return new DataStorageElement(a, s.OperationType, s.Value);
		}

		public static DataStorageElement operator +(DataStorageElement a, Callback c)
		{
			return new DataStorageElement(a, c);
		}

		public static DataStorageElement operator +(DataStorageElement a, AdditionalArgument arg)
		{
			return new DataStorageElement(a, arg);
		}

		public static DataStorageElement operator *(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator *(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator *(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator *(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator *(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator -(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b)));
		}

		public static DataStorageElement operator -(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b)));
		}

		public static DataStorageElement operator -(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0f - b)));
		}

		public static DataStorageElement operator -(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0.0 - b)));
		}

		public static DataStorageElement operator -(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / (double)b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / b)));
		}

		public static implicit operator DataStorageElement(bool b)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(b));
		}

		public static implicit operator DataStorageElement(int i)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(i));
		}

		public static implicit operator DataStorageElement(long l)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(l));
		}

		public static implicit operator DataStorageElement(decimal m)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(m));
		}

		public static implicit operator DataStorageElement(double d)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(d));
		}

		public static implicit operator DataStorageElement(float f)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(f));
		}

		public static implicit operator DataStorageElement(string s)
		{
			if (s != null)
			{
				return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(s));
			}
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull());
		}

		public static implicit operator DataStorageElement(JToken o)
		{
			return new DataStorageElement(OperationType.Replace, o);
		}

		public static implicit operator DataStorageElement(Array a)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)a));
		}

		public static implicit operator DataStorageElement(List<bool> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<int> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<long> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<decimal> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<double> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<float> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<string> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<object> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator bool(DataStorageElement e)
		{
			return RetrieveAndReturnBoolValue<bool>(e);
		}

		public static implicit operator bool?(DataStorageElement e)
		{
			return RetrieveAndReturnBoolValue<bool?>(e);
		}

		public static implicit operator int(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<int>(e);
		}

		public static implicit operator int?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<int?>(e);
		}

		public static implicit operator long(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<long>(e);
		}

		public static implicit operator long?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<long?>(e);
		}

		public static implicit operator float(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<float>(e);
		}

		public static implicit operator float?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<float?>(e);
		}

		public static implicit operator double(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<double>(e);
		}

		public static implicit operator double?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<double?>(e);
		}

		public static implicit operator decimal(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<decimal>(e);
		}

		public static implicit operator decimal?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<decimal?>(e);
		}

		public static implicit operator string(DataStorageElement e)
		{
			return RetrieveAndReturnStringValue(e);
		}

		public static implicit operator bool[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<bool[]>(e);
		}

		public static implicit operator int[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<int[]>(e);
		}

		public static implicit operator long[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<long[]>(e);
		}

		public static implicit operator decimal[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<decimal[]>(e);
		}

		public static implicit operator double[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<double[]>(e);
		}

		public static implicit operator float[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<float[]>(e);
		}

		public static implicit operator string[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<string[]>(e);
		}

		public static implicit operator object[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<object[]>(e);
		}

		public static implicit operator List<bool>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<bool>>(e);
		}

		public static implicit operator List<int>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<int>>(e);
		}

		public static implicit operator List<long>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<long>>(e);
		}

		public static implicit operator List<decimal>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<decimal>>(e);
		}

		public static implicit operator List<double>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<double>>(e);
		}

		public static implicit operator List<float>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<float>>(e);
		}

		public static implicit operator List<string>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<string>>(e);
		}

		public static implicit operator List<object>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<object>>(e);
		}

		public static implicit operator Array(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<Array>(e);
		}

		public static implicit operator JArray(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<JArray>(e);
		}

		public static implicit operator JToken(DataStorageElement e)
		{
			return e.Context.GetData(e.Context.Key);
		}

		public static DataStorageElement operator +(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.Parse(b.ToString()));
		}

		public static DataStorageElement operator *(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.Parse(b.ToString()));
		}

		public static DataStorageElement operator %(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.Parse(b.ToString()));
		}

		public static DataStorageElement operator ^(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.Parse(b.ToString()));
		}

		public static DataStorageElement operator -(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.Parse((-b).ToString()));
		}

		public static DataStorageElement operator /(DataStorageElement a, BigInteger b)
		{
			throw new InvalidOperationException("DataStorage[Key] / BigInterger is not supported, due to loss of precision when using integer division");
		}

		public static implicit operator DataStorageElement(BigInteger bi)
		{
			return new DataStorageElement(OperationType.Replace, JToken.Parse(bi.ToString()));
		}

		public static implicit operator BigInteger(DataStorageElement e)
		{
			return RetrieveAndReturnBigIntegerValue<BigInteger>(e);
		}

		public static implicit operator BigInteger?(DataStorageElement e)
		{
			return RetrieveAndReturnBigIntegerValue<BigInteger?>(e);
		}

		private static T RetrieveAndReturnBigIntegerValue<T>(DataStorageElement e)
		{
			if (e.cachedValue != null)
			{
				if (!BigInteger.TryParse(((object)e.cachedValue).ToString(), out var result))
				{
					return default(T);
				}
				return (T)Convert.ChangeType(result, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T));
			}
			BigInteger result2;
			BigInteger? bigInteger = (BigInteger.TryParse(((object)e.Context.GetData(e.Context.Key)).ToString(), out result2) ? new BigInteger?(result2) : null);
			if (!bigInteger.HasValue && !IsNullable<T>())
			{
				bigInteger = Activator.CreateInstance<BigInteger>();
			}
			foreach (OperationSpecification operation in e.Operations)
			{
				if (operation.OperationType == OperationType.Floor || operation.OperationType == OperationType.Ceil)
				{
					continue;
				}
				if (!BigInteger.TryParse(((object)operation.Value).ToString(), NumberStyles.AllowLeadingSign, null, out var result3))
				{
					throw new InvalidOperationException($"DataStorage[Key] cannot be converted to BigInterger as its value its not an integer number, value: {operation.Value}");
				}
				switch (operation.OperationType)
				{
				case OperationType.Replace:
					bigInteger = result3;
					break;
				case OperationType.Add:
					bigInteger += result3;
					break;
				case OperationType.Mul:
					bigInteger *= result3;
					break;
				case OperationType.Mod:
					bigInteger %= result3;
					break;
				case OperationType.Pow:
					bigInteger = BigInteger.Pow(bigInteger.Value, (int)operation.Value);
					break;
				case OperationType.Max:
				{
					BigInteger value = result3;
					BigInteger? bigInteger2 = bigInteger;
					if (value > bigInteger2)
					{
						bigInteger = result3;
					}
					break;
				}
				case OperationType.Min:
				{
					BigInteger value = result3;
					BigInteger? bigInteger2 = bigInteger;
					if (value < bigInteger2)
					{
						bigInteger = result3;
					}
					break;
				}
				case OperationType.Xor:
					bigInteger ^= result3;
					break;
				case OperationType.Or:
					bigInteger |= result3;
					break;
				case OperationType.And:
					bigInteger &= result3;
					break;
				case OperationType.LeftShift:
					bigInteger <<= (int)operation.Value;
					break;
				case OperationType.RightShift:
					bigInteger >>= (int)operation.Value;
					break;
				}
			}
			e.cachedValue = JToken.Parse(bigInteger.ToString());
			if (!bigInteger.HasValue)
			{
				return default(T);
			}
			return (T)Convert.ChangeType(bigInteger.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T));
		}

		public void Initialize(JToken value)
		{
			Context.Initialize(Context.Key, value);
		}

		public void Initialize(IEnumerable value)
		{
			Context.Initialize(Context.Key, (JToken)(object)JArray.FromObject((object)value));
		}

		public Task<T> GetAsync<T>()
		{
			return GetAsync().ContinueWith((Task<JToken> r) => r.Result.ToObject<T>());
		}

		public Task<JToken> GetAsync()
		{
			return Context.GetAsync(Context.Key);
		}

		private static T RetrieveAndReturnArrayValue<T>(DataStorageElement e)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Invalid comparison between Unknown and I4
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Invalid comparison between Unknown and I4
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (e.cachedValue != null)
			{
				return ((JToken)(JArray)e.cachedValue).ToObject<T>();
			}
			JArray val = (JArray)(((object)e.Context.GetData(e.Context.Key).ToObject<JArray>()) ?? ((object)new JArray()));
			foreach (OperationSpecification operation in e.Operations)
			{
				switch (operation.OperationType)
				{
				case OperationType.Add:
					if ((int)operation.Value.Type != 2)
					{
						throw new InvalidOperationException($"Cannot perform operation {1} on Array value, with a non Array value: {operation.Value}");
					}
					((JContainer)val).Merge((object)operation.Value);
					break;
				case OperationType.Replace:
					if ((int)operation.Value.Type != 2)
					{
						throw new InvalidOperationException($"Cannot replace Array value, with a non Array value: {operation.Value}");
					}
					val = (JArray)(((object)operation.Value.ToObject<JArray>()) ?? ((object)new JArray()));
					break;
				default:
					throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on Array value");
				}
			}
			e.cachedValue = (JToken)(object)val;
			return ((JToken)val).ToObject<T>();
		}

		private static string RetrieveAndReturnStringValue(DataStorageElement e)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Invalid comparison between Unknown and I4
			if (e.cachedValue != null)
			{
				return (string)e.cachedValue;
			}
			JToken val = e.Context.GetData(e.Context.Key);
			string text = (((int)val.Type == 10) ? null : ((object)val).ToString());
			foreach (OperationSpecification operation in e.Operations)
			{
				switch (operation.OperationType)
				{
				case OperationType.Add:
					text += (string)operation.Value;
					break;
				case OperationType.Mul:
					if ((int)operation.Value.Type != 6)
					{
						throw new InvalidOperationException($"Cannot perform operation {2} on string value, with a non interger value: {operation.Value}");
					}
					text = string.Concat(Enumerable.Repeat(text, (int)operation.Value));
					break;
				case OperationType.Replace:
					text = (string)operation.Value;
					break;
				default:
					throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on string value");
				}
			}
			if (text == null)
			{
				e.cachedValue = (JToken)(object)JValue.CreateNull();
			}
			else
			{
				e.cachedValue = JToken.op_Implicit(text);
			}
			return (string)e.cachedValue;
		}

		private static T RetrieveAndReturnBoolValue<T>(DataStorageElement e)
		{
			if (e.cachedValue != null)
			{
				return e.cachedValue.ToObject<T>();
			}
			bool? flag = e.Context.GetData(e.Context.Key).ToObject<bool?>() ?? ((bool?)Activator.CreateInstance(typeof(T)));
			foreach (OperationSpecification operation in e.Operations)
			{
				if (operation.OperationType == OperationType.Replace)
				{
					flag = (bool?)operation.Value;
					continue;
				}
				throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on boolean value");
			}
			e.cachedValue = JToken.op_Implicit(flag);
			if (!flag.HasValue)
			{
				return default(T);
			}
			return (T)Convert.ChangeType(flag.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T));
		}

		private static T RetrieveAndReturnDecimalValue<T>(DataStorageElement e)
		{
			if (e.cachedValue != null)
			{
				return e.cachedValue.ToObject<T>();
			}
			decimal? num = e.Context.GetData(e.Context.Key).ToObject<decimal?>();
			if (!num.HasValue && !IsNullable<T>())
			{
				num = Activator.CreateInstance<decimal>();
			}
			foreach (OperationSpecification operation in e.Operations)
			{
				switch (operation.OperationType)
				{
				case OperationType.Replace:
					num = (decimal)operation.Value;
					break;
				case OperationType.Add:
					num += (decimal?)(decimal)operation.Value;
					break;
				case OperationType.Mul:
					num *= (decimal?)(decimal)operation.Value;
					break;
				case OperationType.Mod:
					num %= (decimal?)(decimal)operation.Value;
					break;
				case OperationType.Pow:
					num = (decimal)Math.Pow((double)num.Value, (double)operation.Value);
					break;
				case OperationType.Max:
					num = Math.Max(num.Value, (decimal)operation.Value);
					break;
				case OperationType.Min:
					num = Math.Min(num.Value, (decimal)operation.Value);
					break;
				case OperationType.Xor:
					num = (long)num.Value ^ (long)operation.Value;
					break;
				case OperationType.Or:
					num = (long)num.Value | (long)operation.Value;
					break;
				case OperationType.And:
					num = (long)num.Value & (long)operation.Value;
					break;
				case OperationType.LeftShift:
					num = (long)num.Value << (int)operation.Value;
					break;
				case OperationType.RightShift:
					num = (long)num.Value >> (int)operation.Value;
					break;
				case OperationType.Floor:
					num = Math.Floor(num.Value);
					break;
				case OperationType.Ceil:
					num = Math.Ceiling(num.Value);
					break;
				}
			}
			e.cachedValue = JToken.op_Implicit(num);
			if (!num.HasValue)
			{
				return default(T);
			}
			return (T)Convert.ChangeType(num.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T));
		}

		private static bool IsNullable<T>()
		{
			if (typeof(T).IsGenericType)
			{
				return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition();
			}
			return false;
		}

		public T To<T>()
		{
			if (Operations.Count != 0)
			{
				throw new InvalidOperationException("DataStorageElement.To<T>() cannot be used together with other operations on the DataStorageElement");
			}
			return Context.GetData(Context.Key).ToObject<T>();
		}

		public override string ToString()
		{
			return (Context?.ToString() ?? "(null)") + ", (" + ListOperations() + ")";
		}

		private string ListOperations()
		{
			if (Operations != null)
			{
				return string.Join(", ", Operations.Select((OperationSpecification o) => o.ToString()).ToArray());
			}
			return "none";
		}
	}
	internal class DataStorageElementContext
	{
		internal string Key { get; set; }

		internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> AddHandler { get; set; }

		internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> RemoveHandler { get; set; }

		internal Func<string, JToken> GetData { get; set; }

		internal Action<string, JToken> Initialize { get; set; }

		internal Func<string, Task<JToken>> GetAsync { get; set; }

		public override string ToString()
		{
			return "Key: " + Key;
		}
	}
	public class GameData
	{
		[JsonProperty("location_name_to_id")]
		public Dictionary<string, long> LocationLookup { get; set; } = new Dictionary<string, long>();


		[JsonProperty("item_name_to_id")]
		public Dictionary<string, long> ItemLookup { get; set; } = new Dictionary<string, long>();


		[Obsolete("use Checksum instead")]
		[JsonProperty("version")]
		public int Version { get; set; }

		[JsonProperty("checksum")]
		public string Checksum { get; set; }
	}
	public class Hint
	{
		[JsonProperty("receiving_player")]
		public int ReceivingPlayer { get; set; }

		[JsonProperty("finding_player")]
		public int FindingPlayer { get; set; }

		[JsonProperty("item")]
		public long ItemId { get; set; }

		[JsonProperty("location")]
		public long LocationId { get; set; }

		[JsonProperty("item_flags")]
		public ItemFlags ItemFlags { get; set; }

		[JsonProperty("found")]
		public bool Found { get; set; }

		[JsonProperty("entrance")]
		public string Entrance { get; set; }

		[JsonProperty("status")]
		public HintStatus Status { get; set; }
	}
	public class ItemInfo
	{
		private readonly IItemInfoResolver itemInfoResolver;

		public long ItemId { get; }

		public long LocationId { get; }

		public PlayerInfo Player { get; }

		public ItemFlags Flags { get; }

		public string ItemName => itemInfoResolver.GetItemName(ItemId, ItemGame);

		public string ItemDisplayName => ItemName ?? $"Item: {ItemId}";

		public string LocationName => itemInfoResolver.GetLocationName(LocationId, LocationGame);

		public string LocationDisplayName => LocationName ?? $"Location: {LocationId}";

		public string ItemGame { get; }

		public string LocationGame { get; }

		public ItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, PlayerInfo player)
		{
			this.itemInfoResolver = itemInfoResolver;
			ItemGame = receiverGame;
			LocationGame = senderGame;
			ItemId = item.Item;
			LocationId = item.Location;
			Flags = item.Flags;
			Player = player;
		}

		public SerializableItemInfo ToSerializable()
		{
			return new SerializableItemInfo
			{
				IsScout = (GetType() == typeof(ScoutedItemInfo)),
				ItemId = ItemId,
				LocationId = LocationId,
				PlayerSlot = Player,
				Player = Player,
				Flags = Flags,
				ItemGame = ItemGame,
				ItemName = ItemName,
				LocationGame = LocationGame,
				LocationName = LocationName
			};
		}
	}
	public class ScoutedItemInfo : ItemInfo
	{
		public new PlayerInfo Player => base.Player;

		public bool IsReceiverRelatedToActivePlayer { get; }

		public ScoutedItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, IPlayerHelper players, PlayerInfo player)
			: base(item, receiverGame, senderGame, itemInfoResolver, player)
		{
			IsReceiverRelatedToActivePlayer = (players.ActivePlayer ?? new PlayerInfo()).IsRelatedTo(player);
		}
	}
	public class JsonMessagePart
	{
		[JsonProperty("type")]
		[JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })]
		public JsonMessagePartType? Type { get; set; }

		[JsonProperty("color")]
		[JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })]
		public JsonMessagePartColor? Color { get; set; }

		[JsonProperty("text")]
		public string Text { get; set; }

		[JsonProperty("player")]
		public int? Player { get; set; }

		[JsonProperty("flags")]
		public ItemFlags? Flags { get; set; }

		[JsonProperty("hint_status")]
		public HintStatus? HintStatus { get; set; }
	}
	public struct NetworkItem
	{
		[JsonProperty("item")]
		public long Item { get; set; }

		[JsonProperty("location")]
		public long Location { get; set; }

		[JsonProperty("player")]
		public int Player { get; set; }

		[JsonProperty("flags")]
		public ItemFlags Flags { get; set; }
	}
	public struct NetworkPlayer
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("alias")]
		public string Alias { get; set; }

		[JsonProperty("name")]
		public string Name { get; set; }
	}
	public struct NetworkSlot
	{
		[JsonProperty("name")]
		public string Name { get; set; }

		[JsonProperty("game")]
		public string Game { get; set; }

		[JsonProperty("type")]
		public SlotType Type { get; set; }

		[JsonProperty("group_members")]
		public int[] GroupMembers { get; set; }
	}
	public class NetworkVersion
	{
		[JsonProperty("major")]
		public int Major { get; set; }

		[JsonProperty("minor")]
		public int Minor { get; set; }

		[JsonProperty("build")]
		public int Build { get; set; }

		[JsonProperty("class")]
		public string Class => "Version";

		public NetworkVersion()
		{
		}

		public NetworkVersion(int major, int minor, int build)
		{
			Major = major;
			Minor = minor;
			Build = build;
		}

		public NetworkVersion(Version version)
		{
			Major = version.Major;
			Minor = version.Minor;
			Build = version.Build;
		}

		public Version ToVersion()
		{
			return new Version(Major, Minor, Build);
		}
	}
	public class OperationSpecification
	{
		[JsonProperty("operation")]
		[JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })]
		public OperationType OperationType;

		[JsonProperty("value")]
		public JToken Value { get; set; }

		public override string ToString()
		{
			return $"{OperationType}: {Value}";
		}
	}
	public static class Operation
	{
		public static OperationSpecification Min(int i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(float i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(double i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(decimal i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(JToken i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = i
			};
		}

		public static OperationSpecification Min(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification Max(int i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(float i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(double i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(decimal i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(JToken i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = i
			};
		}

		public static OperationSpecification Max(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification Remove(JToken value)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Remove,
				Value = value
			};
		}

		public static OperationSpecification Pop(int value)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Pop,
				Value = JToken.op_Implicit(value)
			};
		}

		public static OperationSpecification Pop(JToken value)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Pop,
				Value = value
			};
		}

		public static OperationSpecification Update(IDictionary dictionary)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Update,
				Value = (JToken)(object)JObject.FromObject((object)dictionary)
			};
		}

		public static OperationSpecification Floor()
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Floor,
				Value = null
			};
		}

		public static OperationSpecification Ceiling()
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Ceil,
				Value = null
			};
		}
	}
	public static class Bitwise
	{
		public static OperationSpecification Xor(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Xor,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Xor(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Xor,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification Or(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Or,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Or(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Or,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification And(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.And,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification And(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.And,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification LeftShift(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.LeftShift,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification RightShift(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.RightShift,
				Value = JToken.op_Implicit(i)
			};
		}
	}
	public class Callback
	{
		internal DataStorageHelper.DataStorageUpdatedHandler Method { get; set; }

		private Callback()
		{
		}

		public static Callback Add(DataStorageHelper.DataStorageUpdatedHandler callback)
		{
			return new Callback
			{
				Method = callback
			};
		}
	}
	public class AdditionalArgument
	{
		internal string Key { get; set; }

		internal JToken Value { get; set; }

		private AdditionalArgument()
		{
		}

		public static AdditionalArgument Add(string name, JToken value)
		{
			return new AdditionalArgument
			{
				Key = name,
				Value = value
			};
		}
	}
	public class MinimalSerializableItemInfo
	{
		public long ItemId { get; set; }

		public long LocationId { get; set; }

		public int PlayerSlot { get; set; }

		public ItemFlags Flags { get; set; }

		public string ItemGame { get; set; }

		public string LocationGame { get; set; }
	}
	public class SerializableItemInfo : MinimalSerializableItemInfo
	{
		public bool IsScout { get; set; }

		public PlayerInfo Player { get; set; }

		public string ItemName { get; set; }

		public string LocationName { get; set; }

		[JsonIgnore]
		public string ItemDisplayName => ItemName ?? $"Item: {base.ItemId}";

		[JsonIgnore]
		public string LocationDisplayName => LocationName ?? $"Location: {base.LocationId}";

		public string ToJson(bool full = false)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			MinimalSerializableItemInfo minimalSerializableItemInfo = this;
			if (!full)
			{
				minimalSerializableItemInfo = new MinimalSerializableItemInfo
				{
					ItemId = base.ItemId,
					LocationId = base.LocationId,
					PlayerSlot = base.PlayerSlot,
					Flags = base.Flags
				};
				if (IsScout)
				{
					minimalSerializableItemInfo.ItemGame = base.ItemGame;
				}
				else
				{
					minimalSerializableItemInfo.LocationGame = base.LocationGame;
				}
			}
			JsonSerializerSettings val = new JsonSerializerSettings
			{
				NullValueHandling = (NullValueHandling)1,
				Formatting = (Formatting)0
			};
			return JsonConvert.SerializeObject((object)minimalSerializableItemInfo, val);
		}

		public static SerializableItemInfo FromJson(string json, IArchipelagoSession session = null)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			ItemInfoStreamingContext additional = ((session != null) ? new ItemInfoStreamingContext
			{
				Items = session.Items,
				Locations = session.Locations,
				PlayerHelper = session.Players,
				ConnectionInfo = session.ConnectionInfo
			} : null);
			JsonSerializerSettings val = new JsonSerializerSettings
			{
				Context = new StreamingContext(StreamingContextStates.Other, additional)
			};
			return JsonConvert.DeserializeObject<SerializableItemInfo>(json, val);
		}

		[OnDeserialized]
		internal void OnDeserializedMethod(StreamingContext streamingContext)
		{
			if (base.ItemGame == null && base.LocationGame != null)
			{
				IsScout = false;
			}
			else if (base.ItemGame != null && base.LocationGame == null)
			{
				IsScout = true;
			}
			if (streamingContext.Context is ItemInfoStreamingContext itemInfoStreamingContext)
			{
				if (IsScout && base.LocationGame == null)
				{
					base.LocationGame = itemInfoStreamingContext.ConnectionInfo.Game;
				}
				else if (!IsScout && base.ItemGame == null)
				{
					base.ItemGame = itemInfoStreamingContext.ConnectionInfo.Game;
				}
				if (ItemName == null)
				{
					ItemName = itemInfoStreamingContext.Items.GetItemName(base.ItemId, base.ItemGame);
				}
				if (LocationName == null)
				{
					LocationName = itemInfoStreamingContext.Locations.GetLocationNameFromId(base.LocationId, base.LocationGame);
				}
				if (Player == null)
				{
					Player = itemInfoStreamingContext.PlayerHelper.GetPlayerInfo(base.PlayerSlot);
				}
			}
		}
	}
	internal class ItemInfoStreamingContext
	{
		public IReceivedItemsHelper Items { get; set; }

		public ILocationCheckHelper Locations { get; set; }

		public IPlayerHelper PlayerHelper { get; set; }

		public IConnectionInfoProvider ConnectionInfo { get; set; }
	}
}
namespace Archipelago.MultiClient.Net.MessageLog.Parts
{
	public class EntranceMessagePart : MessagePart
	{
		internal EntranceMessagePart(JsonMessagePart messagePart)
			: base(MessagePartType.Entrance, messagePart, Archipelago.MultiClient.Net.Colors.PaletteColor.Blue)
		{
			base.Text = messagePart.Text;
		}
	}
	public class HintStatusMessagePart : MessagePart
	{
		internal HintStatusMessagePart(JsonMessagePart messagePart)
			: base(MessagePartType.HintStatus, messagePart)
		{
			base.Text = messagePart.Text;
			if (messagePart.HintStatus.HasValue)
			{
				base.PaletteColor = ColorUtils.GetColor(messagePart.HintStatus.Value);
			}
		}
	}
	public class ItemMessagePart : MessagePart
	{
		public ItemFlags Flags { get; }

		public long ItemId { get; }

		public int Player { get; }

		internal ItemMessagePart(IPlayerHelper players, IItemInfoResolver items, JsonMessagePart part)
			: base(MessagePartType.Item, part)
		{
			Flags = part.Flags.GetValueOrDefault();
			base.PaletteColor = ColorUtils.GetColor(Flags);
			Player = part.Player.GetValueOrDefault();
			string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game;
			JsonMessagePartType? type = part.Type;
			if (type.HasValue)
			{
				switch (type.GetValueOrDefault())
				{
				case JsonMessagePartType.ItemId:
					ItemId = long.Parse(part.Text);
					base.Text = items.GetItemName(ItemId, game) ?? $"Item: {ItemId}";
					break;
				case JsonMessagePartType.ItemName:
					ItemId = 0L;
					base.Text = part.Text;
					break;
				}
			}
		}
	}
	public class LocationMessagePart : MessagePart
	{
		public long LocationId { get; }

		public int Player { get; }

		internal LocationMessagePart(IPlayerHelper players, IItemInfoResolver itemInfoResolver, JsonMessagePart part)
			: base(MessagePartType.Location, part, Archipelago.MultiClient.Net.Colors.PaletteColor.Green)
		{
			Player = part.Player.GetValueOrDefault();
			string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game;
			JsonMessagePartType? type = part.Type;
			if (type.HasValue)
			{
				switch (type.GetValueOrDefault())
				{
				case JsonMessagePartType.LocationId:
					LocationId = long.Parse(part.Text);
					base.Text = itemInfoResolver.GetLocationName(LocationId, game) ?? $"Location: {LocationId}";
					break;
				case JsonMessagePartType.LocationName:
					LocationId = itemInfoResolver.GetLocationId(part.Text, game);
					base.Text = part.Text;
					break;
				}
			}
		}
	}
	public class MessagePart
	{
		public string Text { get; internal set; }

		public MessagePartType Type { get; internal set; }

		public Color Color => GetColor(BuiltInPalettes.Dark);

		public PaletteColor? PaletteColor { get; protected set; }

		public bool IsBackgroundColor { get; internal set; }

		internal MessagePart(MessagePartType type, JsonMessagePart messagePart, PaletteColor? color = null)
		{
			Type = type;
			Text = messagePart.Text;
			if (color.HasValue)
			{
				PaletteColor = color.Value;
			}
			else if (messagePart.Color.HasValue)
			{
				PaletteColor = ColorUtils.GetColor(messagePart.Color.Value);
				IsBackgroundColor = messagePart.Color.Value >= JsonMessagePartColor.BlackBg;
			}
			else
			{
				PaletteColor = null;
			}
		}

		public T GetColor<T>(Palette<T> palette)
		{
			return palette[PaletteColor];
		}

		public override string ToString()
		{
			return Text;
		}
	}
	public enum MessagePartType
	{
		Text,
		Player,
		Item,
		Location,
		Entrance,
		HintStatus
	}
	public class PlayerMessagePart : MessagePart
	{
		public bool IsActivePlayer { get; }

		public int SlotId { get; }

		internal PlayerMessagePart(IPlayerHelper players, IConnectionInfoProvider connectionInfo, JsonMessagePart part)
			: base(MessagePartType.Player, part)
		{
			switch (part.Type)
			{
			case JsonMessagePartType.PlayerId:
				SlotId = int.Parse(part.Text);
				IsActivePlayer = SlotId == connectionInfo.Slot;
				base.Text = players.GetPlayerAlias(SlotId) ?? $"Player {SlotId}";
				break;
			case JsonMessagePartType.PlayerName:
				SlotId = 0;
				IsActivePlayer = false;
				base.Text = part.Text;
				break;
			}
			base.PaletteColor = (IsActivePlayer ? Archipelago.MultiClient.Net.Colors.PaletteColor.Magenta : Archipelago.MultiClient.Net.Colors.PaletteColor.Yellow);
		}
	}
}
namespace Archipelago.MultiClient.Net.MessageLog.Messages
{
	public class AdminCommandResultLogMessage : LogMessage
	{
		internal AdminCommandResultLogMessage(MessagePart[] parts)
			: base(parts)
		{
		}
	}
	public class ChatLogMessage : PlayerSpecificLogMessage
	{
		public string Message { get; }

		internal ChatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string message)
			: base(parts, players, team, slot)
		{
			Message = message;
		}
	}
	public class CollectLogMessage : PlayerSpecificLogMessage
	{
		internal CollectLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts, players, team, slot)
		{
		}
	}
	public class CommandResultLogMessage : LogMessage
	{
		internal CommandResultLogMessage(MessagePart[] parts)
			: base(parts)
		{
		}
	}
	public class CountdownLogMessage : LogMessage
	{
		public int RemainingSeconds { get; }

		internal CountdownLogMessage(MessagePart[] parts, int remainingSeconds)
			: base(parts)
		{
			RemainingSeconds = remainingSeconds;
		}
	}
	public class GoalLogMessage : PlayerSpecificLogMessage
	{
		internal GoalLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts, players, team, slot)
		{
		}
	}
	public class HintItemSendLogMessage : ItemSendLogMessage
	{
		public bool IsFound { get; }

		internal HintItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, bool found, IItemInfoResolver itemInfoResolver)
			: base(parts, players, receiver, sender, item, itemInfoResolver)
		{
			IsFound = found;
		}
	}
	public class ItemCheatLogMessage : ItemSendLogMessage
	{
		internal ItemCheatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, NetworkItem item, IItemInfoResolver itemInfoResolver)
			: base(parts, players, slot, 0, item, team, itemInfoResolver)
		{
		}
	}
	public class ItemSendLogMessage : LogMessage
	{
		private PlayerInfo ActivePlayer { get; }

		public PlayerInfo Receiver { get; }

		public PlayerInfo Sender { get; }

		public bool IsReceiverTheActivePlayer => Receiver == ActivePlayer;

		public bool IsSenderTheActivePlayer => Sender == ActivePlayer;

		public bool IsRelatedToActivePlayer
		{
			get
			{
				if (!ActivePlayer.IsRelatedTo(Receiver))
				{
					return ActivePlayer.IsRelatedTo(Sender);
				}
				return true;
			}
		}

		public ItemInfo Item { get; }

		internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, IItemInfoResolver itemInfoResolver)
			: this(parts, players, receiver, sender, item, players.ActivePlayer.Team, itemInfoResolver)
		{
		}

		internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, int team, IItemInfoResolver itemInfoResolver)
			: base(parts)
		{
			ActivePlayer = players.ActivePlayer ?? new PlayerInfo();
			Receiver = players.GetPlayerInfo(team, receiver) ?? new PlayerInfo();
			Sender = players.GetPlayerInfo(team, sender) ?? new PlayerInfo();
			PlayerInfo player = players.GetPlayerInfo(team, item.Player) ?? new PlayerInfo();
			Item = new ItemInfo(item, Receiver.Game, Sender.Game, itemInfoResolver, player);
		}
	}
	public class JoinLogMessage : PlayerSpecificLogMessage
	{
		public string[] Tags { get; }

		internal JoinLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags)
			: base(parts, players, team, slot)
		{
			Tags = tags;
		}
	}
	public class LeaveLogMessage : PlayerSpecificLogMessage
	{
		internal LeaveLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts, players, team, slot)
		{
		}
	}
	public class LogMessage
	{
		public MessagePart[] Parts { get; }

		internal LogMessage(MessagePart[] parts)
		{
			Parts = parts;
		}

		public override string ToString()
		{
			if (Parts.Length == 1)
			{
				return Parts[0].Text;
			}
			StringBuilder stringBuilder = new StringBuilder();
			MessagePart[] parts = Parts;
			foreach (MessagePart messagePart in parts)
			{
				stringBuilder.Append(messagePart.Text);
			}
			return stringBuilder.ToString();
		}
	}
	public abstract class PlayerSpecificLogMessage : LogMessage
	{
		private PlayerInfo ActivePlayer { get; }

		public PlayerInfo Player { get; }

		public bool IsActivePlayer => Player == ActivePlayer;

		public bool IsRelatedToActivePlayer => ActivePlayer.IsRelatedTo(Player);

		internal PlayerSpecificLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts)
		{
			ActivePlayer = players.ActivePlayer ?? new PlayerInfo();
			Player = players.GetPlayerInfo(team, slot) ?? new PlayerInfo();
		}
	}
	public class ReleaseLogMessage : PlayerSpecificLogMessage
	{
		internal ReleaseLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts, players, team, slot)
		{
		}
	}
	public class ServerChatLogMessage : LogMessage
	{
		public string Message { get; }

		internal ServerChatLogMessage(MessagePart[] parts, string message)
			: base(parts)
		{
			Message = message;
		}
	}
	public class TagsChangedLogMessage : PlayerSpecificLogMessage
	{
		public string[] Tags { get; }

		internal TagsChangedLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags)
			: base(parts, players, team, slot)
		{
			Tags = tags;
		}
	}
	public class TutorialLogMessage : LogMessage
	{
		internal TutorialLogMessage(MessagePart[] parts)
			: base(parts)
		{
		}
	}
}
namespace Archipelago.MultiClient.Net.Helpers
{
	public class ArchipelagoSocketHelper : BaseArchipelagoSocketHelper<ClientWebSocket>, IArchipelagoSocketHelper
	{
		public Uri Uri { get; }

		internal ArchipelagoSocketHelper(Uri hostUri)
			: base(CreateWebSocket(), 1024)
		{
			Uri = hostUri;
		}

		private static ClientWebSocket CreateWebSocket()
		{
			ClientWebSocket clientWebSocket = new ClientWebSocket();
			clientWebSocket.Options.DangerousDeflateOptions = new WebSocketDeflateOptions();
			return clientWebSocket;
		}

		public async Task ConnectAsync()
		{
			await ConnectToProvidedUri(Uri);
			StartPolling();
		}

		private async Task ConnectToProvidedUri(Uri uri)
		{
			if (uri.Scheme != "unspecified")
			{
				try
				{
					await Socket.ConnectAsync(uri, CancellationToken.None);
					return;
				}
				catch (Exception e)
				{
					OnError(e);
					throw;
				}
			}
			List<Exception> errors = new List<Exception>(0);
			try
			{
				await Socket.ConnectAsync(uri.AsWss(), CancellationToken.None);
				if (Socket.State == WebSocketState.Open)
				{
					return;
				}
			}
			catch (Exception item)
			{
				errors.Add(item);
				Socket = CreateWebSocket();
			}
			try
			{
				await Socket.ConnectAsync(uri.AsWs(), CancellationToken.None);
			}
			catch (Exception item2)
			{
				errors.Add(item2);
				OnError(new AggregateException(errors));
				throw;
			}
		}
	}
	public class BaseArchipelagoSocketHelper<T> where T : WebSocket
	{
		private static readonly ArchipelagoPacketConverter Converter = new ArchipelagoPacketConverter();

		private readonly BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>> sendQueue = new BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>>();

		internal T Socket;

		private readonly int bufferSize;

		public bool Connected
		{
			get
			{
				if (Socket.State != WebSocketState.Open)
				{
					return Socket.State == WebSocketState.CloseReceived;
				}
				return true;
			}
		}

		public event ArchipelagoSocketHelperDelagates.PacketReceivedHandler PacketReceived;

		public event ArchipelagoSocketHelperDelagates.PacketsSentHandler PacketsSent;

		public event ArchipelagoSocketHelperDelagates.ErrorReceivedHandler ErrorReceived;

		public event ArchipelagoSocketHelperDelagates.SocketClosedHandler SocketClosed;

		public event ArchipelagoSocketHelperDelagates.SocketOpenedHandler SocketOpened;

		internal BaseArchipelagoSocketHelper(T socket, int bufferSize = 1024)
		{
			Socket = socket;
			this.bufferSize = bufferSize;
		}

		internal void StartPolling()
		{
			if (this.SocketOpened != null)
			{
				this.SocketOpened();
			}
			Task.Run((Func<Task?>)PollingLoop);
			Task.Run((Func<Task?>)SendLoop);
		}

		private async Task PollingLoop()
		{
			byte[] buffer = new byte[bufferSize];
			while (Socket.State == WebSocketState.Open)
			{
				string message = null;
				try
				{
					message = await ReadMessageAsync(buffer);
				}
				catch (Exception e)
				{
					OnError(e);
				}
				OnMessageReceived(message);
			}
		}

		private async Task SendLoop()
		{
			while (Socket.State == WebSocketState.Open)
			{
				try
				{
					await HandleSendBuffer();
				}
				catch (Exception e)
				{
					OnError(e);
				}
				await Task.Delay(20);
			}
		}

		private async Task<string> ReadMessageAsync(byte[] buffer)
		{
			using MemoryStream readStream = new MemoryStream(buffer.Length);
			WebSocketReceiveResult result;
			do
			{
				result = await Socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
				if (result.MessageType == WebSocketMessageType.Close)
				{
					try
					{
						await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
					}
					catch
					{
					}
					OnSocketClosed();
				}
				else
				{
					readStream.Write(buffer, 0, result.Count);
				}
			}
			while (!result.EndOfMessage);
			return Encoding.UTF8.GetString(readStream.ToArray());
		}

		public async Task DisconnectAsync()
		{
			await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closure requested by client", CancellationToken.None);
			OnSocketClosed();
		}

		public void SendPacket(ArchipelagoPacketBase packet)
		{
			SendMultiplePackets(new List<ArchipelagoPacketBase> { packet });
		}

		public void SendMultiplePackets(List<ArchipelagoPacketBase> packets)
		{
			SendMultiplePackets(packets.ToArray());
		}

		public void SendMultiplePackets(params ArchipelagoPacketBase[] packets)
		{
			SendMultiplePacketsAsync(packets).Wait();
		}

		public Task SendPacketAsync(ArchipelagoPacketBase packet)
		{
			return SendMultiplePacketsAsync(new List<ArchipelagoPacketBase> { packet });
		}

		public Task SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets)
		{
			return SendMultiplePacketsAsync(packets.ToArray());
		}

		public Task SendMultiplePacketsAsync(params ArchipelagoPacketBase[] packets)
		{
			TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
			foreach (ArchipelagoPacketBase item in packets)
			{
				sendQueue.Add(new Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>(item, taskCompletionSource));
			}
			return taskCompletionSource.Task;
		}

		private async Task HandleSendBuffer()
		{
			List<ArchipelagoPacketBase> list = new List<ArchipelagoPacketBase>();
			List<TaskCompletionSource<bool>> tasks = new List<TaskCompletionSource<bool>>();
			Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> tuple = sendQueue.Take();
			list.Add(tuple.Item1);
			tasks.Add(tuple.Item2);
			Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> item;
			while (sendQueue.TryTake(out item))
			{
				list.Add(item.Item1);
				tasks.Add(item.Item2);
			}
			if (!list.Any())
			{
				return;
			}
			if (Socket.State != WebSocketState.Open)
			{
				throw new ArchipelagoSocketClosedException();
			}
			ArchipelagoPacketBase[] packets = list.ToArray();
			string s = JsonConvert.SerializeObject((object)packets);
			byte[] messageBuffer = Encoding.UTF8.GetBytes(s);
			int messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / (double)bufferSize);
			for (int i = 0; i < messagesCount; i++)
			{
				int num = bufferSize * i;
				int num2 = bufferSize;
				bool endOfMessage = i + 1 == messagesCount;
				if (num2 * (i + 1) > messageBuffer.Length)
				{
					num2 = messageBuffer.Length - num;
				}
				await Socket.SendAsync(new ArraySegment<byte>(messageBuffer, num, num2), WebSocketMessageType.Text, endOfMessage, CancellationToken.None);
			}
			foreach (TaskCompletionSource<bool> item2 in tasks)
			{
				item2.TrySetResult(result: true);
			}
			OnPacketSend(packets);
		}

		private void OnPacketSend(ArchipelagoPacketBase[] packets)
		{
			try
			{
				if (this.PacketsSent != null)
				{
					this.PacketsSent(packets);
				}
			}
			catch (Exception e)
			{
				OnError(e);
			}
		}

		private void OnSocketClosed()
		{
			try
			{
				if (this.SocketClosed != null)
				{
					this.SocketClosed("");
				}
			}
			catch (Exception e)
			{
				OnError(e);
			}
		}

		private void OnMessageReceived(string message)
		{
			try
			{
				if (string.IsNullOrEmpty(message) || this.PacketReceived == null)
				{
					return;
				}
				List<ArchipelagoPacketBase> list = null;
				try
				{
					list = JsonConvert.DeserializeObject<List<ArchipelagoPacketBase>>(message, (JsonConverter[])(object)new JsonConverter[1] { Converter });
				}
				catch (Exception e)
				{
					OnError(e);
				}
				if (list == null)
				{
					return;
				}
				foreach (ArchipelagoPacketBase item in list)
				{
					this.PacketReceived(item);
				}
			}
			catch (Exception e2)
			{
				OnError(e2);
			}
		}

		protected void OnError(Exception e)
		{
			try
			{
				if (this.ErrorReceived != null)
				{
					this.ErrorReceived(e, e.Message);
				}
			}
			catch (Exception ex)
			{
				Console.Out.WriteLine($"Error occured during reporting of errorOuter Errror: {e.Message} {e.StackTrace}Inner Errror: {ex.Message} {ex.StackTrace}");
			}
		}
	}
	public interface IConnectionInfoProvider
	{
		string Game { get; }

		int Team { get; }

		int Slot { get; }

		string[] Tags { get; }

		ItemsHandlingFlags ItemsHandlingFlags { get; }

		string Uuid { get; }

		void UpdateConnectionOptions(string[] tags);

		void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags);

		void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags);
	}
	public class ConnectionInfoHelper : IConnectionInfoProvider
	{
		private readonly IArchipelagoSocketHelper socket;

		public string Game { get; private set; }

		public int Team { get; private set; }

		public int Slot { get; private set; }

		public string[] Tags { get; internal set; }

		public ItemsHandlingFlags ItemsHandlingFlags { get; internal set; }

		public string Uuid { get; private set; }

		internal ConnectionInfoHelper(IArchipelagoSocketHelper socket)
		{
			this.socket = socket;
			Reset();
			socket.PacketReceived += PacketReceived;
		}

		private void PacketReceived(ArchipelagoPacketBase packet)
		{
			if (!(packet is ConnectedPacket connectedPacket))
			{
				if (packet is ConnectionRefusedPacket)
				{
					Reset();
				}
				return;
			}
			Team = connectedPacket.Team;
			Slot = connectedPacket.Slot;
			if (connectedPacket.SlotInfo != null && connectedPacket.SlotInfo.ContainsKey(Slot))
			{
				Game = connectedPacket.SlotInfo[Slot].Game;
			}
		}

		internal void SetConnectionParameters(string game, string[] tags, ItemsHandlingFlags itemsHandlingFlags, string uuid)
		{
			Game = game;
			Tags = tags ?? new string[0];
			ItemsHandlingFlags = itemsHandlingFlags;
			Uuid = uuid ?? Guid.NewGuid().ToString();
		}

		private void Reset()
		{
			Game = null;
			Team = -1;
			Slot = -1;
			Tags = new string[0];
			ItemsHandlingFlags = ItemsHandlingFlags.NoItems;
			Uuid = null;
		}

		public void UpdateConnectionOptions(string[] tags)
		{
			UpdateConnectionOptions(tags, ItemsHandlingFlags);
		}

		public void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags)
		{
			UpdateConnectionOptions(Tags, itemsHandlingFlags);
		}

		public void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags)
		{
			SetConnectionParameters(Game, tags, itemsHandlingFlags, Uuid);
			socket.SendPacket(new ConnectUpdatePacket
			{
				Tags = Tags,
				ItemsHandling = ItemsHandlingFlags
			});
		}
	}
	public interface IDataStorageHelper : IDataStorageWrapper
	{
		DataStorageElement this[Scope scope, string key] { get; set; }

		DataStorageElement this[string key] { get; set; }
	}
	public class DataStorageHelper : IDataStorageHelper, IDataStorageWrapper
	{
		public delegate void DataStorageUpdatedHandler(JToken originalValue, JToken newValue, Dictionary<string, JToken> additionalArguments);

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

		private readonly Dictionary<Guid, DataStorageUpdatedHandler> operationSpecificCallbacks = new Dictionary<Guid, DataStorageUpdatedHandler>();

		private readonly Dictionary<string, TaskCompletionSource<JToken>> asyncRetrievalTasks = new Dictionary<string, TaskCompletionSource<JToken>>();

		private readonly IArchipelagoSocketHelper socket;

		private readonly IConnectionInfoProvider connectionInfoProvider;

		public DataStorageElement this[Scope scope, string key]
		{
			get
			{
				return this[AddScope(scope, key)];
			}
			set
			{
				this[AddScope(scope, key)] = value;
			}
		}

		public DataStorageElement this[string key]
		{
			get
			{
				return new DataStorageElement(GetContextForKey(key));
			}
			set
			{
				SetValue(key, value);
			}
		}

		internal DataStorageHelper(IArchipelagoSocketHelper socket, IConnectionInfoProvider connectionInfoProvider)
		{
			this.socket = socket;
			this.connectionInfoProvider = connectionInfoProvider;
			socket.PacketReceived += OnPacketReceived;
		}

		private void OnPacketReceived(ArchipelagoPacketBase packet)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Invalid comparison between Unknown and I4
			if (!(packet is RetrievedPacket retrievedPacket))
			{
				if (packet is SetReplyPacket setReplyPacket)
				{
					if (setReplyPacket.AdditionalArguments != null && setReplyPacket.AdditionalArguments.ContainsKey("Reference") && (int)setReplyPacket.AdditionalArguments["Reference"].Type == 8 && ((string)setReplyPacket.AdditionalArguments["Reference"]).TryParseNGuid(out var g) && operationSpecificCallbacks.TryGetValue(g, out var value))
					{
						value(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments);
						operationSpecificCallbacks.Remove(g);
					}
					if (onValueChangedEventHandlers.TryGetValue(setReplyPacket.Key, out var value2))
					{
						value2(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments);
					}
				}
				return;
			}
			foreach (KeyValuePair<string, JToken> datum in retrievedPacket.Data)
			{
				if (asyncRetrievalTasks.TryGetValue(datum.Key, out var value3))
				{
					value3.TrySetResult(datum.Value);
					asyncRetrievalTasks.Remove(datum.Key);
				}
			}
		}

		private Task<JToken> GetAsync(string key)
		{
			if (asyncRetrievalTasks.TryGetValue(key, out var value))
			{
				return value.Task;
			}
			TaskCompletionSource<JToken> taskCompletionSource = new TaskCompletionSource<JToken>();
			asyncRetrievalTasks[key] = taskCompletionSource;
			socket.SendPacketAsync(new GetPacket
			{
				Keys = new string[1] { key }
			});
			return taskCompletionSource.Task;
		}

		private void Initialize(string key, JToken value)
		{
			socket.SendPacketAsync(new SetPacket
			{
				Key = key,
				DefaultValue = value,
				Operations = new OperationSpecification[1]
				{
					new OperationSpecification
					{
						OperationType = OperationType.Default
					}
				}
			});
		}

		private JToken GetValue(string key)
		{
			Task<JToken> async = GetAsync(key);
			if (!async.Wait(TimeSpan.FromSeconds(2.0)))
			{
				throw new TimeoutException($"Timed out retrieving data for key `{key}`. This may be due to an attempt to retrieve a value from the DataStorageHelper in a synchronous fashion from within a PacketReceived handler. When using the DataStorageHelper from within code which runs on the websocket thread then use the asynchronous getters. Ex: `DataStorageHelper[\"{key}\"].GetAsync().ContinueWith(x => {{}});`Be aware that DataStorageHelper calls tend to cause packet responses, so making a call from within a PacketReceived handler may cause an infinite loop.");
			}
			return async.Result;
		}

		private void SetValue(string key, DataStorageElement e)
		{
			if (key.StartsWith("_read_"))
			{
				throw new InvalidOperationException("DataStorage write operation on readonly key '" + key + "' is not allowed");
			}
			if (e == null)
			{
				e = new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull());
			}
			if (e.Context == null)
			{
				e.Context = GetContextForKey(key);
			}
			else if (e.Context.Key != key)
			{
				e.Operations.Insert(0, new OperationSpecification
				{
					OperationType = OperationType.Replace,
					Value = GetValue(e.Context.Key)
				});
			}
			Dictionary<string, JToken> dictionary = e.AdditionalArguments ?? new Dictionary<string, JToken>(0);
			if (e.Callbacks != null)
			{
				Guid key2 = Guid.NewGuid();
				operationSpecificCallbacks[key2] = e.Callbacks;
				dictionary["Reference"] = JToken.op_Implicit(key2.ToString("N"));
				socket.SendPacketAsync(new SetPacket
				{
					Key = key,
					Operations = e.Operations.ToArray(),
					WantReply = true,
					AdditionalArguments = dictionary
				});
			}
			else
			{
				socket.SendPacketAsync(new SetPacket
				{
					Key = key,
					Operations = e.Operations.ToArray(),
					AdditionalArguments = dictionary
				});
			}
		}

		private DataStorageElementContext GetContextForKey(string key)
		{
			return new DataStorageElementContext
			{
				Key = key,
				GetData = GetValue,
				GetAsync = GetAsync,
				Initialize = Initialize,
				AddHandler = AddHandler,
				RemoveHandler = RemoveHandler
			};
		}

		private void AddHandler(string key, DataStorageUpdatedHandler handler)
		{
			if (onValueChangedEventHandlers.ContainsKey(key))
			{
				Dictionary<string, DataStorageUpdatedHandler> dictionary = onValueChangedEventHandlers;
				dictionary[key] = (DataStorageUpdatedHandler)Delegate.Combine(dictionary[key], handler);
			}
			else
			{
				onValueChangedEventHandlers[key] = handler;
			}
			socket.SendPacketAsync(new SetNotifyPacket
			{
				Keys = new string[1] { key }
			});
		}

		private void RemoveHandler(string key, DataStorageUpdatedHandler handler)
		{
			if (onValueChangedEventHandlers.ContainsKey(key))
			{
				Dictionary<string, DataStorageUpdatedHandler> dictionary = onValueChangedEventHandlers;
				dictionary[key] = (DataStorageUpdatedHandler)Delegate.Remove(dictionary[key], handler);
				if (onValueChangedEventHandlers[key] == null)
				{
					onValueChangedEventHandlers.Remove(key);
				}
			}
		}

		private string AddScope(Scope scope, string key)
		{
			return scope switch
			{
				Scope.Global => key, 
				Scope.Game => $"{scope}:{connectionInfoProvider.Game}:{key}", 
				Scope.Team => $"{scope}:{connectionInfoProvider.Team}:{key}", 
				Scope.Slot => $"{scope}:{connectionInfoProvider.Slot}:{key}", 
				Scope.ReadOnly => "_read_" + key, 
				_ => throw new ArgumentOutOfRangeException("scope", scope, "Invalid scope for key " + key), 
			};
		}

		private DataStorageElement GetHintsElement(int? slot = null, int? team = null)
		{
			return this[Scope.ReadOnly, $"hints_{team ?? connectionInfoProvider.Team}_{slot ?? connectionInfoProvider.Slot}"];
		}

		private DataStorageElement GetSlotDataElement(int? slot = null)
		{
			return this[Scope.ReadOnly, $"slot_data_{slot ?? connectionInfoProvider.Slot}"];
		}

		private DataStorageElement GetItemNameGroupsElement(string game = null)
		{
			return this[Scope.ReadOnly, "item_name_groups_" + (game ?? connectionInfoProvider.Game)];
		}

		private DataStorageElement GetLocationNameGroupsElement(string game = null)
		{
			return this[Scope.ReadOnly, "location_name_groups_" + (game ?? connectionInfoProvider.Game)];
		}

		private DataStorageElement GetClientStatusElement(int? slot = null, int? team = null)
		{
			return this[Scope.ReadOnly, $"client_status_{team ?? connectionInfoProvider.Team}_{slot ?? connectionInfoProvider.Slot}"];
		}

		private DataStorageElement GetRaceModeElement()
		{
			return this[Scope.ReadOnly, "race_mode"];
		}

		public Hint[] GetHints(int? slot = null, int? team = null)
		{
			return GetHintsElement(slot, team).To<Hint[]>();
		}

		public Task<Hint[]> GetHintsAsync(int? slot = null, int? team = null)
		{
			return GetHintsElement(slot, team).GetAsync<Hint[]>();
		}

		public void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null)
		{
			GetHintsElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x)
			{
				onHintsUpdated(newValue.ToObject<Hint[]>());
			};
			if (retrieveCurrentlyUnlockedHints)
			{
				GetHintsAsync(slot, team).ContinueWith(delegate(Task<Hint[]> t)
				{
					onHintsUpdated(t.Result);
				});
			}
		}

		public Dictionary<string, object> GetSlotData(int? slot = null)
		{
			return GetSlotData<Dictionary<string, object>>(slot);
		}

		public T GetSlotData<T>(int? slot = null) where T : class
		{
			return GetSlotDataElement(slot).To<T>();
		}

		public Task<Dictionary<string, object>> GetSlotDataAsync(int? slot = null)
		{
			return GetSlotDataAsync<Dictionary<string, object>>(slot);
		}

		public Task<T> GetSlotDataAsync<T>(int? slot = null) where T : class
		{
			return GetSlotDataElement(slot).GetAsync<T>();
		}

		public Dictionary<string, string[]> GetItemNameGroups(string game = null)
		{
			return GetItemNameGroupsElement(game).To<Dictionary<string, string[]>>();
		}

		public Task<Dictionary<string, string[]>> GetItemNameGroupsAsync

Newtonsoft.Json.dll

Decompiled 4 hours ago
#define DEBUG
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Numerics;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Versioning;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq.JsonPath;
using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AllowPartiallyTrustedCallers]
[assembly: InternalsVisibleTo("Newtonsoft.Json.Schema")]
[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests")]
[assembly: InternalsVisibleTo("Newtonsoft.Json.Dynamic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cbd8d53b9d7de30f1f1278f636ec462cf9c254991291e66ebb157a885638a517887633b898ccbcf0d5c5ff7be85a6abe9e765d0ac7cd33c68dac67e7e64530e8222101109f154ab14a941c490ac155cd1d4fcba0fabb49016b4ef28593b015cab5937da31172f03f67d09edda404b88a60023f062ae71d0b2e4438b74cc11dc9")]
[assembly: AssemblyTrademark("")]
[assembly: CLSCompliant(true)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("Newtonsoft")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyCopyright("Copyright © James Newton-King 2008")]
[assembly: AssemblyDescription("Json.NET is a popular high-performance JSON framework for .NET")]
[assembly: AssemblyFileVersion("11.0.1")]
[assembly: AssemblyInformationalVersion("11.0.1-beta2+8931bf49300e016eeb846932030e0445944db3bf")]
[assembly: AssemblyProduct("Json.NET")]
[assembly: AssemblyTitle("Json.NET .NET Standard 2.0")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ArchipelagoMW/Archipelago.MultiClient.Net.git")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyVersion("11.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace System.Diagnostics.CodeAnalysis
{
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)]
	internal sealed class NotNullAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
	internal sealed class NotNullWhenAttribute : Attribute
	{
		public bool ReturnValue { get; }

		public NotNullWhenAttribute(bool returnValue)
		{
			ReturnValue = returnValue;
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)]
	internal sealed class MaybeNullAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
	internal sealed class AllowNullAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	internal class DoesNotReturnIfAttribute : Attribute
	{
		public bool ParameterValue { get; }

		public DoesNotReturnIfAttribute(bool parameterValue)
		{
			ParameterValue = parameterValue;
		}
	}
}
namespace Newtonsoft.Json
{
	public enum ConstructorHandling
	{
		Default,
		AllowNonPublicDefaultConstructor
	}
	public enum DateFormatHandling
	{
		IsoDateFormat,
		MicrosoftDateFormat
	}
	public enum DateParseHandling
	{
		None,
		DateTime,
		DateTimeOffset
	}
	public enum DateTimeZoneHandling
	{
		Local,
		Utc,
		Unspecified,
		RoundtripKind
	}
	public class DefaultJsonNameTable : JsonNameTable
	{
		private class Entry
		{
			internal readonly string Value;

			internal readonly int HashCode;

			internal Entry Next;

			internal Entry(string value, int hashCode, Entry next)
			{
				Value = value;
				HashCode = hashCode;
				Next = next;
			}
		}

		private static readonly int HashCodeRandomizer;

		private int _count;

		private Entry[] _entries;

		private int _mask = 31;

		static DefaultJsonNameTable()
		{
			HashCodeRandomizer = Environment.TickCount;
		}

		public DefaultJsonNameTable()
		{
			_entries = new Entry[_mask + 1];
		}

		public override string? Get(char[] key, int start, int length)
		{
			if (length == 0)
			{
				return string.Empty;
			}
			int num = length + HashCodeRandomizer;
			num += (num << 7) ^ key[start];
			int num2 = start + length;
			for (int i = start + 1; i < num2; i++)
			{
				num += (num << 7) ^ key[i];
			}
			num -= num >> 17;
			num -= num >> 11;
			num -= num >> 5;
			int num3 = num & _mask;
			Entry[] entries = _entries;
			for (Entry entry = entries[num3]; entry != null; entry = entry.Next)
			{
				if (entry.HashCode == num && TextEquals(entry.Value, key, start, length))
				{
					return entry.Value;
				}
			}
			return null;
		}

		public string Add(string key)
		{
			if (key == null)
			{
				throw new ArgumentNullException("key");
			}
			int length = key.Length;
			if (length == 0)
			{
				return string.Empty;
			}
			int num = length + HashCodeRandomizer;
			for (int i = 0; i < key.Length; i++)
			{
				num += (num << 7) ^ key[i];
			}
			num -= num >> 17;
			num -= num >> 11;
			num -= num >> 5;
			for (Entry entry = _entries[num & _mask]; entry != null; entry = entry.Next)
			{
				if (entry.HashCode == num && entry.Value.Equals(key, StringComparison.Ordinal))
				{
					return entry.Value;
				}
			}
			return AddEntry(key, num);
		}

		private string AddEntry(string str, int hashCode)
		{
			int num = hashCode & _mask;
			Entry entry = new Entry(str, hashCode, _entries[num]);
			_entries[num] = entry;
			if (_count++ == _mask)
			{
				Grow();
			}
			return entry.Value;
		}

		private void Grow()
		{
			Entry[] entries = _entries;
			int num = _mask * 2 + 1;
			Entry[] array = new Entry[num + 1];
			for (int i = 0; i < entries.Length; i++)
			{
				Entry entry = entries[i];
				while (entry != null)
				{
					int num2 = entry.HashCode & num;
					Entry next = entry.Next;
					entry.Next = array[num2];
					array[num2] = entry;
					entry = next;
				}
			}
			_entries = array;
			_mask = num;
		}

		private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length)
		{
			if (str1.Length != str2Length)
			{
				return false;
			}
			for (int i = 0; i < str1.Length; i++)
			{
				if (str1[i] != str2[str2Start + i])
				{
					return false;
				}
			}
			return true;
		}
	}
	[Flags]
	public enum DefaultValueHandling
	{
		Include = 0,
		Ignore = 1,
		Populate = 2,
		IgnoreAndPopulate = 3
	}
	public enum FloatFormatHandling
	{
		String,
		Symbol,
		DefaultValue
	}
	public enum FloatParseHandling
	{
		Double,
		Decimal
	}
	public enum Formatting
	{
		None,
		Indented
	}
	public interface IArrayPool<T>
	{
		T[] Rent(int minimumLength);

		void Return(T[]? array);
	}
	public interface IJsonLineInfo
	{
		int LineNumber { get; }

		int LinePosition { get; }

		bool HasLineInfo();
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
	public sealed class JsonArrayAttribute : JsonContainerAttribute
	{
		private bool _allowNullItems;

		public bool AllowNullItems
		{
			get
			{
				return _allowNullItems;
			}
			set
			{
				_allowNullItems = value;
			}
		}

		public JsonArrayAttribute()
		{
		}

		public JsonArrayAttribute(bool allowNullItems)
		{
			_allowNullItems = allowNullItems;
		}

		public JsonArrayAttribute(string id)
			: base(id)
		{
		}
	}
	[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)]
	public sealed class JsonConstructorAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
	public abstract class JsonContainerAttribute : Attribute
	{
		internal bool? _isReference;

		internal bool? _itemIsReference;

		internal ReferenceLoopHandling? _itemReferenceLoopHandling;

		internal TypeNameHandling? _itemTypeNameHandling;

		private Type? _namingStrategyType;

		private object[]? _namingStrategyParameters;

		public string? Id { get; set; }

		public string? Title { get; set; }

		public string? Description { get; set; }

		public Type? ItemConverterType { get; set; }

		public object[]? ItemConverterParameters { get; set; }

		public Type? NamingStrategyType
		{
			get
			{
				return _namingStrategyType;
			}
			set
			{
				_namingStrategyType = value;
				NamingStrategyInstance = null;
			}
		}

		public object[]? NamingStrategyParameters
		{
			get
			{
				return _namingStrategyParameters;
			}
			set
			{
				_namingStrategyParameters = value;
				NamingStrategyInstance = null;
			}
		}

		internal NamingStrategy? NamingStrategyInstance { get; set; }

		public bool IsReference
		{
			get
			{
				return _isReference.GetValueOrDefault();
			}
			set
			{
				_isReference = value;
			}
		}

		public bool ItemIsReference
		{
			get
			{
				return _itemIsReference.GetValueOrDefault();
			}
			set
			{
				_itemIsReference = value;
			}
		}

		public ReferenceLoopHandling ItemReferenceLoopHandling
		{
			get
			{
				return _itemReferenceLoopHandling.GetValueOrDefault();
			}
			set
			{
				_itemReferenceLoopHandling = value;
			}
		}

		public TypeNameHandling ItemTypeNameHandling
		{
			get
			{
				return _itemTypeNameHandling.GetValueOrDefault();
			}
			set
			{
				_itemTypeNameHandling = value;
			}
		}

		protected JsonContainerAttribute()
		{
		}

		protected JsonContainerAttribute(string id)
		{
			Id = id;
		}
	}
	public static class JsonConvert
	{
		public static readonly string True = "true";

		public static readonly string False = "false";

		public static readonly string Null = "null";

		public static readonly string Undefined = "undefined";

		public static readonly string PositiveInfinity = "Infinity";

		public static readonly string NegativeInfinity = "-Infinity";

		public static readonly string NaN = "NaN";

		public static Func<JsonSerializerSettings>? DefaultSettings { get; set; }

		public static string ToString(DateTime value)
		{
			return ToString(value, DateFormatHandling.IsoDateFormat, DateTimeZoneHandling.RoundtripKind);
		}

		public static string ToString(DateTime value, DateFormatHandling format, DateTimeZoneHandling timeZoneHandling)
		{
			DateTime value2 = DateTimeUtils.EnsureDateTime(value, timeZoneHandling);
			using StringWriter stringWriter = StringUtils.CreateStringWriter(64);
			stringWriter.Write('"');
			DateTimeUtils.WriteDateTimeString(stringWriter, value2, format, null, CultureInfo.InvariantCulture);
			stringWriter.Write('"');
			return stringWriter.ToString();
		}

		public static string ToString(DateTimeOffset value)
		{
			return ToString(value, DateFormatHandling.IsoDateFormat);
		}

		public static string ToString(DateTimeOffset value, DateFormatHandling format)
		{
			using StringWriter stringWriter = StringUtils.CreateStringWriter(64);
			stringWriter.Write('"');
			DateTimeUtils.WriteDateTimeOffsetString(stringWriter, value, format, null, CultureInfo.InvariantCulture);
			stringWriter.Write('"');
			return stringWriter.ToString();
		}

		public static string ToString(bool value)
		{
			return value ? True : False;
		}

		public static string ToString(char value)
		{
			return ToString(char.ToString(value));
		}

		public static string ToString(Enum value)
		{
			return value.ToString("D");
		}

		public static string ToString(int value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		public static string ToString(short value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		[CLSCompliant(false)]
		public static string ToString(ushort value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		[CLSCompliant(false)]
		public static string ToString(uint value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		public static string ToString(long value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		private static string ToStringInternal(BigInteger value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		[CLSCompliant(false)]
		public static string ToString(ulong value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		public static string ToString(float value)
		{
			return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture));
		}

		internal static string ToString(float value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable)
		{
			return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable);
		}

		private static string EnsureFloatFormat(double value, string text, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable)
		{
			if (floatFormatHandling == FloatFormatHandling.Symbol || (!double.IsInfinity(value) && !double.IsNaN(value)))
			{
				return text;
			}
			if (floatFormatHandling == FloatFormatHandling.DefaultValue)
			{
				return (!nullable) ? "0.0" : Null;
			}
			return quoteChar + text + quoteChar;
		}

		public static string ToString(double value)
		{
			return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture));
		}

		internal static string ToString(double value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable)
		{
			return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable);
		}

		private static string EnsureDecimalPlace(double value, string text)
		{
			if (double.IsNaN(value) || double.IsInfinity(value) || text.IndexOf('.') != -1 || text.IndexOf('E') != -1 || text.IndexOf('e') != -1)
			{
				return text;
			}
			return text + ".0";
		}

		private static string EnsureDecimalPlace(string text)
		{
			if (text.IndexOf('.') != -1)
			{
				return text;
			}
			return text + ".0";
		}

		public static string ToString(byte value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		[CLSCompliant(false)]
		public static string ToString(sbyte value)
		{
			return value.ToString(null, CultureInfo.InvariantCulture);
		}

		public static string ToString(decimal value)
		{
			return EnsureDecimalPlace(value.ToString(null, CultureInfo.InvariantCulture));
		}

		public static string ToString(Guid value)
		{
			return ToString(value, '"');
		}

		internal static string ToString(Guid value, char quoteChar)
		{
			string text = value.ToString("D", CultureInfo.InvariantCulture);
			string text2 = quoteChar.ToString(CultureInfo.InvariantCulture);
			return text2 + text + text2;
		}

		public static string ToString(TimeSpan value)
		{
			return ToString(value, '"');
		}

		internal static string ToString(TimeSpan value, char quoteChar)
		{
			return ToString(value.ToString(), quoteChar);
		}

		public static string ToString(Uri? value)
		{
			if (value == null)
			{
				return Null;
			}
			return ToString(value, '"');
		}

		internal static string ToString(Uri value, char quoteChar)
		{
			return ToString(value.OriginalString, quoteChar);
		}

		public static string ToString(string? value)
		{
			return ToString(value, '"');
		}

		public static string ToString(string? value, char delimiter)
		{
			return ToString(value, delimiter, StringEscapeHandling.Default);
		}

		public static string ToString(string? value, char delimiter, StringEscapeHandling stringEscapeHandling)
		{
			if (delimiter != '"' && delimiter != '\'')
			{
				throw new ArgumentException("Delimiter must be a single or double quote.", "delimiter");
			}
			return JavaScriptUtils.ToEscapedJavaScriptString(value, delimiter, appendDelimiters: true, stringEscapeHandling);
		}

		public static string ToString(object? value)
		{
			if (value == null)
			{
				return Null;
			}
			return ConvertUtils.GetTypeCode(value.GetType()) switch
			{
				PrimitiveTypeCode.String => ToString((string)value), 
				PrimitiveTypeCode.Char => ToString((char)value), 
				PrimitiveTypeCode.Boolean => ToString((bool)value), 
				PrimitiveTypeCode.SByte => ToString((sbyte)value), 
				PrimitiveTypeCode.Int16 => ToString((short)value), 
				PrimitiveTypeCode.UInt16 => ToString((ushort)value), 
				PrimitiveTypeCode.Int32 => ToString((int)value), 
				PrimitiveTypeCode.Byte => ToString((byte)value), 
				PrimitiveTypeCode.UInt32 => ToString((uint)value), 
				PrimitiveTypeCode.Int64 => ToString((long)value), 
				PrimitiveTypeCode.UInt64 => ToString((ulong)value), 
				PrimitiveTypeCode.Single => ToString((float)value), 
				PrimitiveTypeCode.Double => ToString((double)value), 
				PrimitiveTypeCode.DateTime => ToString((DateTime)value), 
				PrimitiveTypeCode.Decimal => ToString((decimal)value), 
				PrimitiveTypeCode.DateTimeOffset => ToString((DateTimeOffset)value), 
				PrimitiveTypeCode.Guid => ToString((Guid)value), 
				PrimitiveTypeCode.Uri => ToString((Uri)value), 
				PrimitiveTypeCode.TimeSpan => ToString((TimeSpan)value), 
				PrimitiveTypeCode.BigInteger => ToStringInternal((BigInteger)value), 
				_ => throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())), 
			};
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value)
		{
			return SerializeObject(value, (Type?)null, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Formatting formatting)
		{
			return SerializeObject(value, formatting, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, params JsonConverter[] converters)
		{
			JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings
			{
				Converters = converters
			} : null);
			return SerializeObject(value, null, settings);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Formatting formatting, params JsonConverter[] converters)
		{
			JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings
			{
				Converters = converters
			} : null);
			return SerializeObject(value, null, formatting, settings);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, JsonSerializerSettings? settings)
		{
			return SerializeObject(value, null, settings);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Type? type, JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
			return SerializeObjectInternal(value, type, jsonSerializer);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Formatting formatting, JsonSerializerSettings? settings)
		{
			return SerializeObject(value, null, formatting, settings);
		}

		[DebuggerStepThrough]
		public static string SerializeObject(object? value, Type? type, Formatting formatting, JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
			jsonSerializer.Formatting = formatting;
			return SerializeObjectInternal(value, type, jsonSerializer);
		}

		private static string SerializeObjectInternal(object? value, Type? type, JsonSerializer jsonSerializer)
		{
			StringBuilder sb = new StringBuilder(256);
			StringWriter stringWriter = new StringWriter(sb, CultureInfo.InvariantCulture);
			using (JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter))
			{
				jsonTextWriter.Formatting = jsonSerializer.Formatting;
				jsonSerializer.Serialize(jsonTextWriter, value, type);
			}
			return stringWriter.ToString();
		}

		[DebuggerStepThrough]
		public static object? DeserializeObject(string value)
		{
			return DeserializeObject(value, (Type?)null, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static object? DeserializeObject(string value, JsonSerializerSettings settings)
		{
			return DeserializeObject(value, null, settings);
		}

		[DebuggerStepThrough]
		public static object? DeserializeObject(string value, Type type)
		{
			return DeserializeObject(value, type, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static T? DeserializeObject<T>(string value)
		{
			return JsonConvert.DeserializeObject<T>(value, (JsonSerializerSettings?)null);
		}

		[DebuggerStepThrough]
		public static T? DeserializeAnonymousType<T>(string value, T anonymousTypeObject)
		{
			return DeserializeObject<T>(value);
		}

		[DebuggerStepThrough]
		public static T? DeserializeAnonymousType<T>(string value, T anonymousTypeObject, JsonSerializerSettings settings)
		{
			return DeserializeObject<T>(value, settings);
		}

		[DebuggerStepThrough]
		public static T? DeserializeObject<T>(string value, params JsonConverter[] converters)
		{
			return (T)DeserializeObject(value, typeof(T), converters);
		}

		[DebuggerStepThrough]
		public static T? DeserializeObject<T>(string value, JsonSerializerSettings? settings)
		{
			return (T)DeserializeObject(value, typeof(T), settings);
		}

		[DebuggerStepThrough]
		public static object? DeserializeObject(string value, Type type, params JsonConverter[] converters)
		{
			JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings
			{
				Converters = converters
			} : null);
			return DeserializeObject(value, type, settings);
		}

		public static object? DeserializeObject(string value, Type? type, JsonSerializerSettings? settings)
		{
			ValidationUtils.ArgumentNotNull(value, "value");
			JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
			if (!jsonSerializer.IsCheckAdditionalContentSet())
			{
				jsonSerializer.CheckAdditionalContent = true;
			}
			using JsonTextReader reader = new JsonTextReader(new StringReader(value));
			return jsonSerializer.Deserialize(reader, type);
		}

		[DebuggerStepThrough]
		public static void PopulateObject(string value, object target)
		{
			PopulateObject(value, target, null);
		}

		public static void PopulateObject(string value, object target, JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
			using JsonReader jsonReader = new JsonTextReader(new StringReader(value));
			jsonSerializer.Populate(jsonReader, target);
			if (settings == null || !settings.CheckAdditionalContent)
			{
				return;
			}
			while (jsonReader.Read())
			{
				if (jsonReader.TokenType != JsonToken.Comment)
				{
					throw JsonSerializationException.Create(jsonReader, "Additional text found in JSON string after finishing deserializing object.");
				}
			}
		}
	}
	public abstract class JsonConverter
	{
		public virtual bool CanRead => true;

		public virtual bool CanWrite => true;

		public abstract void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer);

		public abstract object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer);

		public abstract bool CanConvert(Type objectType);
	}
	public abstract class JsonConverter<T> : JsonConverter
	{
		public sealed override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
		{
			if (!((value != null) ? (value is T) : ReflectionUtils.IsNullable(typeof(T))))
			{
				throw new JsonSerializationException("Converter cannot write specified value to JSON. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T)));
			}
			WriteJson(writer, (T)value, serializer);
		}

		public abstract void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer);

		public sealed override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
		{
			bool flag = existingValue == null;
			if (!flag && !(existingValue is T))
			{
				throw new JsonSerializationException("Converter cannot read JSON with the specified existing value. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T)));
			}
			return ReadJson(reader, objectType, flag ? default(T) : ((T)existingValue), !flag, serializer);
		}

		public abstract T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer);

		public sealed override bool CanConvert(Type objectType)
		{
			return typeof(T).IsAssignableFrom(objectType);
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = false)]
	public sealed class JsonConverterAttribute : Attribute
	{
		private readonly Type _converterType;

		public Type ConverterType => _converterType;

		public object[]? ConverterParameters { get; }

		public JsonConverterAttribute(Type converterType)
		{
			if (converterType == null)
			{
				throw new ArgumentNullException("converterType");
			}
			_converterType = converterType;
		}

		public JsonConverterAttribute(Type converterType, params object[] converterParameters)
			: this(converterType)
		{
			ConverterParameters = converterParameters;
		}
	}
	public class JsonConverterCollection : Collection<JsonConverter>
	{
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
	public sealed class JsonDictionaryAttribute : JsonContainerAttribute
	{
		public JsonDictionaryAttribute()
		{
		}

		public JsonDictionaryAttribute(string id)
			: base(id)
		{
		}
	}
	[Serializable]
	public class JsonException : Exception
	{
		public JsonException()
		{
		}

		public JsonException(string message)
			: base(message)
		{
		}

		public JsonException(string message, Exception? innerException)
			: base(message, innerException)
		{
		}

		public JsonException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}

		internal static JsonException Create(IJsonLineInfo lineInfo, string path, string message)
		{
			message = JsonPosition.FormatMessage(lineInfo, path, message);
			return new JsonException(message);
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
	public class JsonExtensionDataAttribute : Attribute
	{
		public bool WriteData { get; set; }

		public bool ReadData { get; set; }

		public JsonExtensionDataAttribute()
		{
			WriteData = true;
			ReadData = true;
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
	public sealed class JsonIgnoreAttribute : Attribute
	{
	}
	public abstract class JsonNameTable
	{
		public abstract string? Get(char[] key, int start, int length);
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false)]
	public sealed class JsonObjectAttribute : JsonContainerAttribute
	{
		private MemberSerialization _memberSerialization = MemberSerialization.OptOut;

		internal MissingMemberHandling? _missingMemberHandling;

		internal Required? _itemRequired;

		internal NullValueHandling? _itemNullValueHandling;

		public MemberSerialization MemberSerialization
		{
			get
			{
				return _memberSerialization;
			}
			set
			{
				_memberSerialization = value;
			}
		}

		public MissingMemberHandling MissingMemberHandling
		{
			get
			{
				return _missingMemberHandling.GetValueOrDefault();
			}
			set
			{
				_missingMemberHandling = value;
			}
		}

		public NullValueHandling ItemNullValueHandling
		{
			get
			{
				return _itemNullValueHandling.GetValueOrDefault();
			}
			set
			{
				_itemNullValueHandling = value;
			}
		}

		public Required ItemRequired
		{
			get
			{
				return _itemRequired.GetValueOrDefault();
			}
			set
			{
				_itemRequired = value;
			}
		}

		public JsonObjectAttribute()
		{
		}

		public JsonObjectAttribute(MemberSerialization memberSerialization)
		{
			MemberSerialization = memberSerialization;
		}

		public JsonObjectAttribute(string id)
			: base(id)
		{
		}
	}
	internal enum JsonContainerType
	{
		None,
		Object,
		Array,
		Constructor
	}
	internal struct JsonPosition
	{
		private static readonly char[] SpecialCharacters = new char[18]
		{
			'.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t',
			'\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029'
		};

		internal JsonContainerType Type;

		internal int Position;

		internal string? PropertyName;

		internal bool HasIndex;

		public JsonPosition(JsonContainerType type)
		{
			Type = type;
			HasIndex = TypeHasIndex(type);
			Position = -1;
			PropertyName = null;
		}

		internal int CalculateLength()
		{
			switch (Type)
			{
			case JsonContainerType.Object:
				return PropertyName.Length + 5;
			case JsonContainerType.Array:
			case JsonContainerType.Constructor:
				return MathUtils.IntLength((ulong)Position) + 2;
			default:
				throw new ArgumentOutOfRangeException("Type");
			}
		}

		internal void WriteTo(StringBuilder sb, ref StringWriter? writer, ref char[]? buffer)
		{
			switch (Type)
			{
			case JsonContainerType.Object:
			{
				string propertyName = PropertyName;
				if (propertyName.IndexOfAny(SpecialCharacters) != -1)
				{
					sb.Append("['");
					if (writer == null)
					{
						writer = new StringWriter(sb);
					}
					JavaScriptUtils.WriteEscapedJavaScriptString(writer, propertyName, '\'', appendDelimiters: false, JavaScriptUtils.SingleQuoteCharEscapeFlags, StringEscapeHandling.Default, null, ref buffer);
					sb.Append("']");
				}
				else
				{
					if (sb.Length > 0)
					{
						sb.Append('.');
					}
					sb.Append(propertyName);
				}
				break;
			}
			case JsonContainerType.Array:
			case JsonContainerType.Constructor:
				sb.Append('[');
				sb.Append(Position);
				sb.Append(']');
				break;
			}
		}

		internal static bool TypeHasIndex(JsonContainerType type)
		{
			return type == JsonContainerType.Array || type == JsonContainerType.Constructor;
		}

		internal static string BuildPath(List<JsonPosition> positions, JsonPosition? currentPosition)
		{
			int num = 0;
			if (positions != null)
			{
				for (int i = 0; i < positions.Count; i++)
				{
					num += positions[i].CalculateLength();
				}
			}
			if (currentPosition.HasValue)
			{
				num += currentPosition.GetValueOrDefault().CalculateLength();
			}
			StringBuilder stringBuilder = new StringBuilder(num);
			StringWriter writer = null;
			char[] buffer = null;
			if (positions != null)
			{
				foreach (JsonPosition position in positions)
				{
					position.WriteTo(stringBuilder, ref writer, ref buffer);
				}
			}
			currentPosition?.WriteTo(stringBuilder, ref writer, ref buffer);
			return stringBuilder.ToString();
		}

		internal static string FormatMessage(IJsonLineInfo? lineInfo, string path, string message)
		{
			if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal))
			{
				message = message.Trim();
				if (!StringUtils.EndsWith(message, '.'))
				{
					message += ".";
				}
				message += " ";
			}
			message += "Path '{0}'".FormatWith(CultureInfo.InvariantCulture, path);
			if (lineInfo != null && lineInfo.HasLineInfo())
			{
				message += ", line {0}, position {1}".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition);
			}
			message += ".";
			return message;
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
	public sealed class JsonPropertyAttribute : Attribute
	{
		internal NullValueHandling? _nullValueHandling;

		internal DefaultValueHandling? _defaultValueHandling;

		internal ReferenceLoopHandling? _referenceLoopHandling;

		internal ObjectCreationHandling? _objectCreationHandling;

		internal TypeNameHandling? _typeNameHandling;

		internal bool? _isReference;

		internal int? _order;

		internal Required? _required;

		internal bool? _itemIsReference;

		internal ReferenceLoopHandling? _itemReferenceLoopHandling;

		internal TypeNameHandling? _itemTypeNameHandling;

		public Type? ItemConverterType { get; set; }

		public object[]? ItemConverterParameters { get; set; }

		public Type? NamingStrategyType { get; set; }

		public object[]? NamingStrategyParameters { get; set; }

		public NullValueHandling NullValueHandling
		{
			get
			{
				return _nullValueHandling.GetValueOrDefault();
			}
			set
			{
				_nullValueHandling = value;
			}
		}

		public DefaultValueHandling DefaultValueHandling
		{
			get
			{
				return _defaultValueHandling.GetValueOrDefault();
			}
			set
			{
				_defaultValueHandling = value;
			}
		}

		public ReferenceLoopHandling ReferenceLoopHandling
		{
			get
			{
				return _referenceLoopHandling.GetValueOrDefault();
			}
			set
			{
				_referenceLoopHandling = value;
			}
		}

		public ObjectCreationHandling ObjectCreationHandling
		{
			get
			{
				return _objectCreationHandling.GetValueOrDefault();
			}
			set
			{
				_objectCreationHandling = value;
			}
		}

		public TypeNameHandling TypeNameHandling
		{
			get
			{
				return _typeNameHandling.GetValueOrDefault();
			}
			set
			{
				_typeNameHandling = value;
			}
		}

		public bool IsReference
		{
			get
			{
				return _isReference.GetValueOrDefault();
			}
			set
			{
				_isReference = value;
			}
		}

		public int Order
		{
			get
			{
				return _order.GetValueOrDefault();
			}
			set
			{
				_order = value;
			}
		}

		public Required Required
		{
			get
			{
				return _required.GetValueOrDefault();
			}
			set
			{
				_required = value;
			}
		}

		public string? PropertyName { get; set; }

		public ReferenceLoopHandling ItemReferenceLoopHandling
		{
			get
			{
				return _itemReferenceLoopHandling.GetValueOrDefault();
			}
			set
			{
				_itemReferenceLoopHandling = value;
			}
		}

		public TypeNameHandling ItemTypeNameHandling
		{
			get
			{
				return _itemTypeNameHandling.GetValueOrDefault();
			}
			set
			{
				_itemTypeNameHandling = value;
			}
		}

		public bool ItemIsReference
		{
			get
			{
				return _itemIsReference.GetValueOrDefault();
			}
			set
			{
				_itemIsReference = value;
			}
		}

		public JsonPropertyAttribute()
		{
		}

		public JsonPropertyAttribute(string propertyName)
		{
			PropertyName = propertyName;
		}
	}
	public abstract class JsonReader : IDisposable
	{
		protected internal enum State
		{
			Start,
			Complete,
			Property,
			ObjectStart,
			Object,
			ArrayStart,
			Array,
			Closed,
			PostValue,
			ConstructorStart,
			Constructor,
			Error,
			Finished
		}

		private JsonToken _tokenType;

		private object? _value;

		internal char _quoteChar;

		internal State _currentState;

		private JsonPosition _currentPosition;

		private CultureInfo? _culture;

		private DateTimeZoneHandling _dateTimeZoneHandling;

		private int? _maxDepth;

		private bool _hasExceededMaxDepth;

		internal DateParseHandling _dateParseHandling;

		internal FloatParseHandling _floatParseHandling;

		private string? _dateFormatString;

		private List<JsonPosition>? _stack;

		protected State CurrentState => _currentState;

		public bool CloseInput { get; set; }

		public bool SupportMultipleContent { get; set; }

		public virtual char QuoteChar
		{
			get
			{
				return _quoteChar;
			}
			protected internal set
			{
				_quoteChar = value;
			}
		}

		public DateTimeZoneHandling DateTimeZoneHandling
		{
			get
			{
				return _dateTimeZoneHandling;
			}
			set
			{
				if (value < DateTimeZoneHandling.Local || value > DateTimeZoneHandling.RoundtripKind)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_dateTimeZoneHandling = value;
			}
		}

		public DateParseHandling DateParseHandling
		{
			get
			{
				return _dateParseHandling;
			}
			set
			{
				if (value < DateParseHandling.None || value > DateParseHandling.DateTimeOffset)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_dateParseHandling = value;
			}
		}

		public FloatParseHandling FloatParseHandling
		{
			get
			{
				return _floatParseHandling;
			}
			set
			{
				if (value < FloatParseHandling.Double || value > FloatParseHandling.Decimal)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_floatParseHandling = value;
			}
		}

		public string? DateFormatString
		{
			get
			{
				return _dateFormatString;
			}
			set
			{
				_dateFormatString = value;
			}
		}

		public int? MaxDepth
		{
			get
			{
				return _maxDepth;
			}
			set
			{
				if (value <= 0)
				{
					throw new ArgumentException("Value must be positive.", "value");
				}
				_maxDepth = value;
			}
		}

		public virtual JsonToken TokenType => _tokenType;

		public virtual object? Value => _value;

		public virtual Type? ValueType => _value?.GetType();

		public virtual int Depth
		{
			get
			{
				int num = _stack?.Count ?? 0;
				if (JsonTokenUtils.IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None)
				{
					return num;
				}
				return num + 1;
			}
		}

		public virtual string Path
		{
			get
			{
				if (_currentPosition.Type == JsonContainerType.None)
				{
					return string.Empty;
				}
				JsonPosition? currentPosition = ((_currentState != State.ArrayStart && _currentState != State.ConstructorStart && _currentState != State.ObjectStart) ? new JsonPosition?(_currentPosition) : null);
				return JsonPosition.BuildPath(_stack, currentPosition);
			}
		}

		public CultureInfo Culture
		{
			get
			{
				return _culture ?? CultureInfo.InvariantCulture;
			}
			set
			{
				_culture = value;
			}
		}

		public virtual Task<bool> ReadAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<bool>() ?? Read().ToAsync();
		}

		public async Task SkipAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			if (TokenType == JsonToken.PropertyName)
			{
				await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			if (JsonTokenUtils.IsStartToken(TokenType))
			{
				int depth = Depth;
				while (await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false) && depth < Depth)
				{
				}
			}
		}

		internal async Task ReaderReadAndAssertAsync(CancellationToken cancellationToken)
		{
			if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false)))
			{
				throw CreateUnexpectedEndException();
			}
		}

		public virtual Task<bool?> ReadAsBooleanAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<bool?>() ?? Task.FromResult(ReadAsBoolean());
		}

		public virtual Task<byte[]?> ReadAsBytesAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<byte[]>() ?? Task.FromResult(ReadAsBytes());
		}

		internal async Task<byte[]?> ReadArrayIntoByteArrayAsync(CancellationToken cancellationToken)
		{
			List<byte> buffer = new List<byte>();
			do
			{
				if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false)))
				{
					SetToken(JsonToken.None);
				}
			}
			while (!ReadArrayElementIntoByteArrayReportDone(buffer));
			byte[] d = buffer.ToArray();
			SetToken(JsonToken.Bytes, d, updateIndex: false);
			return d;
		}

		public virtual Task<DateTime?> ReadAsDateTimeAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<DateTime?>() ?? Task.FromResult(ReadAsDateTime());
		}

		public virtual Task<DateTimeOffset?> ReadAsDateTimeOffsetAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<DateTimeOffset?>() ?? Task.FromResult(ReadAsDateTimeOffset());
		}

		public virtual Task<decimal?> ReadAsDecimalAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<decimal?>() ?? Task.FromResult(ReadAsDecimal());
		}

		public virtual Task<double?> ReadAsDoubleAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return Task.FromResult(ReadAsDouble());
		}

		public virtual Task<int?> ReadAsInt32Async(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<int?>() ?? Task.FromResult(ReadAsInt32());
		}

		public virtual Task<string?> ReadAsStringAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			return cancellationToken.CancelIfRequestedAsync<string>() ?? Task.FromResult(ReadAsString());
		}

		internal async Task<bool> ReadAndMoveToContentAsync(CancellationToken cancellationToken)
		{
			bool flag = await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			if (flag)
			{
				flag = await MoveToContentAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			return flag;
		}

		internal Task<bool> MoveToContentAsync(CancellationToken cancellationToken)
		{
			JsonToken tokenType = TokenType;
			JsonToken jsonToken = tokenType;
			if (jsonToken == JsonToken.None || jsonToken == JsonToken.Comment)
			{
				return MoveToContentFromNonContentAsync(cancellationToken);
			}
			return AsyncUtils.True;
		}

		private async Task<bool> MoveToContentFromNonContentAsync(CancellationToken cancellationToken)
		{
			JsonToken jsonToken2;
			do
			{
				if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false)))
				{
					return false;
				}
				JsonToken tokenType = TokenType;
				JsonToken jsonToken = tokenType;
				jsonToken2 = jsonToken;
			}
			while (jsonToken2 == JsonToken.None || jsonToken2 == JsonToken.Comment);
			return true;
		}

		internal JsonPosition GetPosition(int depth)
		{
			if (_stack != null && depth < _stack.Count)
			{
				return _stack[depth];
			}
			return _currentPosition;
		}

		protected JsonReader()
		{
			_currentState = State.Start;
			_dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
			_dateParseHandling = DateParseHandling.DateTime;
			_floatParseHandling = FloatParseHandling.Double;
			_maxDepth = 64;
			CloseInput = true;
		}

		private void Push(JsonContainerType value)
		{
			UpdateScopeWithFinishedValue();
			if (_currentPosition.Type == JsonContainerType.None)
			{
				_currentPosition = new JsonPosition(value);
				return;
			}
			if (_stack == null)
			{
				_stack = new List<JsonPosition>();
			}
			_stack.Add(_currentPosition);
			_currentPosition = new JsonPosition(value);
			if (!_maxDepth.HasValue || !(Depth + 1 > _maxDepth) || _hasExceededMaxDepth)
			{
				return;
			}
			_hasExceededMaxDepth = true;
			throw JsonReaderException.Create(this, "The reader's MaxDepth of {0} has been exceeded.".FormatWith(CultureInfo.InvariantCulture, _maxDepth));
		}

		private JsonContainerType Pop()
		{
			JsonPosition currentPosition;
			if (_stack != null && _stack.Count > 0)
			{
				currentPosition = _currentPosition;
				_currentPosition = _stack[_stack.Count - 1];
				_stack.RemoveAt(_stack.Count - 1);
			}
			else
			{
				currentPosition = _currentPosition;
				_currentPosition = default(JsonPosition);
			}
			if (_maxDepth.HasValue && Depth <= _maxDepth)
			{
				_hasExceededMaxDepth = false;
			}
			return currentPosition.Type;
		}

		private JsonContainerType Peek()
		{
			return _currentPosition.Type;
		}

		public abstract bool Read();

		public virtual int? ReadAsInt32()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Integer:
			case JsonToken.Float:
			{
				object value = Value;
				if (value is int value2)
				{
					return value2;
				}
				int num;
				if (value is BigInteger bigInteger)
				{
					num = (int)bigInteger;
				}
				else
				{
					try
					{
						num = Convert.ToInt32(value, CultureInfo.InvariantCulture);
					}
					catch (Exception ex)
					{
						throw JsonReaderException.Create(this, "Could not convert to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, value), ex);
					}
				}
				SetToken(JsonToken.Integer, num, updateIndex: false);
				return num;
			}
			case JsonToken.String:
			{
				string s = (string)Value;
				return ReadInt32String(s);
			}
			default:
				throw JsonReaderException.Create(this, "Error reading integer. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal int? ReadInt32String(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (int.TryParse(s, NumberStyles.Integer, Culture, out var result))
			{
				SetToken(JsonToken.Integer, result, updateIndex: false);
				return result;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual string? ReadAsString()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.String:
				return (string)Value;
			default:
				if (JsonTokenUtils.IsPrimitiveToken(contentToken))
				{
					object value = Value;
					if (value != null)
					{
						string text = ((!(value is IFormattable formattable)) ? ((value is Uri uri) ? uri.OriginalString : value.ToString()) : formattable.ToString(null, Culture));
						SetToken(JsonToken.String, text, updateIndex: false);
						return text;
					}
				}
				throw JsonReaderException.Create(this, "Error reading string. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		public virtual byte[]? ReadAsBytes()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.StartObject:
			{
				ReadIntoWrappedTypeObject();
				byte[] array3 = ReadAsBytes();
				ReaderReadAndAssert();
				if (TokenType != JsonToken.EndObject)
				{
					throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
				}
				SetToken(JsonToken.Bytes, array3, updateIndex: false);
				return array3;
			}
			case JsonToken.String:
			{
				string text = (string)Value;
				Guid g;
				byte[] array2 = ((text.Length == 0) ? CollectionUtils.ArrayEmpty<byte>() : ((!ConvertUtils.TryConvertGuid(text, out g)) ? Convert.FromBase64String(text) : g.ToByteArray()));
				SetToken(JsonToken.Bytes, array2, updateIndex: false);
				return array2;
			}
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Bytes:
				if (Value is Guid guid)
				{
					byte[] array = guid.ToByteArray();
					SetToken(JsonToken.Bytes, array, updateIndex: false);
					return array;
				}
				return (byte[])Value;
			case JsonToken.StartArray:
				return ReadArrayIntoByteArray();
			default:
				throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal byte[] ReadArrayIntoByteArray()
		{
			List<byte> list = new List<byte>();
			do
			{
				if (!Read())
				{
					SetToken(JsonToken.None);
				}
			}
			while (!ReadArrayElementIntoByteArrayReportDone(list));
			byte[] array = list.ToArray();
			SetToken(JsonToken.Bytes, array, updateIndex: false);
			return array;
		}

		private bool ReadArrayElementIntoByteArrayReportDone(List<byte> buffer)
		{
			switch (TokenType)
			{
			case JsonToken.None:
				throw JsonReaderException.Create(this, "Unexpected end when reading bytes.");
			case JsonToken.Integer:
				buffer.Add(Convert.ToByte(Value, CultureInfo.InvariantCulture));
				return false;
			case JsonToken.EndArray:
				return true;
			case JsonToken.Comment:
				return false;
			default:
				throw JsonReaderException.Create(this, "Unexpected token when reading bytes: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
			}
		}

		public virtual double? ReadAsDouble()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Integer:
			case JsonToken.Float:
			{
				object value = Value;
				if (value is double value2)
				{
					return value2;
				}
				double num = ((!(value is BigInteger bigInteger) || 1 == 0) ? Convert.ToDouble(value, CultureInfo.InvariantCulture) : ((double)bigInteger));
				SetToken(JsonToken.Float, num, updateIndex: false);
				return num;
			}
			case JsonToken.String:
				return ReadDoubleString((string)Value);
			default:
				throw JsonReaderException.Create(this, "Error reading double. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal double? ReadDoubleString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (double.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, Culture, out var result))
			{
				SetToken(JsonToken.Float, result, updateIndex: false);
				return result;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to double: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual bool? ReadAsBoolean()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Integer:
			case JsonToken.Float:
			{
				bool flag = ((!(Value is BigInteger bigInteger) || 1 == 0) ? Convert.ToBoolean(Value, CultureInfo.InvariantCulture) : (bigInteger != 0L));
				SetToken(JsonToken.Boolean, flag, updateIndex: false);
				return flag;
			}
			case JsonToken.String:
				return ReadBooleanString((string)Value);
			case JsonToken.Boolean:
				return (bool)Value;
			default:
				throw JsonReaderException.Create(this, "Error reading boolean. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal bool? ReadBooleanString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (bool.TryParse(s, out var result))
			{
				SetToken(JsonToken.Boolean, result, updateIndex: false);
				return result;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to boolean: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual decimal? ReadAsDecimal()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Integer:
			case JsonToken.Float:
			{
				object value = Value;
				if (value is decimal value2)
				{
					return value2;
				}
				decimal num;
				if (value is BigInteger bigInteger)
				{
					num = (decimal)bigInteger;
				}
				else
				{
					try
					{
						num = Convert.ToDecimal(value, CultureInfo.InvariantCulture);
					}
					catch (Exception ex)
					{
						throw JsonReaderException.Create(this, "Could not convert to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, value), ex);
					}
				}
				SetToken(JsonToken.Float, num, updateIndex: false);
				return num;
			}
			case JsonToken.String:
				return ReadDecimalString((string)Value);
			default:
				throw JsonReaderException.Create(this, "Error reading decimal. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal decimal? ReadDecimalString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (decimal.TryParse(s, NumberStyles.Number, Culture, out var result))
			{
				SetToken(JsonToken.Float, result, updateIndex: false);
				return result;
			}
			if (ConvertUtils.DecimalTryParse(s.ToCharArray(), 0, s.Length, out result) == ParseResult.Success)
			{
				SetToken(JsonToken.Float, result, updateIndex: false);
				return result;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual DateTime? ReadAsDateTime()
		{
			switch (GetContentToken())
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Date:
				if (Value is DateTimeOffset dateTimeOffset)
				{
					SetToken(JsonToken.Date, dateTimeOffset.DateTime, updateIndex: false);
				}
				return (DateTime)Value;
			case JsonToken.String:
				return ReadDateTimeString((string)Value);
			default:
				throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
			}
		}

		internal DateTime? ReadDateTimeString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (DateTimeUtils.TryParseDateTime(s, DateTimeZoneHandling, _dateFormatString, Culture, out var dt))
			{
				dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
				SetToken(JsonToken.Date, dt, updateIndex: false);
				return dt;
			}
			if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
			{
				dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
				SetToken(JsonToken.Date, dt, updateIndex: false);
				return dt;
			}
			throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		public virtual DateTimeOffset? ReadAsDateTimeOffset()
		{
			JsonToken contentToken = GetContentToken();
			switch (contentToken)
			{
			case JsonToken.None:
			case JsonToken.Null:
			case JsonToken.EndArray:
				return null;
			case JsonToken.Date:
				if (Value is DateTime dateTime)
				{
					SetToken(JsonToken.Date, new DateTimeOffset(dateTime), updateIndex: false);
				}
				return (DateTimeOffset)Value;
			case JsonToken.String:
			{
				string s = (string)Value;
				return ReadDateTimeOffsetString(s);
			}
			default:
				throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken));
			}
		}

		internal DateTimeOffset? ReadDateTimeOffsetString(string? s)
		{
			if (StringUtils.IsNullOrEmpty(s))
			{
				SetToken(JsonToken.Null, null, updateIndex: false);
				return null;
			}
			if (DateTimeUtils.TryParseDateTimeOffset(s, _dateFormatString, Culture, out var dt))
			{
				SetToken(JsonToken.Date, dt, updateIndex: false);
				return dt;
			}
			if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
			{
				SetToken(JsonToken.Date, dt, updateIndex: false);
				return dt;
			}
			SetToken(JsonToken.String, s, updateIndex: false);
			throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, s));
		}

		internal void ReaderReadAndAssert()
		{
			if (!Read())
			{
				throw CreateUnexpectedEndException();
			}
		}

		internal JsonReaderException CreateUnexpectedEndException()
		{
			return JsonReaderException.Create(this, "Unexpected end when reading JSON.");
		}

		internal void ReadIntoWrappedTypeObject()
		{
			ReaderReadAndAssert();
			if (Value != null && Value.ToString() == "$type")
			{
				ReaderReadAndAssert();
				if (Value != null && Value.ToString().StartsWith("System.Byte[]", StringComparison.Ordinal))
				{
					ReaderReadAndAssert();
					if (Value.ToString() == "$value")
					{
						return;
					}
				}
			}
			throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, JsonToken.StartObject));
		}

		public void Skip()
		{
			if (TokenType == JsonToken.PropertyName)
			{
				Read();
			}
			if (JsonTokenUtils.IsStartToken(TokenType))
			{
				int depth = Depth;
				while (Read() && depth < Depth)
				{
				}
			}
		}

		protected void SetToken(JsonToken newToken)
		{
			SetToken(newToken, null, updateIndex: true);
		}

		protected void SetToken(JsonToken newToken, object? value)
		{
			SetToken(newToken, value, updateIndex: true);
		}

		protected void SetToken(JsonToken newToken, object? value, bool updateIndex)
		{
			_tokenType = newToken;
			_value = value;
			switch (newToken)
			{
			case JsonToken.StartObject:
				_currentState = State.ObjectStart;
				Push(JsonContainerType.Object);
				break;
			case JsonToken.StartArray:
				_currentState = State.ArrayStart;
				Push(JsonContainerType.Array);
				break;
			case JsonToken.StartConstructor:
				_currentState = State.ConstructorStart;
				Push(JsonContainerType.Constructor);
				break;
			case JsonToken.EndObject:
				ValidateEnd(JsonToken.EndObject);
				break;
			case JsonToken.EndArray:
				ValidateEnd(JsonToken.EndArray);
				break;
			case JsonToken.EndConstructor:
				ValidateEnd(JsonToken.EndConstructor);
				break;
			case JsonToken.PropertyName:
				_currentState = State.Property;
				_currentPosition.PropertyName = (string)value;
				break;
			case JsonToken.Raw:
			case JsonToken.Integer:
			case JsonToken.Float:
			case JsonToken.String:
			case JsonToken.Boolean:
			case JsonToken.Null:
			case JsonToken.Undefined:
			case JsonToken.Date:
			case JsonToken.Bytes:
				SetPostValueState(updateIndex);
				break;
			case JsonToken.Comment:
				break;
			}
		}

		internal void SetPostValueState(bool updateIndex)
		{
			if (Peek() != 0 || SupportMultipleContent)
			{
				_currentState = State.PostValue;
			}
			else
			{
				SetFinished();
			}
			if (updateIndex)
			{
				UpdateScopeWithFinishedValue();
			}
		}

		private void UpdateScopeWithFinishedValue()
		{
			if (_currentPosition.HasIndex)
			{
				_currentPosition.Position++;
			}
		}

		private void ValidateEnd(JsonToken endToken)
		{
			JsonContainerType jsonContainerType = Pop();
			if (GetTypeForCloseToken(endToken) != jsonContainerType)
			{
				throw JsonReaderException.Create(this, "JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, jsonContainerType));
			}
			if (Peek() != 0 || SupportMultipleContent)
			{
				_currentState = State.PostValue;
			}
			else
			{
				SetFinished();
			}
		}

		protected void SetStateBasedOnCurrent()
		{
			JsonContainerType jsonContainerType = Peek();
			switch (jsonContainerType)
			{
			case JsonContainerType.Object:
				_currentState = State.Object;
				break;
			case JsonContainerType.Array:
				_currentState = State.Array;
				break;
			case JsonContainerType.Constructor:
				_currentState = State.Constructor;
				break;
			case JsonContainerType.None:
				SetFinished();
				break;
			default:
				throw JsonReaderException.Create(this, "While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, jsonContainerType));
			}
		}

		private void SetFinished()
		{
			_currentState = ((!SupportMultipleContent) ? State.Finished : State.Start);
		}

		private JsonContainerType GetTypeForCloseToken(JsonToken token)
		{
			return token switch
			{
				JsonToken.EndObject => JsonContainerType.Object, 
				JsonToken.EndArray => JsonContainerType.Array, 
				JsonToken.EndConstructor => JsonContainerType.Constructor, 
				_ => throw JsonReaderException.Create(this, "Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token)), 
			};
		}

		void IDisposable.Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (_currentState != State.Closed && disposing)
			{
				Close();
			}
		}

		public virtual void Close()
		{
			_currentState = State.Closed;
			_tokenType = JsonToken.None;
			_value = null;
		}

		internal void ReadAndAssert()
		{
			if (!Read())
			{
				throw JsonSerializationException.Create(this, "Unexpected end when reading JSON.");
			}
		}

		internal void ReadForTypeAndAssert(JsonContract? contract, bool hasConverter)
		{
			if (!ReadForType(contract, hasConverter))
			{
				throw JsonSerializationException.Create(this, "Unexpected end when reading JSON.");
			}
		}

		internal bool ReadForType(JsonContract? contract, bool hasConverter)
		{
			if (hasConverter)
			{
				return Read();
			}
			switch (contract?.InternalReadType ?? ReadType.Read)
			{
			case ReadType.Read:
				return ReadAndMoveToContent();
			case ReadType.ReadAsInt32:
				ReadAsInt32();
				break;
			case ReadType.ReadAsInt64:
			{
				bool result = ReadAndMoveToContent();
				if (TokenType == JsonToken.Undefined)
				{
					throw JsonReaderException.Create(this, "An undefined token is not a valid {0}.".FormatWith(CultureInfo.InvariantCulture, contract?.UnderlyingType ?? typeof(long)));
				}
				return result;
			}
			case ReadType.ReadAsDecimal:
				ReadAsDecimal();
				break;
			case ReadType.ReadAsDouble:
				ReadAsDouble();
				break;
			case ReadType.ReadAsBytes:
				ReadAsBytes();
				break;
			case ReadType.ReadAsBoolean:
				ReadAsBoolean();
				break;
			case ReadType.ReadAsString:
				ReadAsString();
				break;
			case ReadType.ReadAsDateTime:
				ReadAsDateTime();
				break;
			case ReadType.ReadAsDateTimeOffset:
				ReadAsDateTimeOffset();
				break;
			default:
				throw new ArgumentOutOfRangeException();
			}
			return TokenType != JsonToken.None;
		}

		internal bool ReadAndMoveToContent()
		{
			return Read() && MoveToContent();
		}

		internal bool MoveToContent()
		{
			JsonToken tokenType = TokenType;
			while (tokenType == JsonToken.None || tokenType == JsonToken.Comment)
			{
				if (!Read())
				{
					return false;
				}
				tokenType = TokenType;
			}
			return true;
		}

		private JsonToken GetContentToken()
		{
			JsonToken tokenType;
			do
			{
				if (!Read())
				{
					SetToken(JsonToken.None);
					return JsonToken.None;
				}
				tokenType = TokenType;
			}
			while (tokenType == JsonToken.Comment);
			return tokenType;
		}
	}
	[Serializable]
	public class JsonReaderException : JsonException
	{
		public int LineNumber { get; }

		public int LinePosition { get; }

		public string? Path { get; }

		public JsonReaderException()
		{
		}

		public JsonReaderException(string message)
			: base(message)
		{
		}

		public JsonReaderException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		public JsonReaderException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}

		public JsonReaderException(string message, string path, int lineNumber, int linePosition, Exception? innerException)
			: base(message, innerException)
		{
			Path = path;
			LineNumber = lineNumber;
			LinePosition = linePosition;
		}

		internal static JsonReaderException Create(JsonReader reader, string message)
		{
			return Create(reader, message, null);
		}

		internal static JsonReaderException Create(JsonReader reader, string message, Exception? ex)
		{
			return Create(reader as IJsonLineInfo, reader.Path, message, ex);
		}

		internal static JsonReaderException Create(IJsonLineInfo? lineInfo, string path, string message, Exception? ex)
		{
			message = JsonPosition.FormatMessage(lineInfo, path, message);
			int lineNumber;
			int linePosition;
			if (lineInfo != null && lineInfo.HasLineInfo())
			{
				lineNumber = lineInfo.LineNumber;
				linePosition = lineInfo.LinePosition;
			}
			else
			{
				lineNumber = 0;
				linePosition = 0;
			}
			return new JsonReaderException(message, path, lineNumber, linePosition, ex);
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
	public sealed class JsonRequiredAttribute : Attribute
	{
	}
	[Serializable]
	public class JsonSerializationException : JsonException
	{
		public int LineNumber { get; }

		public int LinePosition { get; }

		public string? Path { get; }

		public JsonSerializationException()
		{
		}

		public JsonSerializationException(string message)
			: base(message)
		{
		}

		public JsonSerializationException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		public JsonSerializationException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}

		public JsonSerializationException(string message, string path, int lineNumber, int linePosition, Exception? innerException)
			: base(message, innerException)
		{
			Path = path;
			LineNumber = lineNumber;
			LinePosition = linePosition;
		}

		internal static JsonSerializationException Create(JsonReader reader, string message)
		{
			return Create(reader, message, null);
		}

		internal static JsonSerializationException Create(JsonReader reader, string message, Exception? ex)
		{
			return Create(reader as IJsonLineInfo, reader.Path, message, ex);
		}

		internal static JsonSerializationException Create(IJsonLineInfo? lineInfo, string path, string message, Exception? ex)
		{
			message = JsonPosition.FormatMessage(lineInfo, path, message);
			int lineNumber;
			int linePosition;
			if (lineInfo != null && lineInfo.HasLineInfo())
			{
				lineNumber = lineInfo.LineNumber;
				linePosition = lineInfo.LinePosition;
			}
			else
			{
				lineNumber = 0;
				linePosition = 0;
			}
			return new JsonSerializationException(message, path, lineNumber, linePosition, ex);
		}
	}
	public class JsonSerializer
	{
		internal TypeNameHandling _typeNameHandling;

		internal TypeNameAssemblyFormatHandling _typeNameAssemblyFormatHandling;

		internal PreserveReferencesHandling _preserveReferencesHandling;

		internal ReferenceLoopHandling _referenceLoopHandling;

		internal MissingMemberHandling _missingMemberHandling;

		internal ObjectCreationHandling _objectCreationHandling;

		internal NullValueHandling _nullValueHandling;

		internal DefaultValueHandling _defaultValueHandling;

		internal ConstructorHandling _constructorHandling;

		internal MetadataPropertyHandling _metadataPropertyHandling;

		internal JsonConverterCollection? _converters;

		internal IContractResolver _contractResolver;

		internal ITraceWriter? _traceWriter;

		internal IEqualityComparer? _equalityComparer;

		internal ISerializationBinder _serializationBinder;

		internal StreamingContext _context;

		private IReferenceResolver? _referenceResolver;

		private Formatting? _formatting;

		private DateFormatHandling? _dateFormatHandling;

		private DateTimeZoneHandling? _dateTimeZoneHandling;

		private DateParseHandling? _dateParseHandling;

		private FloatFormatHandling? _floatFormatHandling;

		private FloatParseHandling? _floatParseHandling;

		private StringEscapeHandling? _stringEscapeHandling;

		private CultureInfo _culture;

		private int? _maxDepth;

		private bool _maxDepthSet;

		private bool? _checkAdditionalContent;

		private string? _dateFormatString;

		private bool _dateFormatStringSet;

		public virtual IReferenceResolver? ReferenceResolver
		{
			get
			{
				return GetReferenceResolver();
			}
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value", "Reference resolver cannot be null.");
				}
				_referenceResolver = value;
			}
		}

		[Obsolete("Binder is obsolete. Use SerializationBinder instead.")]
		public virtual SerializationBinder Binder
		{
			get
			{
				if (_serializationBinder is SerializationBinder result)
				{
					return result;
				}
				if (_serializationBinder is SerializationBinderAdapter serializationBinderAdapter)
				{
					return serializationBinderAdapter.SerializationBinder;
				}
				throw new InvalidOperationException("Cannot get SerializationBinder because an ISerializationBinder was previously set.");
			}
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value", "Serialization binder cannot be null.");
				}
				_serializationBinder = (value as ISerializationBinder) ?? new SerializationBinderAdapter(value);
			}
		}

		public virtual ISerializationBinder SerializationBinder
		{
			get
			{
				return _serializationBinder;
			}
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value", "Serialization binder cannot be null.");
				}
				_serializationBinder = value;
			}
		}

		public virtual ITraceWriter? TraceWriter
		{
			get
			{
				return _traceWriter;
			}
			set
			{
				_traceWriter = value;
			}
		}

		public virtual IEqualityComparer? EqualityComparer
		{
			get
			{
				return _equalityComparer;
			}
			set
			{
				_equalityComparer = value;
			}
		}

		public virtual TypeNameHandling TypeNameHandling
		{
			get
			{
				return _typeNameHandling;
			}
			set
			{
				if (value < TypeNameHandling.None || value > TypeNameHandling.Auto)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_typeNameHandling = value;
			}
		}

		[Obsolete("TypeNameAssemblyFormat is obsolete. Use TypeNameAssemblyFormatHandling instead.")]
		public virtual FormatterAssemblyStyle TypeNameAssemblyFormat
		{
			get
			{
				return (FormatterAssemblyStyle)_typeNameAssemblyFormatHandling;
			}
			set
			{
				if (value < FormatterAssemblyStyle.Simple || value > FormatterAssemblyStyle.Full)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_typeNameAssemblyFormatHandling = (TypeNameAssemblyFormatHandling)value;
			}
		}

		public virtual TypeNameAssemblyFormatHandling TypeNameAssemblyFormatHandling
		{
			get
			{
				return _typeNameAssemblyFormatHandling;
			}
			set
			{
				if (value < TypeNameAssemblyFormatHandling.Simple || value > TypeNameAssemblyFormatHandling.Full)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_typeNameAssemblyFormatHandling = value;
			}
		}

		public virtual PreserveReferencesHandling PreserveReferencesHandling
		{
			get
			{
				return _preserveReferencesHandling;
			}
			set
			{
				if (value < PreserveReferencesHandling.None || value > PreserveReferencesHandling.All)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_preserveReferencesHandling = value;
			}
		}

		public virtual ReferenceLoopHandling ReferenceLoopHandling
		{
			get
			{
				return _referenceLoopHandling;
			}
			set
			{
				if (value < ReferenceLoopHandling.Error || value > ReferenceLoopHandling.Serialize)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_referenceLoopHandling = value;
			}
		}

		public virtual MissingMemberHandling MissingMemberHandling
		{
			get
			{
				return _missingMemberHandling;
			}
			set
			{
				if (value < MissingMemberHandling.Ignore || value > MissingMemberHandling.Error)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_missingMemberHandling = value;
			}
		}

		public virtual NullValueHandling NullValueHandling
		{
			get
			{
				return _nullValueHandling;
			}
			set
			{
				if (value < NullValueHandling.Include || value > NullValueHandling.Ignore)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_nullValueHandling = value;
			}
		}

		public virtual DefaultValueHandling DefaultValueHandling
		{
			get
			{
				return _defaultValueHandling;
			}
			set
			{
				if (value < DefaultValueHandling.Include || value > DefaultValueHandling.IgnoreAndPopulate)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_defaultValueHandling = value;
			}
		}

		public virtual ObjectCreationHandling ObjectCreationHandling
		{
			get
			{
				return _objectCreationHandling;
			}
			set
			{
				if (value < ObjectCreationHandling.Auto || value > ObjectCreationHandling.Replace)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_objectCreationHandling = value;
			}
		}

		public virtual ConstructorHandling ConstructorHandling
		{
			get
			{
				return _constructorHandling;
			}
			set
			{
				if (value < ConstructorHandling.Default || value > ConstructorHandling.AllowNonPublicDefaultConstructor)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_constructorHandling = value;
			}
		}

		public virtual MetadataPropertyHandling MetadataPropertyHandling
		{
			get
			{
				return _metadataPropertyHandling;
			}
			set
			{
				if (value < MetadataPropertyHandling.Default || value > MetadataPropertyHandling.Ignore)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				_metadataPropertyHandling = value;
			}
		}

		public virtual JsonConverterCollection Converters
		{
			get
			{
				if (_converters == null)
				{
					_converters = new JsonConverterCollection();
				}
				return _converters;
			}
		}

		public virtual IContractResolver ContractResolver
		{
			get
			{
				return _contractResolver;
			}
			set
			{
				_contractResolver = value ?? DefaultContractResolver.Instance;
			}
		}

		public virtual StreamingContext Context
		{
			get
			{
				return _context;
			}
			set
			{
				_context = value;
			}
		}

		public virtual Formatting Formatting
		{
			get
			{
				return _formatting.GetValueOrDefault();
			}
			set
			{
				_formatting = value;
			}
		}

		public virtual DateFormatHandling DateFormatHandling
		{
			get
			{
				return _dateFormatHandling.GetValueOrDefault();
			}
			set
			{
				_dateFormatHandling = value;
			}
		}

		public virtual DateTimeZoneHandling DateTimeZoneHandling
		{
			get
			{
				return _dateTimeZoneHandling ?? DateTimeZoneHandling.RoundtripKind;
			}
			set
			{
				_dateTimeZoneHandling = value;
			}
		}

		public virtual DateParseHandling DateParseHandling
		{
			get
			{
				return _dateParseHandling ?? DateParseHandling.DateTime;
			}
			set
			{
				_dateParseHandling = value;
			}
		}

		public virtual FloatParseHandling FloatParseHandling
		{
			get
			{
				return _floatParseHandling.GetValueOrDefault();
			}
			set
			{
				_floatParseHandling = value;
			}
		}

		public virtual FloatFormatHandling FloatFormatHandling
		{
			get
			{
				return _floatFormatHandling.GetValueOrDefault();
			}
			set
			{
				_floatFormatHandling = value;
			}
		}

		public virtual StringEscapeHandling StringEscapeHandling
		{
			get
			{
				return _stringEscapeHandling.GetValueOrDefault();
			}
			set
			{
				_stringEscapeHandling = value;
			}
		}

		public virtual string DateFormatString
		{
			get
			{
				return _dateFormatString ?? "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
			}
			set
			{
				_dateFormatString = value;
				_dateFormatStringSet = true;
			}
		}

		public virtual CultureInfo Culture
		{
			get
			{
				return _culture ?? JsonSerializerSettings.DefaultCulture;
			}
			set
			{
				_culture = value;
			}
		}

		public virtual int? MaxDepth
		{
			get
			{
				return _maxDepth;
			}
			set
			{
				if (value <= 0)
				{
					throw new ArgumentException("Value must be positive.", "value");
				}
				_maxDepth = value;
				_maxDepthSet = true;
			}
		}

		public virtual bool CheckAdditionalContent
		{
			get
			{
				return _checkAdditionalContent.GetValueOrDefault();
			}
			set
			{
				_checkAdditionalContent = value;
			}
		}

		public virtual event EventHandler<Newtonsoft.Json.Serialization.ErrorEventArgs>? Error;

		internal bool IsCheckAdditionalContentSet()
		{
			return _checkAdditionalContent.HasValue;
		}

		public JsonSerializer()
		{
			_referenceLoopHandling = ReferenceLoopHandling.Error;
			_missingMemberHandling = MissingMemberHandling.Ignore;
			_nullValueHandling = NullValueHandling.Include;
			_defaultValueHandling = DefaultValueHandling.Include;
			_objectCreationHandling = ObjectCreationHandling.Auto;
			_preserveReferencesHandling = PreserveReferencesHandling.None;
			_constructorHandling = ConstructorHandling.Default;
			_typeNameHandling = TypeNameHandling.None;
			_metadataPropertyHandling = MetadataPropertyHandling.Default;
			_context = JsonSerializerSettings.DefaultContext;
			_serializationBinder = DefaultSerializationBinder.Instance;
			_culture = JsonSerializerSettings.DefaultCulture;
			_contractResolver = DefaultContractResolver.Instance;
		}

		public static JsonSerializer Create()
		{
			return new JsonSerializer();
		}

		public static JsonSerializer Create(JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = Create();
			if (settings != null)
			{
				ApplySerializerSettings(jsonSerializer, settings);
			}
			return jsonSerializer;
		}

		public static JsonSerializer CreateDefault()
		{
			JsonSerializerSettings settings = JsonConvert.DefaultSettings?.Invoke();
			return Create(settings);
		}

		public static JsonSerializer CreateDefault(JsonSerializerSettings? settings)
		{
			JsonSerializer jsonSerializer = CreateDefault();
			if (settings != null)
			{
				ApplySerializerSettings(jsonSerializer, settings);
			}
			return jsonSerializer;
		}

		private static void ApplySerializerSettings(JsonSerializer serializer, JsonSerializerSettings settings)
		{
			if (!CollectionUtils.IsNullOrEmpty(settings.Converters))
			{
				for (int i = 0; i < settings.Converters.Count; i++)
				{
					serializer.Converters.Insert(i, settings.Converters[i]);
				}
			}
			if (settings._typeNameHandling.HasValue)
			{
				serializer.TypeNameHandling = settings.TypeNameHandling;
			}
			if (settings._metadataPropertyHandling.HasValue)
			{
				serializer.MetadataPropertyHandling = settings.MetadataPropertyHandling;
			}
			if (settings._typeNameAssemblyFormatHandling.HasValue)
			{
				serializer.TypeNameAssemblyFormatHandling = settings.TypeNameAssemblyFormatHandling;
			}
			if (settings._preserveReferencesHandling.HasValue)
			{
				serializer.PreserveReferencesHandling = settings.PreserveReferencesHandling;
			}
			if (settings._referenceLoopHandling.HasValue)
			{
				serializer.ReferenceLoopHandling = settings.ReferenceLoopHandling;
			}
			if (settings._missingMemberHandling.HasValue)
			{
				serializer.MissingMemberHandling = settings.MissingMemberHandling;
			}
			if (settings._objectCreationHandling.HasValue)
			{
				serializer.ObjectCreationHandling = settings.ObjectCreationHandling;
			}
			if (settings._nullValueHandling.HasValue)
			{
				serializer.NullValueHandling = settings.NullValueHandling;
			}
			if (settings._defaultValueHandling.HasValue)
			{
				serializer.DefaultValueHandling = settings.DefaultValueHandling;
			}
			if (settings._constructorHandling.HasValue)
			{
				serializer.ConstructorHandling = settings.ConstructorHandling;
			}
			if (settings._context.HasValue)
			{
				serializer.Context = settings.Context;
			}
			if (settings._checkAdditionalContent.HasValue)
			{
				serializer._checkAdditionalContent = settings._checkAdditionalContent;
			}
			if (settings.Error != null)
			{
				serializer.Error += settings.Error;
			}
			if (settings.ContractResolver != null)
			{
				serializer.ContractResolver = settings.ContractResolver;
			}
			if (settings.ReferenceResolverProvider != null)
			{
				serializer.ReferenceResolver = settings.ReferenceResolverProvider();
			}
			if (settings.TraceWriter != null)
			{
				serializer.TraceWriter = settings.TraceWriter;
			}
			if (settings.EqualityComparer != null)
			{
				serializer.EqualityComparer = settings.EqualityComparer;
			}
			if (settings.SerializationBinder != null)
			{
				serializer.SerializationBinder = settings.SerializationBinder;
			}
			if (settings._formatting.HasValue)
			{
				serializer._formatting = settings._formatting;
			}
			if (settings._dateFormatHandling.HasValue)
			{
				serializer._dateFormatHandling = settings._dateFormatHandling;
			}
			if (settings._dateTimeZoneHandling.HasValue)
			{
				serializer._dateTimeZoneHandling = settings._dateTimeZoneHandling;
			}
			if (settings._dateParseHandling.HasValue)
			{
				serializer._dateParseHandling = settings._dateParseHandling;
			}
			if (settings._dateFormatStringSet)
			{
				serializer._dateFormatString = settings._dateFormatString;
				serializer._dateFormatStringSet = settings._dateFormatStringSet;
			}
			if (settings._floatFormatHandling.HasValue)
			{
				serializer._floatFormatHandling = settings._floatFormatHandling;
			}
			if (settings._floatParseHandling.HasValue)
			{
				serializer._floatParseHandling = settings._floatParseHandling;
			}
			if (settings._stringEscapeHandling.HasValue)
			{
				serializer._stringEscapeHandling = settings._stringEscapeHandling;
			}
			if (settings._culture != null)
			{
				serializer._culture = settings._culture;
			}
			if (settings._maxDepthSet)
			{
				serializer._maxDepth = settings._maxDepth;
				serializer._maxDepthSet = settings._maxDepthSet;
			}
		}

		[DebuggerStepThrough]
		public void Populate(TextReader reader, object target)
		{
			Populate(new JsonTextReader(reader), target);
		}

		[DebuggerStepThrough]
		public void Populate(JsonReader reader, object target)
		{
			PopulateInternal(reader, target);
		}

		internal virtual void PopulateInternal(JsonReader reader, object target)
		{
			ValidationUtils.ArgumentNotNull(reader, "reader");
			ValidationUtils.ArgumentNotNull(target, "target");
			SetupReader(reader, out CultureInfo previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string previousDateFormatString);
			TraceJsonReader traceJsonReader = ((TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) ? CreateTraceJsonReader(reader) : null);
			JsonSerializerInternalReader jsonSerializerInternalReader = new JsonSerializerInternalReader(this);
			jsonSerializerInternalReader.Populate(traceJsonReader ?? reader, target);
			if (traceJsonReader != null)
			{
				TraceWriter.Trace(TraceLevel.Verbose, traceJsonReader.GetDeserializedJsonMessage(), null);
			}
			ResetReader(reader, previousCulture, previousDateTimeZoneHandling, previousDateParseHandling, previousFloatParseHandling, previousMaxDepth, previousDateFormatString);
		}

		[DebuggerStepThrough]
		public object? Deserialize(JsonReader reader)
		{
			return Deserialize(reader, null);
		}

		[DebuggerStepThrough]
		public object? Deserialize(TextReader reader, Type objectType)
		{
			return Deserialize(new JsonTextReader(reader), objectType);
		}

		[DebuggerStepThrough]
		public T? Deserialize<T>(JsonReader reader)
		{
			return (T)Deserialize(reader, typeof(T));
		}

		[DebuggerStepThrough]
		public object? Deserialize(JsonReader reader, Type? objectType)
		{
			return DeserializeInternal(reader, objectType);
		}

		internal virtual object? DeserializeInternal(JsonReader reader, Type? objectType)
		{
			ValidationUtils.ArgumentNotNull(reader, "reader");
			SetupReader(reader, out CultureInfo previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string previousDateFormatString);
			TraceJsonReader traceJsonReader = ((TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) ? CreateTraceJsonReader(reader) : null);
			JsonSerializerInternalReader jsonSerializerInternalReader = new JsonSerializerInternalReader(this);
			object result = jsonSerializerInternalReader.Deserialize(traceJsonReader ?? reader, objectType, CheckAdditionalContent);
			if (traceJsonReader != null)
			{
				TraceWriter.Trace(TraceLevel.Verbose, traceJsonReader.GetDeserializedJsonMessage(), null);
			}
			ResetReader(reader, previousCulture, previousDateTimeZoneHandling, previousDateParseHandling, previousFloatParseHandling, previousMaxDepth, previousDateFormatString);
			return result;
		}

		internal void SetupReader(JsonReader reader, out CultureInfo? previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string? previousDateFormatString)
		{
			if (_culture != null && !_culture.Equals(reader.Culture))
			{
				previousCulture = reader.Culture;
				reader.Culture = _culture;
			}
			else
			{
				previousCulture = null;
			}
			if (_dateTimeZoneHandling.HasValue && reader.DateTimeZoneHandling != _dateTimeZoneHandling)
			{
				previousDateTimeZoneHandling = reader.DateTimeZoneHandling;
				reader.DateTimeZoneHandling = _dateTimeZoneHandling.GetValueOrDefault();
			}
			else
			{
				previousDateTimeZoneHandling = null;
			}
			if (_dateParseHandling.HasValue && reader.DateParseHandling != _dateParseHandling)
			{
				previousDateParseHandling = reader.DateParseHandling;
				reader.DateParseHandling = _dateParseHandling.GetValueOrDefault();
			}
			else
			{
				previousDateParseHandling = null;
			}
			if (_floatParseHandling.HasValue && reader.FloatParseHandling != _floatParseHandling)
			{
				previousFloatParseHandling = reader.FloatParseHandling;
				reader.FloatParseHandling = _floatParseHandling.GetValueOrDefault();
			}
			else
			{
				previousFloatParseHandling = null;
			}
			if (_maxDepthSet && reader.MaxDepth != _maxDepth)
			{
				previousMaxDepth = reader.MaxDepth;
				reader.MaxDepth = _maxDepth;
			}
			else
			{
				previousMaxDepth = null;
			}
			if (_dateFormatStringSet && reader.DateFormatString != _dateFormatString)
			{
				previousDateFormatString = reader.DateFormatString;
				reader.DateFormatString = _dateFormatString;
			}
			else
			{
				previousDateFormatString = null;
			}
			if (reader is JsonTextReader jsonTextReader && jsonTextReader.PropertyNameTable == null && _contractResolver is DefaultContractResolver defaultContractResolver)
			{
				jsonTextReader.PropertyNameTable = defaultContractResolver.GetNameTable();
			}
		}

		private void ResetReader(JsonReader reader, CultureInfo? previousCulture, DateTimeZoneHandling? previousDateTimeZoneHandling, DateParseHandling? previousDateParseHandling, FloatParseHandling? previousFloatParseHandling, int? previousMaxDepth, string? previousDateFormatString)
		{
			if (previousCulture != null)
			{
				reader.Culture = previousCulture;
			}
			if (previousDateTimeZoneHandling.HasValue)
			{
				reader.DateTimeZoneHandling = previousDateTimeZoneHandling.GetValueOrDefault();
			}
			if (previousDateParseHandling.HasValue)
			{
				reader.DateParseHandling = previousDateParseHandling.GetValueOrDefault();
			}
			if (previousFloatParseHandling.HasValue)
			{
				reader.FloatParseHandling = previousFloatParseHandling.GetValueOrDefault();
			}
			if (_maxDepthSet)
			{
				reader.MaxDepth = previousMaxDepth;
			}
			if (_dateFormatStringSet)
			{
				reader.DateFormatString = previousDateFormatString;
			}
			if (reader is JsonTextReader jsonTextReader && jsonTextReader.PropertyNameTable != null && _contractResolver is DefaultContractResolver defaultContractResolver && jsonTextReader.PropertyNameTable == defaultContractResolver.GetNameTable())
			{
				jsonTextReader.PropertyNameTable = null;
			}
		}

		public void Serialize(TextWriter textWriter, object? value)
		{
			Serialize(new JsonTextWriter(textWriter), value);
		}

		public void Serialize(JsonWriter jsonWriter, object? value, Type? objectType)
		{
			SerializeInternal(jsonWriter, value, objectType);
		}

		public void Serialize(TextWriter textWriter, object? value, Type objectType)
		{
			Serialize(new JsonTextWriter(textWriter), value, objectType);
		}

		public void Serialize(JsonWriter jsonWriter, object? value)
		{
			SerializeInternal(jsonWriter, value, null);
		}

		private TraceJsonReader CreateTraceJsonReader(JsonReader reader)
		{
			TraceJsonReader traceJsonReader = new TraceJsonReader(reader);
			if (reader.TokenType != 0)
			{
				traceJsonReader.WriteCurrentToken();
			}
			return traceJsonReader;
		}

		internal virtual void SerializeInternal(JsonWriter jsonWriter, object? value, Type? objectType)
		{
			ValidationUtils.ArgumentNotNull(jsonWriter, "jsonWriter");
			Formatting? formatting = null;
			if (_formatting.HasValue && jsonWriter.Formatting != _formatting)
			{
				formatting = jsonWriter.Formatting;
				jsonWriter.Formatting = _formatting.GetValueOrDefault();
			}
			DateFormatHandling? dateFormatHandling = null;
			if (_dateFormatHandling.HasValue && jsonWriter.DateFormatHandling != _dateFormatHandling)
			{
				dateFormatHandling = jsonWriter.DateFormatHandling;
				jsonWriter.DateFormatHandling = _dateFormatHandling.GetValueOrDefault();
			}
			DateTimeZoneHandling? dateTimeZoneHandling = null;
			if (_dateTimeZoneHandling.HasValue && jsonWriter.DateTimeZoneHandling != _dateTimeZoneHandling)
			{
				dateTimeZoneHandling = jsonWriter.DateTimeZoneHandling;
				jsonWriter.DateTimeZoneHandling = _dateTimeZoneHandling.GetValueOrDefault();
			}
			FloatFormatHandling? floatFormatHandling = null;
			if (_floatFormatHandling.HasValue && jsonWriter.FloatFormatHandling != _floatFormatHandling)
			{
				floatFormatHandling = jsonWriter.FloatFormatHandling;
				jsonWriter.FloatFormatHandling = _floatFormatHandling.GetValueOrDefault();
			}
			StringEscapeHandling? stringEscapeHandling = null;
			if (_stringEscapeHandling.HasValue && jsonWriter.StringEscapeHandling != _stringEscapeHandling)
			{
				stringEscapeHandling = jsonWriter.StringEscapeHandling;
				jsonWriter.StringEscapeHandling = _stringEscapeHandling.GetValueOrDefault();
			}
			CultureInfo cultureInfo = null;
			if (_culture != null && !_culture.Equals(jsonWriter.Culture))
			{
				cultureInfo = jsonWriter.Culture;
				jsonWriter.Culture = _culture;
			}
			string dateFormatString = null;
			if (_dateFormatStringSet && jsonWriter.DateFormatString != _dateFormatString)
			{
				dateFormatString = jsonWriter.DateFormatString;
				jsonWriter.DateFormatString = _dateFormatString;
			}
			TraceJsonWriter traceJsonWriter = ((TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) ? new TraceJsonWriter(jsonWriter) : null);
			JsonSerializerInternalWriter jsonSerializerInternalWriter = new JsonSerializerInternalWriter(this);
			jsonSerializerInternalWriter.Serialize(traceJsonWriter ?? jsonWriter, value, objectType);
			if (traceJsonWriter != null)
			{
				TraceWriter.Trace(TraceLevel.Verbose, traceJsonWriter.GetSerializedJsonMessage(), null);
			}
			if (formatting.HasValue)
			{
				jsonWriter.Formatting = formatting.GetValueOrDefault();
			}
			if (dateFormatHandling.HasValue)
			{
				jsonWriter.DateFormatHandling = dateFormatHandling.GetValueOrDefault();
			}
			if (dateTimeZoneHandling.HasValue)
			{
				jsonWriter.DateTimeZoneHandling = dateTimeZoneHandling.GetValueOrDefault();
			}
			if (floatFormatHandling.HasValue)
			{
				jsonWriter.FloatFormatHandling = floatFormatHandling.GetValueOrDefault();
			}
			if (stringEscapeHandling.HasValue)
			{
				jsonWriter.StringEscapeHandling = stringEscapeHandling.GetValueOrDefault();
			}
			if (_dateFormatStringSet)
			{
				jsonWriter.DateFormatString = dateFormatString;
			}
			if (cultureInfo != null)
			{
				jsonWriter.Culture = cultureInfo;
			}
		}

		internal IReferenceResolver GetReferenceResolver()
		{
			if (_referenceResolver == null)
			{
				_referenceResolver = new DefaultReferenceResolver();
			}
			return _referenceResolver;
		}

		internal JsonConverter? GetMatchingConverter(Type type)
		{
			return GetMatchingConverter(_converters, type);
		}

		internal static JsonConverter? GetMatchingConverter(IList<JsonConverter>? converters, Type objectType)
		{
			ValidationUtils.ArgumentNotNull(objectType, "objectType");
			if (converters != null)
			{
				for (int i = 0; i < converters.Count; i++)
				{
					JsonConverter jsonConverter = converters[i];
					if (jsonConverter.CanConvert(objectType))
					{
						return jsonConverter;
					}
				}
			}
			return null;
		}

		internal void OnError(Newtonsoft.Json.Serialization.ErrorEventArgs e)
		{
			this.Error?.Invoke(this, e);
		}
	}
	public class JsonSerializerSettings
	{
		internal const ReferenceLoopHandling DefaultReferenceLoopHandling = ReferenceLoopHandling.Error;

		internal const MissingMemberHandling DefaultMissingMemberHandling = MissingMemberHandling.Ignore;

		internal const NullValueHandling DefaultNullValueHandling = NullValueHandling.Include;

		internal const DefaultValueHandling DefaultDefaultValueHandling = DefaultValueHandling.Include;

		internal const ObjectCreationHandling DefaultObjectCreationHandling = ObjectCreationHandling.Auto;

		internal const PreserveReferencesHandling DefaultPreserveReferencesHandling = PreserveReferencesHandling.None;

		internal const ConstructorHandling DefaultConstructorHandling = ConstructorHandling.Default;

		internal const TypeNameHandling DefaultTypeNameHandling = TypeNameHandling.None;

		internal const MetadataPropertyHandling DefaultMetadataPropertyHandling = MetadataPropertyHandling.Default;

		internal static readonly StreamingContext DefaultContext;

		internal const Formatting DefaultFormatting = Formatting.None;

		internal const DateFormatHandling DefaultDateFormatHandling = DateFormatHandling.IsoDateFormat;

		internal const DateTimeZoneHandling DefaultDateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;

		internal const DateParseHandling DefaultDateParseHandling = DateParseHandling.DateTime;

		internal const FloatParseHandling DefaultFloatParseHandling = FloatParseHandling.Double;

		internal const FloatFormatHandling DefaultFloatFormatHandling = FloatFormatHandling.String;

		internal const StringEscapeHandling DefaultStringEscapeHandling = StringEscapeHandling.Default;

		internal const TypeNameAssemblyFormatHandling DefaultTypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple;

		internal static readonly CultureInfo DefaultCulture;

		internal const bool DefaultCheckAdditionalContent = false;

		internal const string DefaultDateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";

		internal const int DefaultMaxDepth = 64;

		internal Formatting? _formatting;

		internal DateFormatHandling? _dateFormatHandling;

		internal DateTimeZoneHandling? _dateTimeZoneHandling;

		internal DateParseHandling? _dateParseHandling;

		internal FloatFormatHandling? _floatFormatHandling;

		internal FloatParseHandling? _floatParseHandling;

		internal StringEscapeHandling? _stringEscapeHandling;

		internal CultureInfo? _culture;

		internal bool? _checkAdditionalContent;

		internal int? _maxDepth;

		internal bool _maxDepthSet;

		internal string? _dateFormatString;

		internal bool _dateFormatStringSet;

		internal TypeNameAssemblyFormatHandling? _typeNameAssemblyFormatHandling;

		internal DefaultValueHandling? _defaultValueHandling;

		internal PreserveReferencesHandling? _preserveReferencesHandling;

		internal NullValueHandling? _nullValueHandling;

		internal ObjectCreationHandling? _objectCreationHandling;

		internal MissingMemberHandling? _missingMemberHandling;

		internal ReferenceLoopHandling? _referenceLoopHandling;

		internal StreamingContext? _context;

		internal ConstructorHandling? _constructorHandling;

		internal TypeNameHandling? _typeNameHandling;

		internal MetadataPropertyHandling? _metadataPropertyHandling;

		public ReferenceLoopHandling ReferenceLoopHandling
		{
			get
			{
				return _referenceLoopHandling.GetValueOrDefault();
			}
			set
			{
				_referenceLoopHandling = value;
			}
		}

		public MissingMemberHandling MissingMemberHandling
		{
			get
			{
				return _missingMemberHandling.GetValueOrDefault();
			}
			set
			{
				_missingMemberHandling = value;
			}
		}

		public ObjectCreationHandling ObjectCreationHandling
		{
			get
			{
				return _objectCreationHandling.GetValueOrDefault();
			}
			set
			{
				_objectCreationHandling = value;
			}
		}

		public NullValueHandling NullValueHandling
		{
			get
			{
				return _nullValueHandling.GetValueOrDefault();
			}
			set
			{
				_nullValueHandling = value;
			}
		}

		public DefaultValueHandling DefaultValueHandling
		{
			get
			{
				return _defaultValueHandling.GetValueOrDefault();
			}
			set
			{
				_defaultValueHandling = value;
			}
		}

		public IList<JsonConverter> Converters { get; set; }

		public PreserveReferencesHandling PreserveReferencesHandling
		{
			get
			{
				return _preserveReferencesHandling.GetValueOrDefault();
			}
			set
			{
				_preserveReferencesHandling = value;
			}
		}

		public TypeNameHandling TypeNameHandling
		{
			get
			{
				return _typeNameHandling.GetValueOrDefault();
			}
			set
			{
				_typeNameHandling = value;
			}
		}

		public MetadataPropertyHandling MetadataPropertyHandling
		{
			get
			{
				return _metadataPropertyHandling.GetValueOrDefault();
			}
			set
			{
				_metadataPropertyHandling = value;
			}
		}

		[Obsolete("TypeNameAssemblyFormat is obsolete. Use TypeNameAssemblyFormatHandling instead.")]
		public FormatterAssemblyStyle TypeNameAssemblyFormat
		{
			get
			{
				return (FormatterAssemblyStyle)TypeNameAssemblyFormatHandling;
			}
			set
			{
				TypeNameAssemblyFormatHandling = (TypeNameAssemblyFormatHandling)value;
			}
		}

		public TypeNameAssemblyFormatHandling TypeNameAssemblyFormatHandling
		{
			get
			{
				return _typeNameAssemblyFormatHandling.GetValueOrDefault();
			}
			set
			{
				_typeNameAssemblyFormatHandling = value;
			}
		}

		public ConstructorHandling ConstructorHandling
		{
			get
			{
				return _constructorHandling.GetValueOrDefault();
			}
			set
			{
				_constructorHandling = value;
			}
		}

		public IContractResolver? ContractResolver { get; set; }

		public IEqualityComparer? EqualityComparer { get; set; }

		[Obsolete("ReferenceResolver property is obsolete. Use the ReferenceResolverProvider property to set the IReferenceResolver: settings.ReferenceResolverProvider = () => resolver")]
		public IReferenceResolver? ReferenceResolver
		{
			get
			{
				return ReferenceResolverProvider?.Invoke();
			}
			set
			{
				IReferenceResolver value2 = value;
				ReferenceResolverProvider = ((value2 != null) ? ((Func<IReferenceResolver>)(() => value2)) : null);
			}
		}

		public Func<IReferenceResolver?>? ReferenceResolverProvider { get; set; }

		public ITraceWriter? TraceWriter { get; set; }

		[Obsolete("Binder is obsolete. Use SerializationBinder instead.")]
		public SerializationBinder? Binder
		{
			get
			{
				if (SerializationBinder == null)
				{
					return null;
				}
				if (SerializationBinder is SerializationBinderAdapter serializationBinderAdapter)
				{
					return serializationBinderAdapter.SerializationBinder;
				}
				throw new InvalidOperationException("Cannot get SerializationBinder because an ISerializationBinder was previously set.");
			}
			set
			{
				SerializationBinder = ((value == null) ? null : new SerializationBinderAdapter(value));
			}
		}

		public ISerializationBinder? SerializationBinder { get; set; }

		public EventHandler<Newtonsoft.Json.Serialization.ErrorEventArgs>? Error { get; set; }

		public StreamingContext Context
		{
			get
			{
				return _context ?? DefaultContext;
			}
			set
			{
				_context = value;
			}
		}

		public string DateFormatString
		{
			get
			{
				return _dateFormatString ?? "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
			}
			set
			{
				_dateFormatString = value;
				_dateFormatStringSet = true;
			}
		}

		public int? MaxDepth
		{
			get
			{
				return _maxDepthSet ? _maxDepth : new int?(64);
			}
			set
			{
				if (value <= 0)
				{
					throw new ArgumentException("Value must be positive.", "value");
				}
				_maxDepth = value;
				_maxDepthSet = true;
			}
		}

		public Formatting Formatting
		{
			get
			{
				return _formatting.GetValueOrDefault();
			}
			set
			{
				_formatting = value;
			}
		}

		public DateFormatHandling DateFormatHandling
		{
			get
			{
				return _dateFormatHandling.GetValueOrDefault();
			}
			set
			{
				_dateFormatHandling = value;
			}
		}

		public DateTimeZoneHandling DateTimeZoneHandling
		{
			get
			{
				return _dateTimeZoneHandling ?? DateTimeZoneHandling.RoundtripKind;
			}
			set
			{
				_dateTimeZoneHandling = value;
			}
		}

		public DateParseHandling DateParseHandling
		{
			get
			{
				return _dateParseHandling ?? DateParseHandling.DateT