Decompiled source of WoLArchipelago v1.0.0

BepInEx/plugins/WoLArchipelago/Newtonsoft.Json.dll

Decompiled 2 weeks ago
#define DEBUG
using System;
using System.Collections;
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.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
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: 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+bdfeb80d3eb277241ce8f051a360c9461b33afc5")]
[assembly: AssemblyProduct("Json.NET")]
[assembly: AssemblyTitle("Json.NET .NET 3.5")]
[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);
		}

		[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), 
				_ => 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 ((object)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;
			}
		}

		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;
				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 = Convert.ToDouble(value, CultureInfo.InvariantCulture);
				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 = Convert.ToBoolean(Value, CultureInfo.InvariantCulture);
				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;
				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.DateTime;
			}
			set
			{
				_dateParseHandling = value;
			}
		}

		public FloatFormatHandling FloatFormatHandling
		{
			get
			{
				return _floatFormatHandling.GetValueOrDefault();
			}
			set
			{
				_floatFormatHandling = value;
			}
		}

		public FloatParseHandling FloatParseHandling
		{
			get
			{
				return _floatParseHandling.GetValueOrDefault();
			}
			set
			{
				_floatParseHandling = value;
			}
		}

		public StringEscapeHandling StringEscapeHandling
		{
			get
			{
				return _stringEscapeHandling.GetValueOrDefault();
			}
			set
			{
				_stringEscapeHandling = value;
			}
		}

		public CultureInfo Culture
		{
			get
			{
				return _culture ?? DefaultCulture;
			}
			set
			{
				_culture = value;
			}
		}

		public bool CheckAdditionalContent
		{
			get
			{
				return _checkAdditionalContent.GetValueOrDefault();
			}
			set
			{
				_checkAdditionalContent = value;
			}
		}

		static JsonSerializerSettings()
		{
			DefaultContext = default(StreamingContext);
			DefaultCulture = CultureInfo.InvariantCulture;
		}

		[DebuggerStepThrough]
		public JsonSerializerSettings()
		{
			Converters = new List<JsonConverter>();
		}
	}
	internal enum ReadType
	{
		Read,
		ReadAsInt32,
		ReadAsInt64,
		ReadAsBytes,
		ReadAsString,
		ReadAsDecimal,
		ReadAsDateTime,
		ReadAsDateTimeOffset,
		ReadAsDouble,
		ReadAsBoolean
	}
	public class JsonTextReader : JsonReader, IJsonLineInfo
	{
		private const char UnicodeReplacementChar = '\ufffd';

		private readonly TextReader _reader;

		private char[]? _chars;

		private int _charsUsed;

		private int _charPos;

		private int _lineStartPos;

		private int _lineNumber;

		private bool _isEndOfFile;

		private StringBuffer _stringBuffer;

		private StringReference _stringReference;

		private IArrayPool<char>? _arrayPool;

		internal int LargeBufferLength { get; set; } = 1073741823;


		internal char[]? CharBuffer
		{
			get
			{
				return _chars;
			}
			set
			{
				_chars = value;
			}
		}

		internal int CharPos => _charPos;

		public JsonNameTable? PropertyNameTable { get; set; }

		public IArrayPool<char>? ArrayPool
		{
			get
			{
				return _arrayPool;
			}
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}
				_arrayPool = value;
			}
		}

		public int LineNumber
		{
			get
			{
				if (base.CurrentState == State.Start && LinePosition == 0 && TokenType != JsonToken.Comment)
				{
					return 0;
				}
				return _lineNumber;
			}
		}

		public int LinePosition => _charPos - _lineStartPos;

		public JsonTextReader(TextReader reader)
		{
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			_reader = reader;
			_lineNumber = 1;
		}

		private void EnsureBufferNotEmpty()
		{
			if (_stringBuffer.IsEmpty)
			{
				_stringBuffer = new StringBuffer(_arrayPool, 1024);
			}
		}

		private void SetNewLine(bool hasNextChar)
		{
			MiscellaneousUtils.Assert(_chars != null);
			if (hasNextChar && _chars[_charPos] == '\n')
			{
				_charPos++;
			}
			OnNewLine(_charPos);
		}

		private void OnNewLine(int pos)
		{
			_lineNumber++;
			_lineStartPos = pos;
		}

		private void ParseString(char quote, ReadType readType)
		{
			_charPos++;
			ShiftBufferIfNeeded();
			ReadStringIntoBuffer(quote);
			ParseReadString(quote, readType);
		}

		private void ParseReadString(char quote, ReadType readType)
		{
			SetPostValueState(updateIndex: true);
			switch (readType)
			{
			case ReadType.ReadAsBytes:
			{
				Guid g;
				byte[] value2 = ((_stringReference.Length == 0) ? CollectionUtils.ArrayEmpty<byte>() : ((_stringReference.Length != 36 || !ConvertUtils.TryConvertGuid(_stringReference.ToString(), out g)) ? Convert.FromBase64CharArray(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length) : g.ToByteArray()));
				SetToken(JsonToken.Bytes, value2, updateIndex: false);
				return;
			}
			case ReadType.ReadAsString:
			{
				string value = _stringReference.ToString();
				SetToken(JsonToken.String, value, updateIndex: false);
				_quoteChar = quote;
				return;
			}
			case ReadType.ReadAsInt32:
			case ReadType.ReadAsDecimal:
			case ReadType.ReadAsBoolean:
				return;
			}
			if (_dateParseHandling != 0)
			{
				DateTimeOffset dt2;
				if (readType switch
				{
					ReadType.ReadAsDateTime => 1, 
					ReadType.ReadAsDateTimeOffset => 2, 
					_ => (int)_dateParseHandling, 
				} == 1)
				{
					if (DateTimeUtils.TryParseDateTime(_stringReference, base.DateTimeZoneHandling, base.DateFormatString, base.Culture, out var dt))
					{
						SetToken(JsonToken.Date, dt, updateIndex: false);
						return;
					}
				}
				else if (DateTimeUtils.TryParseDateTimeOffset(_stringReference, base.DateFormatString, base.Culture, out dt2))
				{
					SetToken(JsonToken.Date, dt2, updateIndex: false);
					return;
				}
			}
			SetToken(JsonToken.String, _stringReference.ToString(), updateIndex: false);
			_quoteChar = quote;
		}

		private static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count)
		{
			Buffer.BlockCopy(src, srcOffset * 2, dst, dstOffset * 

BepInEx/plugins/WoLArchipelago/websocket-sharp.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Timers;
using WebSocketSharp.Net;
using WebSocketSharp.Net.WebSockets;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("websocket-sharp")]
[assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("websocket-sharp.dll")]
[assembly: AssemblyCopyright("sta.blockhead")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("1.0.2.34775")]
namespace WebSocketSharp
{
	public static class Ext
	{
		private static readonly byte[] _last = new byte[1];

		private static readonly int _retry = 5;

		private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";

		private static byte[] compress(this byte[] data)
		{
			if (data.LongLength == 0)
			{
				return data;
			}
			using MemoryStream stream = new MemoryStream(data);
			return stream.compressToArray();
		}

		private static MemoryStream compress(this Stream stream)
		{
			MemoryStream memoryStream = new MemoryStream();
			if (stream.Length == 0)
			{
				return memoryStream;
			}
			stream.Position = 0L;
			using DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress, leaveOpen: true);
			CopyTo(stream, deflateStream, 1024);
			deflateStream.Close();
			memoryStream.Write(_last, 0, 1);
			memoryStream.Position = 0L;
			return memoryStream;
		}

		private static byte[] compressToArray(this Stream stream)
		{
			using MemoryStream memoryStream = stream.compress();
			memoryStream.Close();
			return memoryStream.ToArray();
		}

		private static byte[] decompress(this byte[] data)
		{
			if (data.LongLength == 0)
			{
				return data;
			}
			using MemoryStream stream = new MemoryStream(data);
			return stream.decompressToArray();
		}

		private static MemoryStream decompress(this Stream stream)
		{
			MemoryStream memoryStream = new MemoryStream();
			if (stream.Length == 0)
			{
				return memoryStream;
			}
			stream.Position = 0L;
			using DeflateStream source = new DeflateStream(stream, CompressionMode.Decompress, leaveOpen: true);
			CopyTo(source, memoryStream, 1024);
			memoryStream.Position = 0L;
			return memoryStream;
		}

		private static byte[] decompressToArray(this Stream stream)
		{
			using MemoryStream memoryStream = stream.decompress();
			memoryStream.Close();
			return memoryStream.ToArray();
		}

		private static bool isHttpMethod(this string value)
		{
			int result;
			switch (value)
			{
			default:
				result = ((value == "TRACE") ? 1 : 0);
				break;
			case "GET":
			case "HEAD":
			case "POST":
			case "PUT":
			case "DELETE":
			case "CONNECT":
			case "OPTIONS":
				result = 1;
				break;
			}
			return (byte)result != 0;
		}

		private static bool isHttpMethod10(this string value)
		{
			return value == "GET" || value == "HEAD" || value == "POST";
		}

		internal static byte[] Append(this ushort code, string reason)
		{
			byte[] array = code.InternalToByteArray(ByteOrder.Big);
			if (reason == null || reason.Length == 0)
			{
				return array;
			}
			List<byte> list = new List<byte>(array);
			list.AddRange(Encoding.UTF8.GetBytes(reason));
			return list.ToArray();
		}

		internal static byte[] Compress(this byte[] data, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? data.compress() : data;
		}

		internal static Stream Compress(this Stream stream, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? stream.compress() : stream;
		}

		internal static byte[] CompressToArray(this Stream stream, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? stream.compressToArray() : stream.ToByteArray();
		}

		internal static bool Contains(this string value, params char[] anyOf)
		{
			return anyOf != null && anyOf.Length != 0 && value.IndexOfAny(anyOf) > -1;
		}

		internal static bool Contains(this NameValueCollection collection, string name)
		{
			return collection[name] != null;
		}

		internal static bool Contains(this NameValueCollection collection, string name, string value, StringComparison comparisonTypeForValue)
		{
			string text = collection[name];
			if (text == null)
			{
				return false;
			}
			string[] array = text.Split(new char[1] { ',' });
			foreach (string text2 in array)
			{
				if (text2.Trim().Equals(value, comparisonTypeForValue))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition)
		{
			foreach (T item in source)
			{
				if (condition(item))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool ContainsTwice(this string[] values)
		{
			int len = values.Length;
			int end = len - 1;
			Func<int, bool> seek = null;
			seek = delegate(int idx)
			{
				if (idx == end)
				{
					return false;
				}
				string text = values[idx];
				for (int i = idx + 1; i < len; i++)
				{
					if (values[i] == text)
					{
						return true;
					}
				}
				return seek(++idx);
			};
			return seek(0);
		}

		internal static T[] Copy<T>(this T[] source, int length)
		{
			T[] array = new T[length];
			Array.Copy(source, 0, array, 0, length);
			return array;
		}

		internal static T[] Copy<T>(this T[] source, long length)
		{
			T[] array = new T[length];
			Array.Copy(source, 0L, array, 0L, length);
			return array;
		}

		internal static void CopyTo(this Stream source, Stream destination, int bufferLength)
		{
			byte[] buffer = new byte[bufferLength];
			int num = 0;
			while (true)
			{
				num = source.Read(buffer, 0, bufferLength);
				if (num <= 0)
				{
					break;
				}
				destination.Write(buffer, 0, num);
			}
		}

		internal static void CopyToAsync(this Stream source, Stream destination, int bufferLength, Action completed, Action<Exception> error)
		{
			byte[] buff = new byte[bufferLength];
			AsyncCallback callback = null;
			callback = delegate(IAsyncResult ar)
			{
				try
				{
					int num = source.EndRead(ar);
					if (num <= 0)
					{
						if (completed != null)
						{
							completed();
						}
					}
					else
					{
						destination.Write(buff, 0, num);
						source.BeginRead(buff, 0, bufferLength, callback, null);
					}
				}
				catch (Exception obj2)
				{
					if (error != null)
					{
						error(obj2);
					}
				}
			};
			try
			{
				source.BeginRead(buff, 0, bufferLength, callback, null);
			}
			catch (Exception obj)
			{
				if (error != null)
				{
					error(obj);
				}
			}
		}

		internal static byte[] Decompress(this byte[] data, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? data.decompress() : data;
		}

		internal static Stream Decompress(this Stream stream, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? stream.decompress() : stream;
		}

		internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? stream.decompressToArray() : stream.ToByteArray();
		}

		internal static void Emit(this EventHandler eventHandler, object sender, EventArgs e)
		{
			eventHandler?.Invoke(sender, e);
		}

		internal static void Emit<TEventArgs>(this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e) where TEventArgs : EventArgs
		{
			eventHandler?.Invoke(sender, e);
		}

		internal static bool EqualsWith(this int value, char c, Action<int> action)
		{
			action(value);
			return value == c;
		}

		internal static string GetAbsolutePath(this Uri uri)
		{
			if (uri.IsAbsoluteUri)
			{
				return uri.AbsolutePath;
			}
			string originalString = uri.OriginalString;
			if (originalString[0] != '/')
			{
				return null;
			}
			int num = originalString.IndexOfAny(new char[2] { '?', '#' });
			return (num > 0) ? originalString.Substring(0, num) : originalString;
		}

		internal static WebSocketSharp.Net.CookieCollection GetCookies(this NameValueCollection headers, bool response)
		{
			string text = headers[response ? "Set-Cookie" : "Cookie"];
			return (text != null) ? WebSocketSharp.Net.CookieCollection.Parse(text, response) : new WebSocketSharp.Net.CookieCollection();
		}

		internal static string GetDnsSafeHost(this Uri uri, bool bracketIPv6)
		{
			return (bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6) ? uri.Host : uri.DnsSafeHost;
		}

		internal static string GetMessage(this CloseStatusCode code)
		{
			return code switch
			{
				CloseStatusCode.TlsHandshakeFailure => "An error has occurred during a TLS handshake.", 
				CloseStatusCode.ServerError => "WebSocket server got an internal error.", 
				CloseStatusCode.MandatoryExtension => "WebSocket client didn't receive expected extension(s).", 
				CloseStatusCode.TooBig => "A too big message has been received.", 
				CloseStatusCode.PolicyViolation => "A policy violation has occurred.", 
				CloseStatusCode.InvalidData => "Invalid data has been received.", 
				CloseStatusCode.Abnormal => "An exception has occurred.", 
				CloseStatusCode.UnsupportedData => "Unsupported data has been received.", 
				CloseStatusCode.ProtocolError => "A WebSocket protocol error has occurred.", 
				_ => string.Empty, 
			};
		}

		internal static string GetName(this string nameAndValue, char separator)
		{
			int num = nameAndValue.IndexOf(separator);
			return (num > 0) ? nameAndValue.Substring(0, num).Trim() : null;
		}

		internal static string GetUTF8DecodedString(this byte[] bytes)
		{
			return Encoding.UTF8.GetString(bytes);
		}

		internal static byte[] GetUTF8EncodedBytes(this string s)
		{
			return Encoding.UTF8.GetBytes(s);
		}

		internal static string GetValue(this string nameAndValue, char separator)
		{
			return nameAndValue.GetValue(separator, unquote: false);
		}

		internal static string GetValue(this string nameAndValue, char separator, bool unquote)
		{
			int num = nameAndValue.IndexOf(separator);
			if (num < 0 || num == nameAndValue.Length - 1)
			{
				return null;
			}
			string text = nameAndValue.Substring(num + 1).Trim();
			return unquote ? text.Unquote() : text;
		}

		internal static byte[] InternalToByteArray(this ushort value, ByteOrder order)
		{
			byte[] bytes = BitConverter.GetBytes(value);
			if (!order.IsHostOrder())
			{
				Array.Reverse((Array)bytes);
			}
			return bytes;
		}

		internal static byte[] InternalToByteArray(this ulong value, ByteOrder order)
		{
			byte[] bytes = BitConverter.GetBytes(value);
			if (!order.IsHostOrder())
			{
				Array.Reverse((Array)bytes);
			}
			return bytes;
		}

		internal static bool IsCompressionExtension(this string value, CompressionMethod method)
		{
			return value.StartsWith(method.ToExtensionString());
		}

		internal static bool IsControl(this byte opcode)
		{
			return opcode > 7 && opcode < 16;
		}

		internal static bool IsControl(this Opcode opcode)
		{
			return (int)opcode >= 8;
		}

		internal static bool IsData(this byte opcode)
		{
			return opcode == 1 || opcode == 2;
		}

		internal static bool IsData(this Opcode opcode)
		{
			return opcode == Opcode.Text || opcode == Opcode.Binary;
		}

		internal static bool IsHttpMethod(this string value, Version version)
		{
			return (version == WebSocketSharp.Net.HttpVersion.Version10) ? value.isHttpMethod10() : value.isHttpMethod();
		}

		internal static bool IsPortNumber(this int value)
		{
			return value > 0 && value < 65536;
		}

		internal static bool IsReserved(this ushort code)
		{
			return code == 1004 || code == 1005 || code == 1006 || code == 1015;
		}

		internal static bool IsReserved(this CloseStatusCode code)
		{
			return code == CloseStatusCode.Undefined || code == CloseStatusCode.NoStatus || code == CloseStatusCode.Abnormal || code == CloseStatusCode.TlsHandshakeFailure;
		}

		internal static bool IsSupported(this byte opcode)
		{
			return Enum.IsDefined(typeof(Opcode), opcode);
		}

		internal static bool IsText(this string value)
		{
			int length = value.Length;
			for (int i = 0; i < length; i++)
			{
				char c = value[i];
				if (c < ' ')
				{
					if ("\r\n\t".IndexOf(c) == -1)
					{
						return false;
					}
					if (c == '\n')
					{
						i++;
						if (i == length)
						{
							break;
						}
						c = value[i];
						if (" \t".IndexOf(c) == -1)
						{
							return false;
						}
					}
				}
				else if (c == '\u007f')
				{
					return false;
				}
			}
			return true;
		}

		internal static bool IsToken(this string value)
		{
			foreach (char c in value)
			{
				if (c < ' ')
				{
					return false;
				}
				if (c > '~')
				{
					return false;
				}
				if ("()<>@,;:\\\"/[]?={} \t".IndexOf(c) > -1)
				{
					return false;
				}
			}
			return true;
		}

		internal static bool KeepsAlive(this NameValueCollection headers, Version version)
		{
			StringComparison comparisonTypeForValue = StringComparison.OrdinalIgnoreCase;
			return (version < WebSocketSharp.Net.HttpVersion.Version11) ? headers.Contains("Connection", "keep-alive", comparisonTypeForValue) : (!headers.Contains("Connection", "close", comparisonTypeForValue));
		}

		internal static string Quote(this string value)
		{
			return string.Format("\"{0}\"", value.Replace("\"", "\\\""));
		}

		internal static byte[] ReadBytes(this Stream stream, int length)
		{
			byte[] array = new byte[length];
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			while (length > 0)
			{
				num3 = stream.Read(array, num, length);
				if (num3 <= 0)
				{
					if (num2 >= _retry)
					{
						return array.SubArray(0, num);
					}
					num2++;
				}
				else
				{
					num2 = 0;
					num += num3;
					length -= num3;
				}
			}
			return array;
		}

		internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength)
		{
			using MemoryStream memoryStream = new MemoryStream();
			byte[] buffer = new byte[bufferLength];
			int num = 0;
			int num2 = 0;
			while (length > 0)
			{
				if (length < bufferLength)
				{
					bufferLength = (int)length;
				}
				num2 = stream.Read(buffer, 0, bufferLength);
				if (num2 <= 0)
				{
					if (num >= _retry)
					{
						break;
					}
					num++;
				}
				else
				{
					num = 0;
					memoryStream.Write(buffer, 0, num2);
					length -= num2;
				}
			}
			memoryStream.Close();
			return memoryStream.ToArray();
		}

		internal static void ReadBytesAsync(this Stream stream, int length, Action<byte[]> completed, Action<Exception> error)
		{
			byte[] buff = new byte[length];
			int offset = 0;
			int retry = 0;
			AsyncCallback callback = null;
			callback = delegate(IAsyncResult ar)
			{
				try
				{
					int num = stream.EndRead(ar);
					if (num <= 0)
					{
						if (retry < _retry)
						{
							retry++;
							stream.BeginRead(buff, offset, length, callback, null);
						}
						else if (completed != null)
						{
							completed(buff.SubArray(0, offset));
						}
					}
					else if (num == length)
					{
						if (completed != null)
						{
							completed(buff);
						}
					}
					else
					{
						retry = 0;
						offset += num;
						length -= num;
						stream.BeginRead(buff, offset, length, callback, null);
					}
				}
				catch (Exception obj2)
				{
					if (error != null)
					{
						error(obj2);
					}
				}
			};
			try
			{
				stream.BeginRead(buff, offset, length, callback, null);
			}
			catch (Exception obj)
			{
				if (error != null)
				{
					error(obj);
				}
			}
		}

		internal static void ReadBytesAsync(this Stream stream, long length, int bufferLength, Action<byte[]> completed, Action<Exception> error)
		{
			MemoryStream dest = new MemoryStream();
			byte[] buff = new byte[bufferLength];
			int retry = 0;
			Action<long> read = null;
			read = delegate(long len)
			{
				if (len < bufferLength)
				{
					bufferLength = (int)len;
				}
				stream.BeginRead(buff, 0, bufferLength, delegate(IAsyncResult ar)
				{
					try
					{
						int num = stream.EndRead(ar);
						if (num <= 0)
						{
							if (retry < _retry)
							{
								int num2 = retry;
								retry = num2 + 1;
								read(len);
							}
							else
							{
								if (completed != null)
								{
									dest.Close();
									completed(dest.ToArray());
								}
								dest.Dispose();
							}
						}
						else
						{
							dest.Write(buff, 0, num);
							if (num == len)
							{
								if (completed != null)
								{
									dest.Close();
									completed(dest.ToArray());
								}
								dest.Dispose();
							}
							else
							{
								retry = 0;
								read(len - num);
							}
						}
					}
					catch (Exception obj2)
					{
						dest.Dispose();
						if (error != null)
						{
							error(obj2);
						}
					}
				}, null);
			};
			try
			{
				read(length);
			}
			catch (Exception obj)
			{
				dest.Dispose();
				if (error != null)
				{
					error(obj);
				}
			}
		}

		internal static T[] Reverse<T>(this T[] array)
		{
			int num = array.Length;
			T[] array2 = new T[num];
			int num2 = num - 1;
			for (int i = 0; i <= num2; i++)
			{
				array2[i] = array[num2 - i];
			}
			return array2;
		}

		internal static IEnumerable<string> SplitHeaderValue(this string value, params char[] separators)
		{
			int len = value.Length;
			int end = len - 1;
			StringBuilder buff = new StringBuilder(32);
			bool escaped = false;
			bool quoted = false;
			for (int i = 0; i <= end; i++)
			{
				char c = value[i];
				buff.Append(c);
				switch (c)
				{
				case '"':
					if (escaped)
					{
						escaped = false;
					}
					else
					{
						quoted = !quoted;
					}
					continue;
				case '\\':
					if (i == end)
					{
						break;
					}
					if (value[i + 1] == '"')
					{
						escaped = true;
					}
					continue;
				default:
					if (Array.IndexOf(separators, c) > -1 && !quoted)
					{
						buff.Length--;
						yield return buff.ToString();
						buff.Length = 0;
					}
					continue;
				}
				break;
			}
			yield return buff.ToString();
		}

		internal static byte[] ToByteArray(this Stream stream)
		{
			using MemoryStream memoryStream = new MemoryStream();
			stream.Position = 0L;
			CopyTo(stream, memoryStream, 1024);
			memoryStream.Close();
			return memoryStream.ToArray();
		}

		internal static CompressionMethod ToCompressionMethod(this string value)
		{
			Array values = Enum.GetValues(typeof(CompressionMethod));
			foreach (CompressionMethod item in values)
			{
				if (item.ToExtensionString() == value)
				{
					return item;
				}
			}
			return CompressionMethod.None;
		}

		internal static string ToExtensionString(this CompressionMethod method, params string[] parameters)
		{
			if (method == CompressionMethod.None)
			{
				return string.Empty;
			}
			string text = $"permessage-{method.ToString().ToLower()}";
			return (parameters != null && parameters.Length != 0) ? string.Format("{0}; {1}", text, parameters.ToString("; ")) : text;
		}

		internal static IPAddress ToIPAddress(this string value)
		{
			if (value == null || value.Length == 0)
			{
				return null;
			}
			if (IPAddress.TryParse(value, out IPAddress address))
			{
				return address;
			}
			try
			{
				IPAddress[] hostAddresses = Dns.GetHostAddresses(value);
				return hostAddresses[0];
			}
			catch
			{
				return null;
			}
		}

		internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
		{
			return new List<TSource>(source);
		}

		internal static string ToString(this IPAddress address, bool bracketIPv6)
		{
			return (bracketIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6) ? $"[{address.ToString()}]" : address.ToString();
		}

		internal static ushort ToUInt16(this byte[] source, ByteOrder sourceOrder)
		{
			return BitConverter.ToUInt16(source.ToHostOrder(sourceOrder), 0);
		}

		internal static ulong ToUInt64(this byte[] source, ByteOrder sourceOrder)
		{
			return BitConverter.ToUInt64(source.ToHostOrder(sourceOrder), 0);
		}

		internal static IEnumerable<string> TrimEach(this IEnumerable<string> source)
		{
			foreach (string elm in source)
			{
				yield return elm.Trim();
			}
		}

		internal static string TrimSlashFromEnd(this string value)
		{
			string text = value.TrimEnd(new char[1] { '/' });
			return (text.Length > 0) ? text : "/";
		}

		internal static string TrimSlashOrBackslashFromEnd(this string value)
		{
			string text = value.TrimEnd('/', '\\');
			return (text.Length > 0) ? text : value[0].ToString();
		}

		internal static bool TryCreateVersion(this string versionString, out Version result)
		{
			result = null;
			try
			{
				result = new Version(versionString);
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool TryCreateWebSocketUri(this string uriString, out Uri result, out string message)
		{
			result = null;
			message = null;
			Uri uri = uriString.ToUri();
			if (uri == null)
			{
				message = "An invalid URI string.";
				return false;
			}
			if (!uri.IsAbsoluteUri)
			{
				message = "A relative URI.";
				return false;
			}
			string scheme = uri.Scheme;
			if (!(scheme == "ws") && !(scheme == "wss"))
			{
				message = "The scheme part is not 'ws' or 'wss'.";
				return false;
			}
			int port = uri.Port;
			if (port == 0)
			{
				message = "The port part is zero.";
				return false;
			}
			if (uri.Fragment.Length > 0)
			{
				message = "It includes the fragment component.";
				return false;
			}
			result = ((port != -1) ? uri : new Uri(string.Format("{0}://{1}:{2}{3}", scheme, uri.Host, (scheme == "ws") ? 80 : 443, uri.PathAndQuery)));
			return true;
		}

		internal static bool TryGetUTF8DecodedString(this byte[] bytes, out string s)
		{
			s = null;
			try
			{
				s = Encoding.UTF8.GetString(bytes);
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool TryGetUTF8EncodedBytes(this string s, out byte[] bytes)
		{
			bytes = null;
			try
			{
				bytes = Encoding.UTF8.GetBytes(s);
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool TryOpenRead(this FileInfo fileInfo, out FileStream fileStream)
		{
			fileStream = null;
			try
			{
				fileStream = fileInfo.OpenRead();
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static string Unquote(this string value)
		{
			int num = value.IndexOf('"');
			if (num == -1)
			{
				return value;
			}
			int num2 = value.LastIndexOf('"');
			if (num2 == num)
			{
				return value;
			}
			int num3 = num2 - num - 1;
			return (num3 > 0) ? value.Substring(num + 1, num3).Replace("\\\"", "\"") : string.Empty;
		}

		internal static bool Upgrades(this NameValueCollection headers, string protocol)
		{
			StringComparison comparisonTypeForValue = StringComparison.OrdinalIgnoreCase;
			return headers.Contains("Upgrade", protocol, comparisonTypeForValue) && headers.Contains("Connection", "Upgrade", comparisonTypeForValue);
		}

		internal static string UrlDecode(this string value, Encoding encoding)
		{
			return HttpUtility.UrlDecode(value, encoding);
		}

		internal static string UrlEncode(this string value, Encoding encoding)
		{
			return HttpUtility.UrlEncode(value, encoding);
		}

		internal static void WriteBytes(this Stream stream, byte[] bytes, int bufferLength)
		{
			using MemoryStream source = new MemoryStream(bytes);
			CopyTo(source, stream, bufferLength);
		}

		internal static void WriteBytesAsync(this Stream stream, byte[] bytes, int bufferLength, Action completed, Action<Exception> error)
		{
			MemoryStream src = new MemoryStream(bytes);
			src.CopyToAsync(stream, bufferLength, delegate
			{
				if (completed != null)
				{
					completed();
				}
				src.Dispose();
			}, delegate(Exception ex)
			{
				src.Dispose();
				if (error != null)
				{
					error(ex);
				}
			});
		}

		public static string GetDescription(this WebSocketSharp.Net.HttpStatusCode code)
		{
			return ((int)code).GetStatusDescription();
		}

		public static string GetStatusDescription(this int code)
		{
			return code switch
			{
				100 => "Continue", 
				101 => "Switching Protocols", 
				102 => "Processing", 
				200 => "OK", 
				201 => "Created", 
				202 => "Accepted", 
				203 => "Non-Authoritative Information", 
				204 => "No Content", 
				205 => "Reset Content", 
				206 => "Partial Content", 
				207 => "Multi-Status", 
				300 => "Multiple Choices", 
				301 => "Moved Permanently", 
				302 => "Found", 
				303 => "See Other", 
				304 => "Not Modified", 
				305 => "Use Proxy", 
				307 => "Temporary Redirect", 
				400 => "Bad Request", 
				401 => "Unauthorized", 
				402 => "Payment Required", 
				403 => "Forbidden", 
				404 => "Not Found", 
				405 => "Method Not Allowed", 
				406 => "Not Acceptable", 
				407 => "Proxy Authentication Required", 
				408 => "Request Timeout", 
				409 => "Conflict", 
				410 => "Gone", 
				411 => "Length Required", 
				412 => "Precondition Failed", 
				413 => "Request Entity Too Large", 
				414 => "Request-Uri Too Long", 
				415 => "Unsupported Media Type", 
				416 => "Requested Range Not Satisfiable", 
				417 => "Expectation Failed", 
				422 => "Unprocessable Entity", 
				423 => "Locked", 
				424 => "Failed Dependency", 
				500 => "Internal Server Error", 
				501 => "Not Implemented", 
				502 => "Bad Gateway", 
				503 => "Service Unavailable", 
				504 => "Gateway Timeout", 
				505 => "Http Version Not Supported", 
				507 => "Insufficient Storage", 
				_ => string.Empty, 
			};
		}

		public static bool IsCloseStatusCode(this ushort value)
		{
			return value > 999 && value < 5000;
		}

		public static bool IsEnclosedIn(this string value, char c)
		{
			if (value == null)
			{
				return false;
			}
			int length = value.Length;
			if (length < 2)
			{
				return false;
			}
			return value[0] == c && value[length - 1] == c;
		}

		public static bool IsHostOrder(this ByteOrder order)
		{
			return BitConverter.IsLittleEndian == (order == ByteOrder.Little);
		}

		public static bool IsLocal(this IPAddress address)
		{
			if (address == null)
			{
				throw new ArgumentNullException("address");
			}
			if (address.Equals(IPAddress.Any))
			{
				return true;
			}
			if (address.Equals(IPAddress.Loopback))
			{
				return true;
			}
			if (Socket.OSSupportsIPv6)
			{
				if (address.Equals(IPAddress.IPv6Any))
				{
					return true;
				}
				if (address.Equals(IPAddress.IPv6Loopback))
				{
					return true;
				}
			}
			string hostName = Dns.GetHostName();
			IPAddress[] hostAddresses = Dns.GetHostAddresses(hostName);
			IPAddress[] array = hostAddresses;
			foreach (IPAddress obj in array)
			{
				if (address.Equals(obj))
				{
					return true;
				}
			}
			return false;
		}

		public static bool IsNullOrEmpty(this string value)
		{
			return value == null || value.Length == 0;
		}

		public static bool IsPredefinedScheme(this string value)
		{
			if (value == null || value.Length < 2)
			{
				return false;
			}
			switch (value[0])
			{
			case 'h':
				return value == "http" || value == "https";
			case 'w':
				return value == "ws" || value == "wss";
			case 'f':
				return value == "file" || value == "ftp";
			case 'g':
				return value == "gopher";
			case 'm':
				return value == "mailto";
			case 'n':
			{
				char c = value[1];
				return (c != 'e') ? (value == "nntp") : (value == "news" || value == "net.pipe" || value == "net.tcp");
			}
			default:
				return false;
			}
		}

		public static bool MaybeUri(this string value)
		{
			if (value == null)
			{
				return false;
			}
			if (value.Length == 0)
			{
				return false;
			}
			int num = value.IndexOf(':');
			if (num == -1)
			{
				return false;
			}
			if (num >= 10)
			{
				return false;
			}
			string value2 = value.Substring(0, num);
			return value2.IsPredefinedScheme();
		}

		public static T[] SubArray<T>(this T[] array, int startIndex, int length)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			int num = array.Length;
			if (num == 0)
			{
				if (startIndex != 0)
				{
					throw new ArgumentOutOfRangeException("startIndex");
				}
				if (length != 0)
				{
					throw new ArgumentOutOfRangeException("length");
				}
				return array;
			}
			if (startIndex < 0 || startIndex >= num)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (length < 0 || length > num - startIndex)
			{
				throw new ArgumentOutOfRangeException("length");
			}
			if (length == 0)
			{
				return new T[0];
			}
			if (length == num)
			{
				return array;
			}
			T[] array2 = new T[length];
			Array.Copy(array, startIndex, array2, 0, length);
			return array2;
		}

		public static T[] SubArray<T>(this T[] array, long startIndex, long length)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			long num = array.LongLength;
			if (num == 0)
			{
				if (startIndex != 0)
				{
					throw new ArgumentOutOfRangeException("startIndex");
				}
				if (length != 0)
				{
					throw new ArgumentOutOfRangeException("length");
				}
				return array;
			}
			if (startIndex < 0 || startIndex >= num)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (length < 0 || length > num - startIndex)
			{
				throw new ArgumentOutOfRangeException("length");
			}
			if (length == 0)
			{
				return new T[0];
			}
			if (length == num)
			{
				return array;
			}
			T[] array2 = new T[length];
			Array.Copy(array, startIndex, array2, 0L, length);
			return array2;
		}

		public static void Times(this int n, Action action)
		{
			if (n > 0 && action != null)
			{
				for (int i = 0; i < n; i++)
				{
					action();
				}
			}
		}

		public static void Times(this long n, Action action)
		{
			if (n > 0 && action != null)
			{
				for (long num = 0L; num < n; num++)
				{
					action();
				}
			}
		}

		public static void Times(this uint n, Action action)
		{
			if (n != 0 && action != null)
			{
				for (uint num = 0u; num < n; num++)
				{
					action();
				}
			}
		}

		public static void Times(this ulong n, Action action)
		{
			if (n != 0 && action != null)
			{
				for (ulong num = 0uL; num < n; num++)
				{
					action();
				}
			}
		}

		public static void Times(this int n, Action<int> action)
		{
			if (n > 0 && action != null)
			{
				for (int i = 0; i < n; i++)
				{
					action(i);
				}
			}
		}

		public static void Times(this long n, Action<long> action)
		{
			if (n > 0 && action != null)
			{
				for (long num = 0L; num < n; num++)
				{
					action(num);
				}
			}
		}

		public static void Times(this uint n, Action<uint> action)
		{
			if (n != 0 && action != null)
			{
				for (uint num = 0u; num < n; num++)
				{
					action(num);
				}
			}
		}

		public static void Times(this ulong n, Action<ulong> action)
		{
			if (n != 0 && action != null)
			{
				for (ulong num = 0uL; num < n; num++)
				{
					action(num);
				}
			}
		}

		[Obsolete("This method will be removed.")]
		public static T To<T>(this byte[] source, ByteOrder sourceOrder) where T : struct
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (source.Length == 0)
			{
				return default(T);
			}
			Type typeFromHandle = typeof(T);
			byte[] value = source.ToHostOrder(sourceOrder);
			return ((object)typeFromHandle == typeof(bool)) ? ((T)(object)BitConverter.ToBoolean(value, 0)) : (((object)typeFromHandle == typeof(char)) ? ((T)(object)BitConverter.ToChar(value, 0)) : (((object)typeFromHandle == typeof(double)) ? ((T)(object)BitConverter.ToDouble(value, 0)) : (((object)typeFromHandle == typeof(short)) ? ((T)(object)BitConverter.ToInt16(value, 0)) : (((object)typeFromHandle == typeof(int)) ? ((T)(object)BitConverter.ToInt32(value, 0)) : (((object)typeFromHandle == typeof(long)) ? ((T)(object)BitConverter.ToInt64(value, 0)) : (((object)typeFromHandle == typeof(float)) ? ((T)(object)BitConverter.ToSingle(value, 0)) : (((object)typeFromHandle == typeof(ushort)) ? ((T)(object)BitConverter.ToUInt16(value, 0)) : (((object)typeFromHandle == typeof(uint)) ? ((T)(object)BitConverter.ToUInt32(value, 0)) : (((object)typeFromHandle == typeof(ulong)) ? ((T)(object)BitConverter.ToUInt64(value, 0)) : default(T))))))))));
		}

		[Obsolete("This method will be removed.")]
		public static byte[] ToByteArray<T>(this T value, ByteOrder order) where T : struct
		{
			Type typeFromHandle = typeof(T);
			byte[] array = (((object)typeFromHandle == typeof(bool)) ? BitConverter.GetBytes((bool)(object)value) : (((object)typeFromHandle != typeof(byte)) ? (((object)typeFromHandle == typeof(char)) ? BitConverter.GetBytes((char)(object)value) : (((object)typeFromHandle == typeof(double)) ? BitConverter.GetBytes((double)(object)value) : (((object)typeFromHandle == typeof(short)) ? BitConverter.GetBytes((short)(object)value) : (((object)typeFromHandle == typeof(int)) ? BitConverter.GetBytes((int)(object)value) : (((object)typeFromHandle == typeof(long)) ? BitConverter.GetBytes((long)(object)value) : (((object)typeFromHandle == typeof(float)) ? BitConverter.GetBytes((float)(object)value) : (((object)typeFromHandle == typeof(ushort)) ? BitConverter.GetBytes((ushort)(object)value) : (((object)typeFromHandle == typeof(uint)) ? BitConverter.GetBytes((uint)(object)value) : (((object)typeFromHandle == typeof(ulong)) ? BitConverter.GetBytes((ulong)(object)value) : WebSocket.EmptyBytes))))))))) : new byte[1] { (byte)(object)value }));
			if (array.Length > 1 && !order.IsHostOrder())
			{
				Array.Reverse((Array)array);
			}
			return array;
		}

		public static byte[] ToHostOrder(this byte[] source, ByteOrder sourceOrder)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (source.Length < 2)
			{
				return source;
			}
			if (sourceOrder.IsHostOrder())
			{
				return source;
			}
			return source.Reverse();
		}

		public static string ToString<T>(this T[] array, string separator)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			int num = array.Length;
			if (num == 0)
			{
				return string.Empty;
			}
			if (separator == null)
			{
				separator = string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(64);
			int num2 = num - 1;
			for (int i = 0; i < num2; i++)
			{
				stringBuilder.AppendFormat("{0}{1}", array[i], separator);
			}
			stringBuilder.Append(array[num2].ToString());
			return stringBuilder.ToString();
		}

		public static Uri ToUri(this string value)
		{
			Uri.TryCreate(value, value.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out Uri result);
			return result;
		}

		[Obsolete("This method will be removed.")]
		public static void WriteContent(this WebSocketSharp.Net.HttpListenerResponse response, byte[] content)
		{
			if (response == null)
			{
				throw new ArgumentNullException("response");
			}
			if (content == null)
			{
				throw new ArgumentNullException("content");
			}
			long num = content.LongLength;
			if (num == 0)
			{
				response.Close();
				return;
			}
			response.ContentLength64 = num;
			Stream outputStream = response.OutputStream;
			if (num <= int.MaxValue)
			{
				outputStream.Write(content, 0, (int)num);
			}
			else
			{
				outputStream.WriteBytes(content, 1024);
			}
			outputStream.Close();
		}
	}
	public class MessageEventArgs : EventArgs
	{
		private string _data;

		private bool _dataSet;

		private Opcode _opcode;

		private byte[] _rawData;

		internal Opcode Opcode => _opcode;

		public string Data
		{
			get
			{
				setData();
				return _data;
			}
		}

		public bool IsBinary => _opcode == Opcode.Binary;

		public bool IsPing => _opcode == Opcode.Ping;

		public bool IsText => _opcode == Opcode.Text;

		public byte[] RawData
		{
			get
			{
				setData();
				return _rawData;
			}
		}

		internal MessageEventArgs(WebSocketFrame frame)
		{
			_opcode = frame.Opcode;
			_rawData = frame.PayloadData.ApplicationData;
		}

		internal MessageEventArgs(Opcode opcode, byte[] rawData)
		{
			if ((ulong)rawData.LongLength > PayloadData.MaxLength)
			{
				throw new WebSocketException(CloseStatusCode.TooBig);
			}
			_opcode = opcode;
			_rawData = rawData;
		}

		private void setData()
		{
			if (_dataSet)
			{
				return;
			}
			if (_opcode == Opcode.Binary)
			{
				_dataSet = true;
				return;
			}
			if (_rawData.TryGetUTF8DecodedString(out var s))
			{
				_data = s;
			}
			_dataSet = true;
		}
	}
	public class CloseEventArgs : EventArgs
	{
		private bool _clean;

		private PayloadData _payloadData;

		public ushort Code => _payloadData.Code;

		public string Reason => _payloadData.Reason;

		public bool WasClean => _clean;

		internal CloseEventArgs(PayloadData payloadData, bool clean)
		{
			_payloadData = payloadData;
			_clean = clean;
		}

		internal CloseEventArgs(ushort code, string reason, bool clean)
		{
			_payloadData = new PayloadData(code, reason);
			_clean = clean;
		}
	}
	public enum ByteOrder
	{
		Little,
		Big
	}
	public class ErrorEventArgs : EventArgs
	{
		private Exception _exception;

		private string _message;

		public Exception Exception => _exception;

		public string Message => _message;

		internal ErrorEventArgs(string message)
			: this(message, null)
		{
		}

		internal ErrorEventArgs(string message, Exception exception)
		{
			_message = message;
			_exception = exception;
		}
	}
	public class WebSocket : IDisposable
	{
		private AuthenticationChallenge _authChallenge;

		private string _base64Key;

		private bool _client;

		private Action _closeContext;

		private CompressionMethod _compression;

		private WebSocketContext _context;

		private WebSocketSharp.Net.CookieCollection _cookies;

		private WebSocketSharp.Net.NetworkCredential _credentials;

		private bool _emitOnPing;

		private bool _enableRedirection;

		private string _extensions;

		private bool _extensionsRequested;

		private object _forMessageEventQueue;

		private object _forPing;

		private object _forSend;

		private object _forState;

		private MemoryStream _fragmentsBuffer;

		private bool _fragmentsCompressed;

		private Opcode _fragmentsOpcode;

		private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

		private Func<WebSocketContext, string> _handshakeRequestChecker;

		private bool _ignoreExtensions;

		private bool _inContinuation;

		private volatile bool _inMessage;

		private volatile Logger _logger;

		private static readonly int _maxRetryCountForConnect;

		private Action<MessageEventArgs> _message;

		private Queue<MessageEventArgs> _messageEventQueue;

		private uint _nonceCount;

		private string _origin;

		private ManualResetEvent _pongReceived;

		private bool _preAuth;

		private string _protocol;

		private string[] _protocols;

		private bool _protocolsRequested;

		private WebSocketSharp.Net.NetworkCredential _proxyCredentials;

		private Uri _proxyUri;

		private volatile WebSocketState _readyState;

		private ManualResetEvent _receivingExited;

		private int _retryCountForConnect;

		private bool _secure;

		private ClientSslConfiguration _sslConfig;

		private Stream _stream;

		private TcpClient _tcpClient;

		private Uri _uri;

		private const string _version = "13";

		private TimeSpan _waitTime;

		internal static readonly byte[] EmptyBytes;

		internal static readonly int FragmentLength;

		internal static readonly RandomNumberGenerator RandomNumber;

		internal WebSocketSharp.Net.CookieCollection CookieCollection => _cookies;

		internal Func<WebSocketContext, string> CustomHandshakeRequestChecker
		{
			get
			{
				return _handshakeRequestChecker;
			}
			set
			{
				_handshakeRequestChecker = value;
			}
		}

		internal bool HasMessage
		{
			get
			{
				lock (_forMessageEventQueue)
				{
					return _messageEventQueue.Count > 0;
				}
			}
		}

		internal bool IgnoreExtensions
		{
			get
			{
				return _ignoreExtensions;
			}
			set
			{
				_ignoreExtensions = value;
			}
		}

		internal bool IsConnected => _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing;

		public CompressionMethod Compression
		{
			get
			{
				return _compression;
			}
			set
			{
				string text = null;
				if (!_client)
				{
					text = "This instance is not a client.";
					throw new InvalidOperationException(text);
				}
				if (!canSet(out text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_forState)
				{
					if (!canSet(out text))
					{
						_logger.Warn(text);
					}
					else
					{
						_compression = value;
					}
				}
			}
		}

		public IEnumerable<WebSocketSharp.Net.Cookie> Cookies
		{
			get
			{
				lock (_cookies.SyncRoot)
				{
					foreach (WebSocketSharp.Net.Cookie cookie in _cookies)
					{
						yield return cookie;
					}
				}
			}
		}

		public WebSocketSharp.Net.NetworkCredential Credentials => _credentials;

		public bool EmitOnPing
		{
			get
			{
				return _emitOnPing;
			}
			set
			{
				_emitOnPing = value;
			}
		}

		public bool EnableRedirection
		{
			get
			{
				return _enableRedirection;
			}
			set
			{
				string text = null;
				if (!_client)
				{
					text = "This instance is not a client.";
					throw new InvalidOperationException(text);
				}
				if (!canSet(out text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_forState)
				{
					if (!canSet(out text))
					{
						_logger.Warn(text);
					}
					else
					{
						_enableRedirection = value;
					}
				}
			}
		}

		public string Extensions => _extensions ?? string.Empty;

		public bool IsAlive => ping(EmptyBytes);

		public bool IsSecure => _secure;

		public Logger Log
		{
			get
			{
				return _logger;
			}
			internal set
			{
				_logger = value;
			}
		}

		public string Origin
		{
			get
			{
				return _origin;
			}
			set
			{
				string text = null;
				if (!_client)
				{
					text = "This instance is not a client.";
					throw new InvalidOperationException(text);
				}
				if (!value.IsNullOrEmpty())
				{
					if (!Uri.TryCreate(value, UriKind.Absolute, out Uri result))
					{
						text = "Not an absolute URI string.";
						throw new ArgumentException(text, "value");
					}
					if (result.Segments.Length > 1)
					{
						text = "It includes the path segments.";
						throw new ArgumentException(text, "value");
					}
				}
				if (!canSet(out text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_forState)
				{
					if (!canSet(out text))
					{
						_logger.Warn(text);
						return;
					}
					_origin = ((!value.IsNullOrEmpty()) ? value.TrimEnd(new char[1] { '/' }) : value);
				}
			}
		}

		public string Protocol
		{
			get
			{
				return _protocol ?? string.Empty;
			}
			internal set
			{
				_protocol = value;
			}
		}

		public WebSocketState ReadyState => _readyState;

		public ClientSslConfiguration SslConfiguration
		{
			get
			{
				if (!_client)
				{
					string text = "This instance is not a client.";
					throw new InvalidOperationException(text);
				}
				if (!_secure)
				{
					string text2 = "This instance does not use a secure connection.";
					throw new InvalidOperationException(text2);
				}
				return getSslConfiguration();
			}
		}

		public Uri Url => _client ? _uri : _context.RequestUri;

		public TimeSpan WaitTime
		{
			get
			{
				return _waitTime;
			}
			set
			{
				if (value <= TimeSpan.Zero)
				{
					throw new ArgumentOutOfRangeException("value", "Zero or less.");
				}
				if (!canSet(out var text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_forState)
				{
					if (!canSet(out text))
					{
						_logger.Warn(text);
					}
					else
					{
						_waitTime = value;
					}
				}
			}
		}

		public event EventHandler<CloseEventArgs> OnClose;

		public event EventHandler<ErrorEventArgs> OnError;

		public event EventHandler<MessageEventArgs> OnMessage;

		public event EventHandler OnOpen;

		static WebSocket()
		{
			_maxRetryCountForConnect = 10;
			EmptyBytes = new byte[0];
			FragmentLength = 1016;
			RandomNumber = new RNGCryptoServiceProvider();
		}

		internal WebSocket(HttpListenerWebSocketContext context, string protocol)
		{
			_context = context;
			_protocol = protocol;
			_closeContext = context.Close;
			_logger = context.Log;
			_message = messages;
			_secure = context.IsSecureConnection;
			_stream = context.Stream;
			_waitTime = TimeSpan.FromSeconds(1.0);
			init();
		}

		internal WebSocket(TcpListenerWebSocketContext context, string protocol)
		{
			_context = context;
			_protocol = protocol;
			_closeContext = context.Close;
			_logger = context.Log;
			_message = messages;
			_secure = context.IsSecureConnection;
			_stream = context.Stream;
			_waitTime = TimeSpan.FromSeconds(1.0);
			init();
		}

		public WebSocket(string url, params string[] protocols)
		{
			if (url == null)
			{
				throw new ArgumentNullException("url");
			}
			if (url.Length == 0)
			{
				throw new ArgumentException("An empty string.", "url");
			}
			if (!url.TryCreateWebSocketUri(out _uri, out var text))
			{
				throw new ArgumentException(text, "url");
			}
			if (protocols != null && protocols.Length != 0)
			{
				if (!checkProtocols(protocols, out text))
				{
					throw new ArgumentException(text, "protocols");
				}
				_protocols = protocols;
			}
			_base64Key = CreateBase64Key();
			_client = true;
			_logger = new Logger();
			_message = messagec;
			_secure = _uri.Scheme == "wss";
			_waitTime = TimeSpan.FromSeconds(5.0);
			init();
		}

		private bool accept()
		{
			if (_readyState == WebSocketState.Open)
			{
				string text = "The handshake request has already been accepted.";
				_logger.Warn(text);
				return false;
			}
			lock (_forState)
			{
				if (_readyState == WebSocketState.Open)
				{
					string text2 = "The handshake request has already been accepted.";
					_logger.Warn(text2);
					return false;
				}
				if (_readyState == WebSocketState.Closing)
				{
					string text3 = "The close process has set in.";
					_logger.Error(text3);
					text3 = "An interruption has occurred while attempting to accept.";
					error(text3, null);
					return false;
				}
				if (_readyState == WebSocketState.Closed)
				{
					string text4 = "The connection has been closed.";
					_logger.Error(text4);
					text4 = "An interruption has occurred while attempting to accept.";
					error(text4, null);
					return false;
				}
				try
				{
					if (!acceptHandshake())
					{
						return false;
					}
				}
				catch (Exception ex)
				{
					_logger.Fatal(ex.Message);
					_logger.Debug(ex.ToString());
					string text5 = "An exception has occurred while attempting to accept.";
					fatal(text5, ex);
					return false;
				}
				_readyState = WebSocketState.Open;
				return true;
			}
		}

		private bool acceptHandshake()
		{
			_logger.Debug($"A handshake request from {_context.UserEndPoint}:\n{_context}");
			if (!checkHandshakeRequest(_context, out var text))
			{
				_logger.Error(text);
				refuseHandshake(CloseStatusCode.ProtocolError, "A handshake error has occurred while attempting to accept.");
				return false;
			}
			if (!customCheckHandshakeRequest(_context, out text))
			{
				_logger.Error(text);
				refuseHandshake(CloseStatusCode.PolicyViolation, "A handshake error has occurred while attempting to accept.");
				return false;
			}
			_base64Key = _context.Headers["Sec-WebSocket-Key"];
			if (_protocol != null)
			{
				IEnumerable<string> secWebSocketProtocols = _context.SecWebSocketProtocols;
				processSecWebSocketProtocolClientHeader(secWebSocketProtocols);
			}
			if (!_ignoreExtensions)
			{
				string value = _context.Headers["Sec-WebSocket-Extensions"];
				processSecWebSocketExtensionsClientHeader(value);
			}
			return sendHttpResponse(createHandshakeResponse());
		}

		private bool canSet(out string message)
		{
			message = null;
			if (_readyState == WebSocketState.Open)
			{
				message = "The connection has already been established.";
				return false;
			}
			if (_readyState == WebSocketState.Closing)
			{
				message = "The connection is closing.";
				return false;
			}
			return true;
		}

		private bool checkHandshakeRequest(WebSocketContext context, out string message)
		{
			message = null;
			if (!context.IsWebSocketRequest)
			{
				message = "Not a handshake request.";
				return false;
			}
			if (context.RequestUri == null)
			{
				message = "It specifies an invalid Request-URI.";
				return false;
			}
			NameValueCollection headers = context.Headers;
			string text = headers["Sec-WebSocket-Key"];
			if (text == null)
			{
				message = "It includes no Sec-WebSocket-Key header.";
				return false;
			}
			if (text.Length == 0)
			{
				message = "It includes an invalid Sec-WebSocket-Key header.";
				return false;
			}
			string text2 = headers["Sec-WebSocket-Version"];
			if (text2 == null)
			{
				message = "It includes no Sec-WebSocket-Version header.";
				return false;
			}
			if (text2 != "13")
			{
				message = "It includes an invalid Sec-WebSocket-Version header.";
				return false;
			}
			string text3 = headers["Sec-WebSocket-Protocol"];
			if (text3 != null && text3.Length == 0)
			{
				message = "It includes an invalid Sec-WebSocket-Protocol header.";
				return false;
			}
			if (!_ignoreExtensions)
			{
				string text4 = headers["Sec-WebSocket-Extensions"];
				if (text4 != null && text4.Length == 0)
				{
					message = "It includes an invalid Sec-WebSocket-Extensions header.";
					return false;
				}
			}
			return true;
		}

		private bool checkHandshakeResponse(HttpResponse response, out string message)
		{
			message = null;
			if (response.IsRedirect)
			{
				message = "Indicates the redirection.";
				return false;
			}
			if (response.IsUnauthorized)
			{
				message = "Requires the authentication.";
				return false;
			}
			if (!response.IsWebSocketResponse)
			{
				message = "Not a WebSocket handshake response.";
				return false;
			}
			NameValueCollection headers = response.Headers;
			if (!validateSecWebSocketAcceptHeader(headers["Sec-WebSocket-Accept"]))
			{
				message = "Includes no Sec-WebSocket-Accept header, or it has an invalid value.";
				return false;
			}
			if (!validateSecWebSocketProtocolServerHeader(headers["Sec-WebSocket-Protocol"]))
			{
				message = "Includes no Sec-WebSocket-Protocol header, or it has an invalid value.";
				return false;
			}
			if (!validateSecWebSocketExtensionsServerHeader(headers["Sec-WebSocket-Extensions"]))
			{
				message = "Includes an invalid Sec-WebSocket-Extensions header.";
				return false;
			}
			if (!validateSecWebSocketVersionServerHeader(headers["Sec-WebSocket-Version"]))
			{
				message = "Includes an invalid Sec-WebSocket-Version header.";
				return false;
			}
			return true;
		}

		private static bool checkProtocols(string[] protocols, out string message)
		{
			message = null;
			Func<string, bool> condition = (string protocol) => protocol.IsNullOrEmpty() || !protocol.IsToken();
			if (protocols.Contains(condition))
			{
				message = "It contains a value that is not a token.";
				return false;
			}
			if (protocols.ContainsTwice())
			{
				message = "It contains a value twice.";
				return false;
			}
			return true;
		}

		private bool checkReceivedFrame(WebSocketFrame frame, out string message)
		{
			message = null;
			bool isMasked = frame.IsMasked;
			if (_client && isMasked)
			{
				message = "A frame from the server is masked.";
				return false;
			}
			if (!_client && !isMasked)
			{
				message = "A frame from a client is not masked.";
				return false;
			}
			if (_inContinuation && frame.IsData)
			{
				message = "A data frame has been received while receiving continuation frames.";
				return false;
			}
			if (frame.IsCompressed && _compression == CompressionMethod.None)
			{
				message = "A compressed frame has been received without any agreement for it.";
				return false;
			}
			if (frame.Rsv2 == Rsv.On)
			{
				message = "The RSV2 of a frame is non-zero without any negotiation for it.";
				return false;
			}
			if (frame.Rsv3 == Rsv.On)
			{
				message = "The RSV3 of a frame is non-zero without any negotiation for it.";
				return false;
			}
			return true;
		}

		private void close(ushort code, string reason)
		{
			if (_readyState == WebSocketState.Closing)
			{
				_logger.Info("The closing is already in progress.");
				return;
			}
			if (_readyState == WebSocketState.Closed)
			{
				_logger.Info("The connection has already been closed.");
				return;
			}
			if (code == 1005)
			{
				close(PayloadData.Empty, send: true, receive: true, received: false);
				return;
			}
			bool receive = !code.IsReserved();
			close(new PayloadData(code, reason), receive, receive, received: false);
		}

		private void close(PayloadData payloadData, bool send, bool receive, bool received)
		{
			lock (_forState)
			{
				if (_readyState == WebSocketState.Closing)
				{
					_logger.Info("The closing is already in progress.");
					return;
				}
				if (_readyState == WebSocketState.Closed)
				{
					_logger.Info("The connection has already been closed.");
					return;
				}
				send = send && _readyState == WebSocketState.Open;
				receive = send && receive;
				_readyState = WebSocketState.Closing;
			}
			_logger.Trace("Begin closing the connection.");
			bool clean = closeHandshake(payloadData, send, receive, received);
			releaseResources();
			_logger.Trace("End closing the connection.");
			_readyState = WebSocketState.Closed;
			CloseEventArgs e = new CloseEventArgs(payloadData, clean);
			try
			{
				this.OnClose.Emit(this, e);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
			}
		}

		private void closeAsync(ushort code, string reason)
		{
			if (_readyState == WebSocketState.Closing)
			{
				_logger.Info("The closing is already in progress.");
				return;
			}
			if (_readyState == WebSocketState.Closed)
			{
				_logger.Info("The connection has already been closed.");
				return;
			}
			if (code == 1005)
			{
				closeAsync(PayloadData.Empty, send: true, receive: true, received: false);
				return;
			}
			bool receive = !code.IsReserved();
			closeAsync(new PayloadData(code, reason), receive, receive, received: false);
		}

		private void closeAsync(PayloadData payloadData, bool send, bool receive, bool received)
		{
			Action<PayloadData, bool, bool, bool> closer = close;
			closer.BeginInvoke(payloadData, send, receive, received, delegate(IAsyncResult ar)
			{
				closer.EndInvoke(ar);
			}, null);
		}

		private bool closeHandshake(byte[] frameAsBytes, bool receive, bool received)
		{
			bool flag = frameAsBytes != null && sendBytes(frameAsBytes);
			if (!received && flag && receive && _receivingExited != null)
			{
				received = _receivingExited.WaitOne(_waitTime);
			}
			bool flag2 = flag && received;
			_logger.Debug($"Was clean?: {flag2}\n  sent: {flag}\n  received: {received}");
			return flag2;
		}

		private bool closeHandshake(PayloadData payloadData, bool send, bool receive, bool received)
		{
			bool flag = false;
			if (send)
			{
				WebSocketFrame webSocketFrame = WebSocketFrame.CreateCloseFrame(payloadData, _client);
				flag = sendBytes(webSocketFrame.ToArray());
				if (_client)
				{
					webSocketFrame.Unmask();
				}
			}
			if (!received && flag && receive && _receivingExited != null)
			{
				received = _receivingExited.WaitOne(_waitTime);
			}
			bool flag2 = flag && received;
			_logger.Debug($"Was clean?: {flag2}\n  sent: {flag}\n  received: {received}");
			return flag2;
		}

		private bool connect()
		{
			if (_readyState == WebSocketState.Open)
			{
				string text = "The connection has already been established.";
				_logger.Warn(text);
				return false;
			}
			lock (_forState)
			{
				if (_readyState == WebSocketState.Open)
				{
					string text2 = "The connection has already been established.";
					_logger.Warn(text2);
					return false;
				}
				if (_readyState == WebSocketState.Closing)
				{
					string text3 = "The close process has set in.";
					_logger.Error(text3);
					text3 = "An interruption has occurred while attempting to connect.";
					error(text3, null);
					return false;
				}
				if (_retryCountForConnect > _maxRetryCountForConnect)
				{
					string text4 = "An opportunity for reconnecting has been lost.";
					_logger.Error(text4);
					text4 = "An interruption has occurred while attempting to connect.";
					error(text4, null);
					return false;
				}
				_readyState = WebSocketState.Connecting;
				try
				{
					doHandshake();
				}
				catch (Exception ex)
				{
					_retryCountForConnect++;
					_logger.Fatal(ex.Message);
					_logger.Debug(ex.ToString());
					string text5 = "An exception has occurred while attempting to connect.";
					fatal(text5, ex);
					return false;
				}
				_retryCountForConnect = 1;
				_readyState = WebSocketState.Open;
				return true;
			}
		}

		private string createExtensions()
		{
			StringBuilder stringBuilder = new StringBuilder(80);
			if (_compression != 0)
			{
				string arg = _compression.ToExtensionString("server_no_context_takeover", "client_no_context_takeover");
				stringBuilder.AppendFormat("{0}, ", arg);
			}
			int length = stringBuilder.Length;
			if (length > 2)
			{
				stringBuilder.Length = length - 2;
				return stringBuilder.ToString();
			}
			return null;
		}

		private HttpResponse createHandshakeFailureResponse(WebSocketSharp.Net.HttpStatusCode code)
		{
			HttpResponse httpResponse = HttpResponse.CreateCloseResponse(code);
			httpResponse.Headers["Sec-WebSocket-Version"] = "13";
			return httpResponse;
		}

		private HttpRequest createHandshakeRequest()
		{
			HttpRequest httpRequest = HttpRequest.CreateWebSocketRequest(_uri);
			NameValueCollection headers = httpRequest.Headers;
			if (!_origin.IsNullOrEmpty())
			{
				headers["Origin"] = _origin;
			}
			headers["Sec-WebSocket-Key"] = _base64Key;
			_protocolsRequested = _protocols != null;
			if (_protocolsRequested)
			{
				headers["Sec-WebSocket-Protocol"] = _protocols.ToString(", ");
			}
			_extensionsRequested = _compression != CompressionMethod.None;
			if (_extensionsRequested)
			{
				headers["Sec-WebSocket-Extensions"] = createExtensions();
			}
			headers["Sec-WebSocket-Version"] = "13";
			AuthenticationResponse authenticationResponse = null;
			if (_authChallenge != null && _credentials != null)
			{
				authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount);
				_nonceCount = authenticationResponse.NonceCount;
			}
			else if (_preAuth)
			{
				authenticationResponse = new AuthenticationResponse(_credentials);
			}
			if (authenticationResponse != null)
			{
				headers["Authorization"] = authenticationResponse.ToString();
			}
			if (_cookies.Count > 0)
			{
				httpRequest.SetCookies(_cookies);
			}
			return httpRequest;
		}

		private HttpResponse createHandshakeResponse()
		{
			HttpResponse httpResponse = HttpResponse.CreateWebSocketResponse();
			NameValueCollection headers = httpResponse.Headers;
			headers["Sec-WebSocket-Accept"] = CreateResponseKey(_base64Key);
			if (_protocol != null)
			{
				headers["Sec-WebSocket-Protocol"] = _protocol;
			}
			if (_extensions != null)
			{
				headers["Sec-WebSocket-Extensions"] = _extensions;
			}
			if (_cookies.Count > 0)
			{
				httpResponse.SetCookies(_cookies);
			}
			return httpResponse;
		}

		private bool customCheckHandshakeRequest(WebSocketContext context, out string message)
		{
			message = null;
			if (_handshakeRequestChecker == null)
			{
				return true;
			}
			message = _handshakeRequestChecker(context);
			return message == null;
		}

		private MessageEventArgs dequeueFromMessageEventQueue()
		{
			lock (_forMessageEventQueue)
			{
				return (_messageEventQueue.Count > 0) ? _messageEventQueue.Dequeue() : null;
			}
		}

		private void doHandshake()
		{
			setClientStream();
			HttpResponse httpResponse = sendHandshakeRequest();
			if (!checkHandshakeResponse(httpResponse, out var text))
			{
				throw new WebSocketException(CloseStatusCode.ProtocolError, text);
			}
			if (_protocolsRequested)
			{
				_protocol = httpResponse.Headers["Sec-WebSocket-Protocol"];
			}
			if (_extensionsRequested)
			{
				processSecWebSocketExtensionsServerHeader(httpResponse.Headers["Sec-WebSocket-Extensions"]);
			}
			processCookies(httpResponse.Cookies);
		}

		private void enqueueToMessageEventQueue(MessageEventArgs e)
		{
			lock (_forMessageEventQueue)
			{
				_messageEventQueue.Enqueue(e);
			}
		}

		private void error(string message, Exception exception)
		{
			try
			{
				this.OnError.Emit(this, new ErrorEventArgs(message, exception));
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
			}
		}

		private void fatal(string message, Exception exception)
		{
			CloseStatusCode code = ((exception is WebSocketException) ? ((WebSocketException)exception).Code : CloseStatusCode.Abnormal);
			fatal(message, (ushort)code);
		}

		private void fatal(string message, ushort code)
		{
			PayloadData payloadData = new PayloadData(code, message);
			close(payloadData, !code.IsReserved(), receive: false, received: false);
		}

		private void fatal(string message, CloseStatusCode code)
		{
			fatal(message, (ushort)code);
		}

		private ClientSslConfiguration getSslConfiguration()
		{
			if (_sslConfig == null)
			{
				_sslConfig = new ClientSslConfiguration(_uri.DnsSafeHost);
			}
			return _sslConfig;
		}

		private void init()
		{
			_compression = CompressionMethod.None;
			_cookies = new WebSocketSharp.Net.CookieCollection();
			_forPing = new object();
			_forSend = new object();
			_forState = new object();
			_messageEventQueue = new Queue<MessageEventArgs>();
			_forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot;
			_readyState = WebSocketState.Connecting;
		}

		private void message()
		{
			MessageEventArgs obj = null;
			lock (_forMessageEventQueue)
			{
				if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
				{
					return;
				}
				_inMessage = true;
				obj = _messageEventQueue.Dequeue();
			}
			_message(obj);
		}

		private void messagec(MessageEventArgs e)
		{
			while (true)
			{
				try
				{
					this.OnMessage.Emit(this, e);
				}
				catch (Exception ex)
				{
					_logger.Error(ex.ToString());
					error("An error has occurred during an OnMessage event.", ex);
				}
				lock (_forMessageEventQueue)
				{
					if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
					{
						_inMessage = false;
						break;
					}
					e = _messageEventQueue.Dequeue();
				}
				bool flag = true;
			}
		}

		private void messages(MessageEventArgs e)
		{
			try
			{
				this.OnMessage.Emit(this, e);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.ToString());
				error("An error has occurred during an OnMessage event.", ex);
			}
			lock (_forMessageEventQueue)
			{
				if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
				{
					_inMessage = false;
					return;
				}
				e = _messageEventQueue.Dequeue();
			}
			ThreadPool.QueueUserWorkItem(delegate
			{
				messages(e);
			});
		}

		private void open()
		{
			_inMessage = true;
			startReceiving();
			try
			{
				this.OnOpen.Emit(this, EventArgs.Empty);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.ToString());
				error("An error has occurred during the OnOpen event.", ex);
			}
			MessageEventArgs obj = null;
			lock (_forMessageEventQueue)
			{
				if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
				{
					_inMessage = false;
					return;
				}
				obj = _messageEventQueue.Dequeue();
			}
			_message.BeginInvoke(obj, delegate(IAsyncResult ar)
			{
				_message.EndInvoke(ar);
			}, null);
		}

		private bool ping(byte[] data)
		{
			if (_readyState != WebSocketState.Open)
			{
				return false;
			}
			ManualResetEvent pongReceived = _pongReceived;
			if (pongReceived == null)
			{
				return false;
			}
			lock (_forPing)
			{
				try
				{
					pongReceived.Reset();
					if (!send(Fin.Final, Opcode.Ping, data, compressed: false))
					{
						return false;
					}
					return pongReceived.WaitOne(_waitTime);
				}
				catch (ObjectDisposedException)
				{
					return false;
				}
			}
		}

		private bool processCloseFrame(WebSocketFrame frame)
		{
			PayloadData payloadData = frame.PayloadData;
			close(payloadData, !payloadData.HasReservedCode, receive: false, received: true);
			return false;
		}

		private void processCookies(WebSocketSharp.Net.CookieCollection cookies)
		{
			if (cookies.Count != 0)
			{
				_cookies.SetOrRemove(cookies);
			}
		}

		private bool processDataFrame(WebSocketFrame frame)
		{
			enqueueToMessageEventQueue(frame.IsCompressed ? new MessageEventArgs(frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression)) : new MessageEventArgs(frame));
			return true;
		}

		private bool processFragmentFrame(WebSocketFrame frame)
		{
			if (!_inContinuation)
			{
				if (frame.IsContinuation)
				{
					return true;
				}
				_fragmentsOpcode = frame.Opcode;
				_fragmentsCompressed = frame.IsCompressed;
				_fragmentsBuffer = new MemoryStream();
				_inContinuation = true;
			}
			_fragmentsBuffer.WriteBytes(frame.PayloadData.ApplicationData, 1024);
			if (frame.IsFinal)
			{
				using (_fragmentsBuffer)
				{
					byte[] rawData = (_fragmentsCompressed ? _fragmentsBuffer.DecompressToArray(_compression) : _fragmentsBuffer.ToArray());
					enqueueToMessageEventQueue(new MessageEventArgs(_fragmentsOpcode, rawData));
				}
				_fragmentsBuffer = null;
				_inContinuation = false;
			}
			return true;
		}

		private bool processPingFrame(WebSocketFrame frame)
		{
			_logger.Trace("A ping was received.");
			WebSocketFrame webSocketFrame = WebSocketFrame.CreatePongFrame(frame.PayloadData, _client);
			lock (_forState)
			{
				if (_readyState != WebSocketState.Open)
				{
					_logger.Error("The connection is closing.");
					return true;
				}
				if (!sendBytes(webSocketFrame.ToArray()))
				{
					return false;
				}
			}
			_logger.Trace("A pong to this ping has been sent.");
			if (_emitOnPing)
			{
				if (_client)
				{
					webSocketFrame.Unmask();
				}
				enqueueToMessageEventQueue(new MessageEventArgs(frame));
			}
			return true;
		}

		private bool processPongFrame(WebSocketFrame frame)
		{
			_logger.Trace("A pong was received.");
			try
			{
				_pongReceived.Set();
			}
			catch (NullReferenceException ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
				return false;
			}
			catch (ObjectDisposedException ex2)
			{
				_logger.Error(ex2.Message);
				_logger.Debug(ex2.ToString());
				return false;
			}
			_logger.Trace("It has been signaled.");
			return true;
		}

		private bool processReceivedFrame(WebSocketFrame frame)
		{
			if (!checkReceivedFrame(frame, out var text))
			{
				throw new WebSocketException(CloseStatusCode.ProtocolError, text);
			}
			frame.Unmask();
			return frame.IsFragment ? processFragmentFrame(frame) : (frame.IsData ? processDataFrame(frame) : (frame.IsPing ? processPingFrame(frame) : (frame.IsPong ? processPongFrame(frame) : (frame.IsClose ? processCloseFrame(frame) : processUnsupportedFrame(frame)))));
		}

		private void processSecWebSocketExtensionsClientHeader(string value)
		{
			if (value == null)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder(80);
			bool flag = false;
			foreach (string item in value.SplitHeaderValue(','))
			{
				string text = item.Trim();
				if (text.Length != 0 && !flag && text.IsCompressionExtension(CompressionMethod.Deflate))
				{
					_compression = CompressionMethod.Deflate;
					stringBuilder.AppendFormat("{0}, ", _compression.ToExtensionString("client_no_context_takeover", "server_no_context_takeover"));
					flag = true;
				}
			}
			int length = stringBuilder.Length;
			if (length > 2)
			{
				stringBuilder.Length = length - 2;
				_extensions = stringBuilder.ToString();
			}
		}

		private void processSecWebSocketExtensionsServerHeader(string value)
		{
			if (value == null)
			{
				_compression = CompressionMethod.None;
			}
			else
			{
				_extensions = value;
			}
		}

		private void processSecWebSocketProtocolClientHeader(IEnumerable<string> values)
		{
			if (!values.Contains((string val) => val == _protocol))
			{
				_protocol = null;
			}
		}

		private bool processUnsupportedFrame(WebSocketFrame frame)
		{
			_logger.Fatal("An unsupported frame:" + frame.PrintToString(dumped: false));
			fatal("There is no way to handle it.", CloseStatusCode.PolicyViolation);
			return false;
		}

		private void refuseHandshake(CloseStatusCode code, string reason)
		{
			_readyState = WebSocketState.Closing;
			HttpResponse response = createHandshakeFailureResponse(WebSocketSharp.Net.HttpStatusCode.BadRequest);
			sendHttpResponse(response);
			releaseServerResources();
			_readyState = WebSocketState.Closed;
			CloseEventArgs e = new CloseEventArgs((ushort)code, reason, clean: false);
			try
			{
				this.OnClose.Emit(this, e);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
			}
		}

		private void releaseClientResources()
		{
			if (_stream != null)
			{
				_stream.Dispose();
				_stream = null;
			}
			if (_tcpClient != null)
			{
				_tcpClient.Close();
				_tcpClient = null;
			}
		}

		private void releaseCommonResources()
		{
			if (_fragmentsBuffer != null)
			{
				_fragmentsBuffer.Dispose();
				_fragmentsBuffer = null;
				_inContinuation = false;
			}
			if (_pongReceived != null)
			{
				_pongReceived.Close();
				_pongReceived = null;
			}
			if (_receivingExited != null)
			{
				_receivingExited.Close();
				_receivingExited = null;
			}
		}

		private void releaseResources()
		{
			if (_client)
			{
				releaseClientResources();
			}
			else
			{
				releaseServerResources();
			}
			releaseCommonResources();
		}

		private void releaseServerResources()
		{
			if (_closeContext != null)
			{
				_closeContext();
				_closeContext = null;
				_stream = null;
				_context = null;
			}
		}

		private bool send(Opcode opcode, Stream stream)
		{
			lock (_forSend)
			{
				Stream stream2 = stream;
				bool flag = false;
				bool flag2 = false;
				try
				{
					if (_compression != 0)
					{
						stream = stream.Compress(_compression);
						flag = true;
					}
					flag2 = send(opcode, stream, flag);
					if (!flag2)
					{
						error("A send has been interrupted.", null);
					}
				}
				catch (Exception ex)
				{
					_logger.Error(ex.ToString());
					error("An error has occurred during a send.", ex);
				}
				finally
				{
					if (flag)
					{
						stream.Dispose();
					}
					stream2.Dispose();
				}
				return flag2;
			}
		}

		private bool send(Opcode opcode, Stream stream, bool compressed)
		{
			long length = stream.Length;
			if (length == 0)
			{
				return send(Fin.Final, opcode, EmptyBytes, compressed: false);
			}
			long num = length / FragmentLength;
			int num2 = (int)(length % FragmentLength);
			byte[] array = null;
			switch (num)
			{
			case 0L:
				array = new byte[num2];
				return stream.Read(array, 0, num2) == num2 && send(Fin.Final, opcode, array, compressed);
			case 1L:
				if (num2 == 0)
				{
					array = new byte[FragmentLength];
					return stream.Read(array, 0, FragmentLength) == FragmentLength && send(Fin.Final, opcode, array, compressed);
				}
				break;
			}
			array = new byte[FragmentLength];
			if (stream.Read(array, 0, FragmentLength) != FragmentLength || !send(Fin.More, opcode, array, compressed))
			{
				return false;
			}
			long num3 = ((num2 == 0) ? (num - 2) : (num - 1));
			for (long num4 = 0L; num4 < num3; num4++)
			{
				if (stream.Read(array, 0, FragmentLength) != FragmentLength || !send(Fin.More, Opcode.Cont, array, compressed: false))
				{
					return false;
				}
			}
			if (num2 == 0)
			{
				num2 = FragmentLength;
			}
			else
			{
				array = new byte[num2];
			}
			return stream.Read(array, 0, num2) == num2 && send(Fin.Final, Opcode.Cont, array, compressed: false);
		}

		private bool send(Fin fin, Opcode opcode, byte[] data, bool compressed)
		{
			lock (_forState)
			{
				if (_readyState != WebSocketState.Open)
				{
					_logger.Error("The connection is closing.");
					return false;
				}
				WebSocketFrame webSocketFrame = new WebSocketFrame(fin, opcode, data, compressed, _client);
				return sendBytes(webSocketFrame.ToArray());
			}
		}

		private void sendAsync(Opcode opcode, Stream stream, Action<bool> completed)
		{
			Func<Opcode, Stream, bool> sender = send;
			sender.BeginInvoke(opcode, stream, delegate(IAsyncResult ar)
			{
				try
				{
					bool obj = sender.EndInvoke(ar);
					if (completed != null)
					{
						completed(obj);
					}
				}
				catch (Exception ex)
				{
					_logger.Error(ex.ToString());
					error("An error has occurred during the callback for an async send.", ex);
				}
			}, null);
		}

		private bool sendBytes(byte[] bytes)
		{
			try
			{
				_stream.Write(bytes, 0, bytes.Length);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
				return false;
			}
			return true;
		}

		private HttpResponse sendHandshakeRequest()
		{
			HttpRequest httpRequest = createHandshakeRequest();
			HttpResponse httpResponse = sendHttpRequest(httpRequest, 90000);
			if (httpResponse.IsUnauthorized)
			{
				string text = httpResponse.Headers["WWW-Authenticate"];
				_logger.Warn($"Received an authentication requirement for '{text}'.");
				if (text.IsNullOrEmpty())
				{
					_logger.Error("No authentication challenge is specified.");
					return httpResponse;
				}
				_authChallenge = AuthenticationChallenge.Parse(text);
				if (_authChallenge == null)
				{
					_logger.Error("An invalid authentication challenge is specified.");
					return httpResponse;
				}
				if (_credentials != null && (!_preAuth || _authChallenge.Scheme == WebSocketSharp.Net.AuthenticationSchemes.Digest))
				{
					if (httpResponse.HasConnectionClose)
					{
						releaseClientResources();
						setClientStream();
					}
					AuthenticationResponse authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount);
					_nonceCount = authenticationResponse.NonceCount;
					httpRequest.Headers["Authorization"] = authenticationResponse.ToString();
					httpResponse = sendHttpRequest(httpRequest, 15000);
				}
			}
			if (httpResponse.IsRedirect)
			{
				string text2 = httpResponse.Headers["Location"];
				_logger.Warn($"Received a redirection to '{text2}'.");
				if (_enableRedirection)
				{
					if (text2.IsNullOrEmpty())
					{
						_logger.Error("No url to redirect is located.");
						return httpResponse;
					}
					if (!text2.TryCreateWebSocketUri(out var result, out var text3))
					{
						_logger.Error("An invalid url to redirect is located: " + text3);
						return httpResponse;
					}
					releaseClientResources();
					_uri = result;
					_secure = result.Scheme == "wss";
					setClientStream();
					return sendHandshakeRequest();
				}
			}
			return httpResponse;
		}

		private HttpResponse sendHttpRequest(HttpRequest request, int millisecondsTimeout)
		{
			_logger.Debug("A request to the server:\n" + request.ToString());
			HttpResponse response = request.GetResponse(_stream, millisecondsTimeout);
			_logger.Debug("A response to this request:\n" + response.ToString());
			return response;
		}

		private bool sendHttpResponse(HttpResponse response)
		{
			_logger.Debug($"A response to {_context.UserEndPoint}:\n{response}");
			return sendBytes(response.ToByteArray());
		}

		private void sendProxyConnectRequest()
		{
			HttpRequest httpRequest = HttpRequest.CreateConnectRequest(_uri);
			HttpResponse httpResponse = sendHttpRequest(httpRequest, 90000);
			if (httpResponse.IsProxyAuthenticationRequired)
			{
				string text = httpResponse.Headers["Proxy-Authenticate"];
				_logger.Warn($"Received a proxy authentication requirement for '{text}'.");
				if (text.IsNullOrEmpty())
				{
					throw new WebSocketException("No proxy authentication challenge is specified.");
				}
				AuthenticationChallenge authenticationChallenge = AuthenticationChallenge.Parse(text);
				if (authenticationChallenge == null)
				{
					throw new WebSocketException("An invalid proxy authentication challenge is specified.");
				}
				if (_proxyCredentials != null)
				{
					if (httpResponse.HasConnectionClose)
					{
						releaseClientResources();
						_tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port);
						_stream = _tcpClient.GetStream();
					}
					AuthenticationResponse authenticationResponse = new AuthenticationResponse(authenticationChallenge, _proxyCredentials, 0u);
					httpRequest.Headers["Proxy-Authorization"] = authenticationResponse.ToString();
					httpResponse = sendHttpRequest(httpRequest, 15000);
				}
				if (httpResponse.IsProxyAuthenticationRequired)
				{
					throw new WebSocketException("A proxy authentication is required.");
				}
			}
			if (httpResponse.StatusCode[0] != '2')
			{
				throw new WebSocketException("The proxy has failed a connection to the requested host and port.");
			}
		}

		private void setClientStream()
		{
			if (_proxyUri != null)
			{
				_tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port);
				_stream = _tcpClient.GetStream();
				sendProxyConnectRequest();
			}
			else
			{
				_tcpClient = new TcpClient(_uri.DnsSafeHost, _uri.Port);
				_stream = _tcpClient.GetStream();
			}
			if (_secure)
			{
				ClientSslConfiguration sslConfiguration = getSslConfiguration();
				string targetHost = sslConfiguration.TargetHost;
				if (targetHost != _uri.DnsSafeHost)
				{
					throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified.");
				}
				try
				{
					SslStream sslStream = new SslStream(_stream, leaveInnerStreamOpen: false, sslConfiguration.ServerCertificateValidationCallback, sslConfiguration.ClientCertificateSelectionCallback);
					sslStream.AuthenticateAsClient(targetHost, sslConfiguration.ClientCertificates, sslConfiguration.EnabledSslProtocols, sslConfiguration.CheckCertificateRevocation);
					_stream = sslStream;
				}
				catch (Exception innerException)
				{
					throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, innerException);
				}
			}
		}

		private void startReceiving()
		{
			if (_messageEventQueue.Count > 0)
			{
				_messageEventQueue.Clear();
			}
			_pongReceived = new ManualResetEvent(initialState: false);
			_receivingExited = new ManualResetEvent(initialState: false);
			Action receive = null;
			receive = delegate
			{
				WebSocketFrame.ReadFrameAsync(_stream, unmask: false, delegate(WebSocketFrame frame)
				{
					if (!processReceivedFrame(frame) || _readyState == WebSocketState.Closed)
					{
						_receivingExited?.Set();
					}
					else
					{
						receive();
						if (!_inMessage && HasMessage && _readyState == WebSocketState.Open)
						{
							message();
						}
					}
				}, delegate(Exception ex)
				{
					_logger.Fatal(ex.ToString());
					fatal("An exception has occurred while receiving.", ex);
				});
			};
			receive();
		}

		private bool validateSecWebSocketAcceptHeader(string value)
		{
			return value != null && value == CreateResponseKey(_base64Key);
		}

		private bool validateSecWebSocketExtensionsServerHeader(string value)
		{
			if (value == null)
			{
				return true;
			}
			if (value.Length == 0)
			{
				return false;
			}
			if (!_extensionsRequested)
			{
				return false;
			}
			bool flag = _compression != CompressionMethod.None;
			foreach (string item in value.SplitHeaderValue(','))
			{
				string text = item.Trim();
				if (flag && text.IsCompressionExtension(_compression))
				{
					if (!text.Contains("server_no_context_takeover"))
					{
						_logger.Error("The server hasn't sent back 'server_no_context_takeover'.");
						return false;
					}
					if (!text.Contains("client_no_context_takeover"))
					{
						_logger.Warn("The server hasn't sent back 'client_no_context_takeover'.");
					}
					string method = _compression.ToExtensionString();
					if (text.SplitHeaderValue(';').Contains(delegate(string t)
					{
						t = t.Trim();
						return t != method && t != "server_no_context_takeover" && t != "client_no_context_takeover";
					}))
					{
						return false;
					}
					continue;
				}
				return false;
			}
			return true;
		}

		private bool validateSecWebSocketProtocolServerHeader(string value)
		{
			if (value == null)
			{
				return !_protocolsRequested;
			}
			if (value.Length == 0)
			{
				return false;
			}
			return _protocolsRequested && _protocols.Contains((string p) => p == value);
		}

		private bool validateSecWebSocketVersionServerHeader(string value)
		{
			return value == null || value == "13";
		}

		internal void Close(HttpResponse response)
		{
			_readyState = WebSocketState.Closing;
			sendHttpResponse(response);
			releaseServerResources();
			_readyState = WebSocketState.Closed;
		}

		internal void Close(WebSocketSharp.Net.HttpStatusCode code)
		{
			Close(createHandshakeFailureResponse(code));
		}

		internal void Close(PayloadData payloadData, byte[] frameAsBytes)
		{
			lock (_forState)
			{
				if (_readyState == WebSocketState.Closing)
				{
					_logger.Info("The closing is already in progress.");
					return;
				}
				if (_readyState == WebSocketState.Closed)
				{
					_logger.Info("The connection has already been closed.");
					return;
				}
				_readyState = WebSocketState.Closing;
			}
			_logger.Trace("Begin closing the connection.");
			bool flag = frameAsBytes != null && sendBytes(frameAsBytes);
			bool flag2 = flag && _receivingExited != null && _receivingExited.WaitOne(_waitTime);
			bool flag3 = flag && flag2;
			_logger.Debug($"Was clean?: {flag3}\n  sent: {flag}\n  received: {flag2}");
			releaseServerResources();
			releaseCommonResources();
			_logger.Trace("End closing the connection.");
			_readyState = WebSocketState.Closed;
			CloseEventArgs e = new CloseEventArgs(payloadData, flag3);
			try
			{
				this.OnClose.Emit(this, e);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
			}
		}

		internal static string CreateBase64Key()
		{
			byte[] array = new byte[16];
			RandomNumber.GetBytes(array);
			return Convert.ToBase64String(array);
		}

		internal static string CreateResponseKey(string base64Key)
		{
			StringBuilder stringBuilder = new StringBuilder(base64Key, 64);
			stringBuilder.Append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
			SHA1 sHA = new SHA1CryptoServiceProvider();
			byte[] inArray = sHA.ComputeHash(stringBuilder.ToString().GetUTF8EncodedBytes());
			return Convert.ToBase64String(inArray);
		}

		internal void InternalAccept()
		{
			try
			{
				if (!acceptHandshake())
				{
					return;
				}
			}
			catch (Exception ex)
			{
				_logger.Fatal(ex.Message);
				_logger.Debug(ex.ToString());
				string text = "An exception has occurred while attempting to accept.";
				fatal(text, ex);
				return;
			}
			_readyState = WebSocketState.Open;
			open();
		}

		internal bool Ping(byte[] frameAsBytes, TimeSpan timeout)
		{
			if (_readyState != WebSocketState.Open)
			{
				return false;
			}
			ManualResetEvent pongReceived = _pongReceived;
			if (pongReceived == null)
			{
				return false;
			}
			lock (_forPing)
			{
				try
				{
					pongReceived.Reset();
					lock (_forState)
					{
						if (_readyState != WebSocketState.Open)
						{
							return false;
						}
						if (!sendBytes(frameAsBytes))
						{
							return false;
						}
					}
					return pongReceived.WaitOne(timeout);
				}
				catch (ObjectDisposedException)
				{
					return false;
				}
			}
		}

		internal void Send(Opcode opcode, byte[] data, Dictionary<CompressionMethod, byte[]> cache)
		{
			lock (_forSend)
			{
				lock (_forState)
				{
					if (_readyState != WebSocketState.Open)
					{
						_logger.Error("The connection is closing.");
						return;
					}
					if (!cache.TryGetValue(_compression, out var value))
					{
						value = new WebSocketFrame(Fin.Final, opcode, data.Compress(_compression), _compression != CompressionMethod.None, mask: false).ToArray();
						cache.Add(_compression, value);
					}
					sendBytes(value);
				}
			}
		}

		internal void Send(Opcode opcode, Stream stream, Dictionary<CompressionMethod, Stream> cache)
		{
			lock (_forSend)
			{
				if (!cache.TryGetValue(_compression, out var value))
				{
					value = stream.Compress(_compression);
					cache.Add(_compression, value);
				}
				else
				{
					value.Position = 0L;
				}
				send(opcode, value, _compression != CompressionMethod.None);
			}
		}

		public void Accept()
		{
			if (_client)
			{
				string text = "This instance is a client.";
				throw new InvalidOperationException(text);
			}
			if (_readyState == WebSocketState.Closing)
			{
				string text2 = "The close process is in progress.";
				throw new InvalidOperationException(text2);
			}
			if (_readyState == WebSocketState.Closed)
			{
				string text3 = "The connection has already been closed.";
				throw new InvalidOperationException(text3);
			}
			if (accept())
			{
				open();
			}
		}

		public void AcceptAsync()
		{
			if (_client)
			{
				string text = "This instance is a client.";
				throw new InvalidOperationException(text);
			}
			if (_readyState == WebSocketState.Closing)
			{
				string text2 = "The close process is in progress.";
				throw new InvalidOperationException(text2);
			}
			if (_readyState == WebSocketState.Closed)
			{
				string text3 = "The connection has already been closed.";
				throw new InvalidOperationException(text3);
			}
			Func<bool> acceptor = accept;
			acceptor.BeginInvoke(delegate(IAsyncResult ar)
			{
				if (acceptor.EndInvoke(ar))
				{
					open();
				}
			}, null);
		}

		public void Close()
		{
			close(1005, string.Empty);
		}

		public void Close(ushort code)
		{
			if (!code.IsCloseStatusCode())
			{
				string text = "Less than 1000 or greater than 4999.";
				throw new ArgumentOutOfRangeException("code", text);
			}
			if (_client && code == 1011)
			{
				string text2 = "1011 cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (!_client && code == 1010)
			{
				string text3 = "1010 cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			close(code, string.Empty);
		}

		public void Close(CloseStatusCode code)
		{
			if (_client && code == CloseStatusCode.ServerError)
			{
				string text = "ServerError cannot be used.";
				throw new ArgumentException(text, "code");
			}
			if (!_client && code == CloseStatusCode.MandatoryExtension)
			{
				string text2 = "MandatoryExtension cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			close((ushort)code, string.Empty);
		}

		public void Close(ushort code, string reason)
		{
			if (!code.IsCloseStatusCode())
			{
				string text = "Less than 1000 or greater than 4999.";
				throw new ArgumentOutOfRangeException("code", text);
			}
			if (_client && code == 1011)
			{
				string text2 = "1011 cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (!_client && code == 1010)
			{
				string text3 = "1010 cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			if (reason.IsNullOrEmpty())
			{
				close(code, string.Empty);
				return;
			}
			if (code == 1005)
			{
				string text4 = "1005 cannot be used.";
				throw new ArgumentException(text4, "code");
			}
			if (!reason.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text5 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text5, "reason");
			}
			if (bytes.Length > 123)
			{
				string text6 = "Its size is greater than 123 bytes.";
				throw new ArgumentOutOfRangeException("reason", text6);
			}
			close(code, reason);
		}

		public void Close(CloseStatusCode code, string reason)
		{
			if (_client && code == CloseStatusCode.ServerError)
			{
				string text = "ServerError cannot be used.";
				throw new ArgumentException(text, "code");
			}
			if (!_client && code == CloseStatusCode.MandatoryExtension)
			{
				string text2 = "MandatoryExtension cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (reason.IsNullOrEmpty())
			{
				close((ushort)code, string.Empty);
				return;
			}
			if (code == CloseStatusCode.NoStatus)
			{
				string text3 = "NoStatus cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			if (!reason.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text4 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text4, "reason");
			}
			if (bytes.Length > 123)
			{
				string text5 = "Its size is greater than 123 bytes.";
				throw new ArgumentOutOfRangeException("reason", text5);
			}
			close((ushort)code, reason);
		}

		public void CloseAsync()
		{
			closeAsync(1005, string.Empty);
		}

		public void CloseAsync(ushort code)
		{
			if (!code.IsCloseStatusCode())
			{
				string text = "Less than 1000 or greater than 4999.";
				throw new ArgumentOutOfRangeException("code", text);
			}
			if (_client && code == 1011)
			{
				string text2 = "1011 cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (!_client && code == 1010)
			{
				string text3 = "1010 cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			closeAsync(code, string.Empty);
		}

		public void CloseAsync(CloseStatusCode code)
		{
			if (_client && code == CloseStatusCode.ServerError)
			{
				string text = "ServerError cannot be used.";
				throw new ArgumentException(text, "code");
			}
			if (!_client && code == CloseStatusCode.MandatoryExtension)
			{
				string text2 = "MandatoryExtension cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			closeAsync((ushort)code, string.Empty);
		}

		public void CloseAsync(ushort code, string reason)
		{
			if (!code.IsCloseStatusCode())
			{
				string text = "Less than 1000 or greater than 4999.";
				throw new ArgumentOutOfRangeException("code", text);
			}
			if (_client && code == 1011)
			{
				string text2 = "1011 cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (!_client && code == 1010)
			{
				string text3 = "1010 cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			if (reason.IsNullOrEmpty())
			{
				closeAsync(code, string.Empty);
				return;
			}
			if (code == 1005)
			{
				string text4 = "1005 cannot be used.";
				throw new ArgumentException(text4, "code");
			}
			if (!reason.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text5 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text5, "reason");
			}
			if (bytes.Length > 123)
			{
				string text6 = "Its size is greater than 123 bytes.";
				throw new ArgumentOutOfRangeException("reason", text6);
			}
			closeAsync(code, reason);
		}

		public void CloseAsync(CloseStatusCode code, string reason)
		{
			if (_client && code == CloseStatusCode.ServerError)
			{
				string text = "ServerError cannot be used.";
				throw new ArgumentException(text, "code");
			}
			if (!_client && code == CloseStatusCode.MandatoryExtension)
			{
				string text2 = "MandatoryExtension cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (reason.IsNullOrEmpty())
			{
				closeAsync((ushort)code, string.Empty);
				return;
			}
			if (code == CloseStatusCode.NoStatus)
			{
				string text3 = "NoStatus cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			if (!reason.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text4 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text4, "reason");
			}
			if (bytes.Length > 123)
			{
				string text5 = "Its size is greater than 123 bytes.";
				throw new ArgumentOutOfRangeException("reason", text5);
			}
			closeAsync((ushort)code, reason);
		}

		public void Connect()
		{
			if (!_client)
			{
				string text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			if (_readyState == WebSocketState.Closing)
			{
				string text2 = "The close process is in progress.";
				throw new InvalidOperationException(text2);
			}
			if (_retryCountForConnect > _maxRetryCountForConnect)
			{
				string text3 = "A series of reconnecting has failed.";
				throw new InvalidOperationException(text3);
			}
			if (connect())
			{
				open();
			}
		}

		public void ConnectAsync()
		{
			if (!_client)
			{
				string text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			if (_readyState == WebSocketState.Closing)
			{
				string text2 = "The close process is in progress.";
				throw new InvalidOperationException(text2);
			}
			if (_retryCountForConnect > _maxRetryCountForConnect)
			{
				string text3 = "A series of reconnecting has failed.";
				throw new InvalidOperationException(text3);
			}
			Func<bool> connector = connect;
			connector.BeginInvoke(delegate(IAsyncResult ar)
			{
				if (connector.EndInvoke(ar))
				{
					open();
				}
			}, null);
		}

		public bool Ping()
		{
			return ping(EmptyBytes);
		}

		public bool Ping(string message)
		{
			if (message.IsNullOrEmpty())
			{
				return ping(EmptyBytes);
			}
			if (!message.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text, "message");
			}
			if (bytes.Length > 125)
			{
				string text2 = "Its size is greater than 125 bytes.";
				throw new ArgumentOutOfRangeException("message", text2);
			}
			return ping(bytes);
		}

		public void Send(byte[] data)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			send(Opcode.Binary, new MemoryStream(data));
		}

		public void Send(FileInfo fileInfo)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (fileInfo == null)
			{
				throw new ArgumentNullException("fileInfo");
			}
			if (!fileInfo.Exists)
			{
				string text2 = "The file does not exist.";
				throw new ArgumentException(text2, "fileInfo");
			}
			if (!fileInfo.TryOpenRead(out var fileStream))
			{
				string text3 = "The file could not be opened.";
				throw new ArgumentException(text3, "fileInfo");
			}
			send(Opcode.Binary, fileStream);
		}

		public void Send(string data)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (!data.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text2 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text2, "data");
			}
			send(Opcode.Text, new MemoryStream(bytes));
		}

		public void Send(Stream stream, int length)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			if (!stream.CanRead)
			{
				string text2 = "It cannot be read.";
				throw new ArgumentException(text2, "stream");
			}
			if (length < 1)
			{
				string text3 = "Less than 1.";
				throw new ArgumentException(text3, "length");
			}
			byte[] array = stream.ReadBytes(length);
			int num = array.Length;
			if (num == 0)
			{
				string text4 = "No data could be read from it.";
				throw new ArgumentException(text4, "stream");
			}
			if (num < length)
			{
				_logger.Warn($"Only {num} byte(s) of data could be read from the stream.");
			}
			send(Opcode.Binary, new MemoryStream(array));
		}

		public void SendAsync(byte[] data, Action<bool> completed)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			sendAsync(Opcode.Binary, new MemoryStream(data), completed);
		}

		public void SendAsync(FileInfo fileInfo, Action<bool> completed)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (fileInfo == null)
			{
				throw new ArgumentNullException("fileInfo");
			}
			if (!fileInfo.Exists)
			{
				string text2 = "The file does not exist.";
				throw new ArgumentException(text2, "fileInfo");
			}
			if (!fileInfo.TryOpenRead(out var fileStream))
			{
				string text3 = "The file could not be opened.";
				throw new ArgumentException(text3, "fileInfo");
			}
			sendAsync(Opcode.Binary, fileStream, completed);
		}

		public void SendAsync(string data, Action<bool> completed)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (!data.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text2 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text2, "data");
			}
			sendAsync(Opcode.Text, new MemoryStream(bytes), completed);
		}

		public void SendAsync(Stream stream, int length, Action<bool> completed)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			if (!stream.CanRead)
			{
				string text2 = "It cannot be read.";
				throw new ArgumentException(text2, "stream");
			}
			if (length < 1)
			{
				string text3 = "Less than 1.";
				throw new ArgumentException(text3, "length");
			}
			byte[] array = stream.ReadBytes(length);
			int num = array.Length;
			if (num == 0)
			{
				string text4 = "No data could be read from it.";
				throw new ArgumentException(text4, "stream");
			}
			if (num < length)
			{
				_logger.Warn($"Only {num} byte(s) of data could be read from the stream.");
			}
			sendAsync(Opcode.Binary, new MemoryStream(array), completed);
		}

		public void SetCookie(WebSocketSharp.Net.Cookie cookie)
		{
			string text = null;
			if (!_client)
			{
				text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			if (cookie == null)
			{
				throw new ArgumentNullException("cookie");
			}
			if (!canSet(out text))
			{
				_logger.Warn(text);
				return;
			}
			lock (_forState)
			{
				if (!canSet(out text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_cookies.SyncRoot)
				{
					_cookies.SetOrRemove(cookie);
				}
			}
		}

		public void SetCredentials(string username, string password, bool preAuth)
		{
			string text = null;
			if (!_client)
			{
				text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			if (!username.IsNullOrEmpty() && (Ext.Contains(username, ':') || !username.IsText()))
			{
				text = "It contains an invalid character.";
				throw new ArgumentException(text, "username");
			}
			if (!password.IsNullOrEmpty() && !password.IsText())
			{
				text = "It contains an invalid character.";
				throw new ArgumentException(text, "password");
			}
			if (!canSet(out text))
			{
				_logger.Warn(text);
				return;
			}
			lock (_forState)
			{
				if (!canSet(out text))
				{
					_logger.Warn(text);
				}
				else if (username.IsNullOrEmpty())
				{
					_credentials = null;
					_preAuth = false;
				}
				else
				{
					_credentials = new WebSocketSharp.Net.NetworkCredential(username, password, _uri.PathAndQuery);
					_preAuth = preAuth;
				}
			}
		}

		public void SetProxy(string url, string username, string password)
		{
			string text = null;
			if (!_client)
			{
				text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			Uri result = null;
			if (!url.IsNullOrEmpty())
			{
				if (!Uri.TryCreate(url, UriKind.Absolute, out result))
				{
					text = "Not an absolute URI string.";
					throw new ArgumentException(text, "url");
				}
				if (result.Scheme != "http")
				{
					text = "The scheme part is not http.";
					throw new ArgumentException(text, "url");
				}
				if (result.Segments.Length > 1)
				{
					text = "It includes the path segments.";
					throw new ArgumentException(text, "url");
				}
			}
			if (!username.IsNullOrEmpty() && (Ext.Contains(username, ':') || !username.IsText()))
			{
				text = "It contains an invalid character.";
				throw new ArgumentException(text, "username");
			}
			if (!password.IsNullOrEmpty() && !password.IsText())
			{
				text = "It contains an invalid character.";
				throw new ArgumentException(text, "password");
			}
			if (!canSet(out text))
			{
				_logger.Warn(text);
				return;
			}
			lock (_forState)
			{
				if (!canSet(out text))
				{
					_logger.Warn(text);
		

BepInEx/plugins/WoLArchipelago/Archipelago.MultiClient.Net.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.Authentication;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
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 Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using WebSocketSharp;

[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: 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: AssemblyVersion("6.7.1.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; }

		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 volatile bool awaitingRoomInfo;

		private volatile bool expectingLoginResult;

		private LoginResult loginResult;

		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)
				{
					awaitingRoomInfo = false;
				}
			}
			else if (expectingLoginResult)
			{
				expectingLoginResult = false;
				loginResult = LoginResult.FromPacket(packet);
			}
		}

		public LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true)
		{
			connectionInfo.SetConnectionParameters(game, tags, itemsHandlingFlags, uuid);
			try
			{
				awaitingRoomInfo = true;
				expectingLoginResult = true;
				loginResult = null;
				Socket.Connect();
				DateTime utcNow = DateTime.UtcNow;
				while (awaitingRoomInfo)
				{
					if (DateTime.UtcNow - utcNow > TimeSpan.FromSeconds(4.0))
					{
						Socket.Disconnect();
						return new LoginFailure("Connection timed out.");
					}
					Thread.Sleep(25);
				}
				Socket.SendPacket(BuildConnectPacket(name, password, version, requestSlotData));
				utcNow = DateTime.UtcNow;
				while (expectingLoginResult)
				{
					if (DateTime.UtcNow - utcNow > TimeSpan.FromSeconds(4.0))
					{
						Socket.Disconnect();
						return new LoginFailure("Connection timed out.");
					}
					Thread.Sleep(25);
				}
				Thread.Sleep(50);
				return loginResult;
			}
			catch (ArchipelagoSocketClosedException)
			{
				return new LoginFailure("Socket closed unexpectedly.");
			}
		}

		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 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 void GetAsync<T>(Action<T> callback)
		{
			GetAsync(delegate(JToken t)
			{
				callback(t.ToObject<T>());
			});
		}

		public void GetAsync(Action<JToken> callback)
		{
			Context.GetAsync(Context.Key, callback);
		}

		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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Invalid comparison between Unknown and I4
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Invalid comparison between Unknown and I4
			//IL_00d8: 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 {OperationType.Add} 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_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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 {OperationType.Mul} on string value, with a non interger value: {operation.Value}");
					}
					text = string.Concat((object?)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 (object)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 Action<string, Action<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 = ((object)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 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 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 Or(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Or,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification And(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.And,
				Value = JToken.op_Implicit(i)
			};
		}

		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 : IArchipelagoSocketHelper
	{
		private const SslProtocols Tls13 = SslProtocols.Tls13;

		private const SslProtocols Tls12 = SslProtocols.Tls12;

		private static readonly ArchipelagoPacketConverter Converter = new ArchipelagoPacketConverter();

		internal WebSocket webSocket;

		public Uri Uri { get; }

		public bool Connected
		{
			get
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Invalid comparison between Unknown and I4
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Invalid comparison between Unknown and I4
				if (webSocket != null)
				{
					if ((int)webSocket.ReadyState != 1)
					{
						return (int)webSocket.ReadyState == 2;
					}
					return true;
				}
				return false;
			}
		}

		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 ArchipelagoSocketHelper(Uri hostUrl)
		{
			Uri = hostUrl;
		}

		private WebSocket CreateWebSocket(Uri uri)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			WebSocket val = new WebSocket(uri.ToString(), new string[0]);
			if (val.IsSecure)
			{
				val.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
			}
			val.OnMessage += OnMessageReceived;
			val.OnError += OnError;
			val.OnClose += OnClose;
			val.OnOpen += OnOpen;
			return val;
		}

		public void Connect()
		{
			ConnectToProvidedUri(Uri);
		}

		private void ConnectToProvidedUri(Uri uri)
		{
			if (uri.Scheme != "unspecified")
			{
				try
				{
					webSocket = CreateWebSocket(uri);
					webSocket.Connect();
					return;
				}
				catch (Exception e)
				{
					OnError(e);
					return;
				}
			}
			List<Exception> list = new List<Exception>();
			try
			{
				try
				{
					ConnectToProvidedUri(uri.AsWss());
				}
				catch (Exception item)
				{
					list.Add(item);
					throw;
				}
				if (webSocket.IsAlive)
				{
					return;
				}
				try
				{
					ConnectToProvidedUri(uri.AsWs());
				}
				catch (Exception item2)
				{
					list.Add(item2);
					throw;
				}
			}
			catch
			{
				try
				{
					ConnectToProvidedUri(uri.AsWs());
				}
				catch (Exception item3)
				{
					list.Add(item3);
					OnError(new Archipelago.MultiClient.Net.Exceptions.AggregateException(list));
				}
			}
		}

		public void Disconnect()
		{
			if (webSocket != null && webSocket.IsAlive)
			{
				webSocket.Close();
			}
		}

		public void DisconnectAsync()
		{
			if (webSocket != null && webSocket.IsAlive)
			{
				webSocket.CloseAsync();
			}
		}

		public void SendPacket(ArchipelagoPacketBase packet)
		{
			SendMultiplePackets(packet);
		}

		public void SendMultiplePackets(List<ArchipelagoPacketBase> packets)
		{
			SendMultiplePackets(packets.ToArray());
		}

		public void SendMultiplePackets(params ArchipelagoPacketBase[] packets)
		{
			if (webSocket != null && webSocket.IsAlive)
			{
				string text = JsonConvert.SerializeObject((object)packets);
				webSocket.Send(text);
				if (this.PacketsSent != null)
				{
					this.PacketsSent(packets);
				}
				return;
			}
			throw new ArchipelagoSocketClosedException();
		}

		public void SendPacketAsync(ArchipelagoPacketBase packet, Action<bool> onComplete = null)
		{
			SendMultiplePacketsAsync(new List<ArchipelagoPacketBase> { packet }, onComplete);
		}

		public void SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets, Action<bool> onComplete = null)
		{
			SendMultiplePacketsAsync(onComplete, packets.ToArray());
		}

		public void SendMultiplePacketsAsync(Action<bool> onComplete = null, params ArchipelagoPacketBase[] packets)
		{
			if (webSocket.IsAlive)
			{
				string text = JsonConvert.SerializeObject((object)packets);
				webSocket.SendAsync(text, onComplete);
				if (this.PacketsSent != null)
				{
					this.PacketsSent(packets);
				}
				return;
			}
			throw new ArchipelagoSocketClosedException();
		}

		private void OnOpen(object sender, EventArgs e)
		{
			if (this.SocketOpened != null)
			{
				this.SocketOpened();
			}
		}

		private void OnClose(object sender, CloseEventArgs e)
		{
			if ((!(Uri.Scheme == "unspecified") || sender != webSocket || !(webSocket.Url.Scheme == "wss")) && this.SocketClosed != null)
			{
				this.SocketClosed(e.Reason);
			}
		}

		private void OnMessageReceived(object sender, MessageEventArgs e)
		{
			if (!e.IsText || this.PacketReceived == null)
			{
				return;
			}
			List<ArchipelagoPacketBase> list = null;
			try
			{
				list = JsonConvert.DeserializeObject<List<ArchipelagoPacketBase>>(e.Data, (JsonConverter[])(object)new JsonConverter[1] { Converter });
			}
			catch (Exception e2)
			{
				OnError(e2);
			}
			if (list == null)
			{
				return;
			}
			foreach (ArchipelagoPacketBase item in list)
			{
				this.PacketReceived(item);
			}
		}

		private void OnError(object sender, ErrorEventArgs e)
		{
			if (this.ErrorReceived != null)
			{
				this.ErrorReceived(e.Exception, e.Message);
			}
		}

		private void OnError(Exception e)
		{
			if (this.ErrorReceived != null)
			{
				this.ErrorReceived(e, e.Message);
			}
		}
	}
	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, Action<JToken>> asyncRetrievalCallbacks = new Dictionary<string, Action<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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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 (asyncRetrievalCallbacks.TryGetValue(datum.Key, out var value3))
				{
					value3(datum.Value);
					asyncRetrievalCallbacks.Remove(datum.Key);
				}
			}
		}

		private void GetAsync(string key, Action<JToken> callback)
		{
			if (!asyncRetrievalCallbacks.ContainsKey(key))
			{
				asyncRetrievalCallbacks[key] = callback;
			}
			else
			{
				Dictionary<string, Action<JToken>> dictionary = asyncRetrievalCallbacks;
				dictionary[key] = (Action<JToken>)Delegate.Combine(dictionary[key], callback);
			}
			socket.SendPacketAsync(new GetPacket
			{
				Keys = new string[1] { key }
			});
		}

		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)
		{
			JToken value = null;
			GetAsync(key, delegate(JToken v)
			{
				value = v;
			});
			int num = 0;
			while (value == null)
			{
				Thread.Sleep(10);
				if (++num > 200)
				{
					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(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 value;
		}

		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 void GetHintsAsync(Action<Hint[]> onHintsRetrieved, int? slot = null, int? team = null)
		{
			GetHintsElement(slot, team).GetAsync(delegate(JToken t)
			{
				onHintsRetrieved((t != null) ? t.ToObject<Hint[]>() : null);
			});
		}

		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(onHintsUpdated, slot, team);
			}
		}

		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 void GetSlotDataAsync(Action<Dictionary<string, object>> onSlotDataRetrieved, int? slot = null)
		{
			GetSlotDataElement(slot).GetAsync(delegate(JToken t)
			{
				onSlotDataRetrieved((t != null) ? t.ToObject<Dictionary<string, object>>() : null);
			});
		}

		public void GetSlotDataAsync<T>(Action<T> onSlotDataRetrieved, int? slot = null) where T : class
		{
			GetSlotDataElement(slot).GetAsync(delegate(JToken t)
			{
				onSlotDataRetrieved((t != null) ? t.ToObject<T>() : null);
			});
		}

		public Dictionary<string, string[]> GetItemNameGroups(string game = null)
		{
			return GetItemNameGroupsElement(game).To<Dictionary<string, string[]>>();
		}

		public void GetItemNameGroupsAsync(Action<Dictionary<string, string[]>> onItemNameGroupsRetrieved, string game = null)
		{
			GetItemNameGroupsElement(game).GetAsync(delegate(JToken t)
			{
				onItemNameGroupsRetrieved((t != null) ? t.ToObject<Dictionary<string, string[]>>() : null);
			});
		}

		public Dictionary<string, string[]> GetLocationNameGroups(string game = null)
		{
			return GetLocationNameGroupsElement(game).To<Dictionary<string, string[]>>();
		}

		public void GetLocationNameGroupsAsync(Action<Dictionary<string, string[]>> onLocationNameGroupsRetrieved, string game = null)
		{
			GetLocationNameGroupsElement(game).GetAsync(delegate(JToken t)
			{
				onLocationNameGroupsRetrieved((t != null) ? t.ToObject<Dictionary<string, string[]>>() : null);
			});
		}

		public ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null)
		{
			return GetClientStatusElement(slot, team).To<ArchipelagoClientState?>().GetValueOrDefault();
		}

		public void GetClientStatusAsync(Action<ArchipelagoClientState> onStatusRetrieved, int? slot = null, int? team = null)
		{
			GetClientStatusElement(slot, team).GetAsync(delegate(JToken t)
			{
				onStatusRetrieved(t.ToObject<ArchipelagoClientState?>().GetValueOrDefault());
			});
		}

		public void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null)
		{
			GetClientStatusElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x)
			{
				onStatusUpdated(newValue.ToObject<ArchipelagoClientState>());
			};
			if (retrieveCurrentClientStatus)
			{
				GetClientStatusAsync(onStatusUpdated, slot, team);
			}
		}

		public bool GetRaceMode()
		{
			return GetRaceModeElement().To<int?>().GetValueOrDefault() > 0;
		}

		public void GetRaceModeAsync(Action<bool> onRaceModeRetrieved)
		{
			GetRaceModeElement().GetAsync(delegate(JToken t)
			{
				onRaceModeRetrieved(t.ToObject<int?>().GetValueOrDefault() > 0);
			});
		}
	}
	public interface IDataStorageWrapper
	{
		Hint[] GetHints(int? slot = null, int? team = null);

		void GetHintsAsync(Action<Hint[]> onHintsRetrieved, int? slot = null, int? team = null);

		void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null);

		Dictionary<string, object> GetSlotData(int? slot = null);

		T GetSlotData<T>(int? slot = null) where T : class;

		void GetSlotDataAsync(Action<Dictionary<string, object>> onSlotDataRetrieved, int? slot = null);

		void GetSlotDataAsync<T>(Action<T> onSlotDataRetrieved, int? slot = null) where T : class;

		Dictionary<string, string[]> GetItemNameGroups(string game = null);

		void GetItemNameGroupsAsync(Action<Dictionary<string, string[]>> onItemNameGroupsRetrieved, string game = null);

		Dictionary<string, string[]> GetLocationNameGroups(string game = null);

		void GetLocationNameGroupsAsync(Action<Dictionary<string, string[]>> onLocationNameGroupsRetrieved, string game = null);

		ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null);

		void GetClientStatusAsync(Action<ArchipelagoClientState> onStatusRetrieved, int? slot = null, int? team = null);

		void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null);

		bool GetRaceMode();

		void GetRaceModeAsync(Action<bool> onRaceModeRetrieved);
	}
	public interface IHintsHelper
	{
		void CreateHints(int player, HintStatus hintStatus = HintStatus.Unspecified, params long[] locationIds);

		void CreateHints(HintStatus hintStatus = HintStatus.Unspecified, params long[] locationIds);

		void UpdateHintStatus(int player, long locationId, HintStatus newHintStatus);

		Hint[] GetHints(int? slot = null, int? team = null);

		void GetHintsAsync(Action<Hint[]> onHintsRetrieved, int? slot = null, int? team = null);

		void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null);
	}
	public class HintsHelper : IHintsHelper
	{
		private readonly IArchipelagoSocketHelper socket;

		private readonly ILocationCheckHelper locationCheckHelper;

		private readonly IRoomStateHelper roomStateHelper;

		private readonly IPlayerHelper players;

		private readonly IDataStorageHelper dataStorageHelper;

		internal HintsHelper(IArchipelagoSocketHelper socket, IPlayerHelper players, ILocationCheckHelper locationCheckHelper, IRoomStateHelper roomStateHelper, IDataStorageHelper dataStorageHelper)
		{
			this.socket = socket;
			this.players = players;
			this.locationCheckHelper = locationCheckHelper;
			this.roomStateHelper = roomStateHelper;
			this.dataStorageHelper = dataStorageHelper;
		}

		public void CreateHints(int player, HintStatus hintStatus = HintStatus.Unspecified, params long[] locationIds)
		{
			if (roomStateHelper.Version < new Version(0, 6, 2))
			{
				locationCheckHelper.ScoutLocationsAsync(null, createAsHint: true, locationIds);
				return;
			}
			socket.SendPacket(new CreateHintsPacket
			{
				Locations = locationIds,
				Player = player,
				Status = hintStatus
			});
		}

		public void CreateHints(HintStatus hintStatus = HintStatus.Unspecified, params long[] ids)
		{
			int slot = players.ActivePlayer.Slot;
			CreateHints(slot, hintStatus, ids);
		}

		public void UpdateHintStatus(int player, long locationId, HintStatus newHintStatus)
		{
			socket.SendPacket(new UpdateHintPacket
			{
				Location = locationId,
				Player = player,
				Status = newHintStatus
			});
		}

		public Hint[] GetHints(int? slot = null, int? team = null)
		{
			return dataStorageHelper.GetHints(slot, team);
		}

		public void GetHintsAsync(Action<Hint[]> onHintsRetrieved, int? slot = null, int? team = null)
		{
			dataStorageHelper.GetHintsAsync(onHintsRetrieved, slot, team);
		}

		public void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null)
		{
			dataStorageHelper.TrackHints(onHintsUpdated, retrieveCurrentlyUnlockedHints, slot, team);
		}
	}
	public class ArchipelagoSocketHelperDelagates
	{
		public delegate void PacketReceivedHandler(ArchipelagoPacketBase packet);

		public delegate void PacketsSentHandler(ArchipelagoPacketBase[] packets);

		public delegate void ErrorReceivedHandler(Exception e, string message);

		public delegate void SocketClosedHandler(string reason);

		public delegate void SocketOpenedHandler();
	}
	public interface IArchipelagoSocketHelper
	{
		Uri Uri { get; }

		bool Connected { get; }

		event ArchipelagoSocketHelperDelagates.PacketReceivedHandler PacketReceived;

		event ArchipelagoSocketHelperDelagates.PacketsSentHandler PacketsSent;

		event ArchipelagoSocketHelperDelagates.ErrorReceivedHandler ErrorReceived;

		event ArchipelagoSocketHelperDelagates.SocketClosedHandler SocketClosed;

		event ArchipelagoSocketHelperDelagates.SocketOpenedHandler SocketOpened;

		void SendPacket(ArchipelagoPacketBase packet);

		void SendMultiplePackets(List<ArchipelagoPacketBase> packets);

		void SendMultiplePackets(params ArchipelagoPacketBase[] packets);

		void Connect();

		void Disconnect();

		void SendPacketAsync(ArchipelagoPacketBase packet, Action<bool> onComplete = null);

		void SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets, Action<bool> onComplete = null);

		void SendMultiplePacketsAsync(Action<bool> onComplete = null, params ArchipelagoPacketBase[] packets);
	}
	public interface ILocationCheckHelper
	{
		ReadOnlyCollection<long> AllLocations { get; }

		ReadOnlyCollection<long> AllLocationsChecked { get; }

		ReadOnlyCollection<long> AllMissingLocations { get; }

		event LocationCheckHelper.CheckedLocationsUpdatedHandler CheckedLocationsUpdated;

		void CompleteLocationChecks(params long[] ids);

		void CompleteLocationChecksAsync(Action<bool> onComplete, params long[] ids);

		void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, HintCreationPolicy hintCreationPolicy = HintCreationPolicy.None, params long[] ids);

		void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, bool createAsHint = false, params long[] ids);

		void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, params long[] ids);

		long GetLocationIdFromName(string game, string locationName);

		string GetLocationNameFromId(long locationId, string game = null);
	}
	public class LocationCheckHelper : ILocationCheckHelper
	{
		public delegate void CheckedLocationsUpdatedHandler(ReadOnlyCollection<long> newCheckedLocations);

		private readonly IConcurrentHashSet<long> allLocations = new ConcurrentHashSet<long>();

		private readonly IConcurrentHashSet<long> locationsChecked = new ConcurrentHashSet<long>();

		private readonly IConcurrentHashSet<long> serverConfirmedChecks = new ConcurrentHashSet<long>();

		private ReadOnlyCollection<long> missingLocations = new ReadOnlyCollection<long>(new long[0]);

		private readonly IArchipelagoSocketHelper socket;

		private readonly IItemInfoResolver itemInfoResolver;

		private readonly IConnectionInfoProvider connectionInfoProvider;

		private readonly IPlayerHelper players;

		private bool awaitingLocationInfoPacket;

		private Action<LocationInfoPacket> locationInfoPacketCallback;

		public ReadOnlyCollection<long> AllLocations => allLocations.AsToReadOnlyCollection();

		public ReadOnlyCollection<long> AllLocationsChecked => locationsChecked.AsToReadOnlyCollection();

		public ReadOnlyCollection<long> AllMissingLocations => missingLocations;

		public event CheckedLocationsUpdatedHandler CheckedLocationsUpdated;

		internal LocationCheckHelper(IArchipelagoSocketHelper socket, IItemInfoResolver itemInfoResolver, IConnectionInfoProvider connectionInfoProvider, IPlayerHelper players)
		{
			this.socket = socket;
			this.itemInfoResolver = itemInfoResolver;
			this.connectionInfoProvider = connectionInfoProvider;
			this.players = players;
			socket.PacketReceived += Socket_PacketReceived;
		}

		private void Socket_PacketReceived(ArchipelagoPacketBase packet)
		{
			if (!(packet is ConnectedPacket connectedPacket))
			{
		

BepInEx/plugins/WoLArchipelago/WoLArchipelago.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using Archipelago.MultiClient.Net;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Helpers;
using Archipelago.MultiClient.Net.Models;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Org.BouncyCastle.Crypto.Tls;
using Org.BouncyCastle.Security;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using WoLArchipelago.Services;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyCompany("WoLArchipelago")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+5969276975a52a2529b44d3b458fafaf27e08185")]
[assembly: AssemblyProduct("WoLArchipelago")]
[assembly: AssemblyTitle("WoLArchipelago")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WoLArchipelago
{
	public static class APItemLocationDatabase
	{
		public static readonly Dictionary<long, string> ItemIdToName;

		public static readonly Dictionary<string, long> ItemNameToId;

		public static readonly Dictionary<long, string> LocationIdToName;

		public static readonly Dictionary<string, long> LocationNameToId;

		private static long baseLocationCode;

		static APItemLocationDatabase()
		{
			ItemIdToName = new Dictionary<long, string>();
			ItemNameToId = new Dictionary<string, long>();
			LocationIdToName = new Dictionary<long, string>();
			LocationNameToId = new Dictionary<string, long>();
			baseLocationCode = 871130000L;
			InitializeItems();
			InitializeLocations();
		}

		public static string GetItemName(long id)
		{
			string value;
			return ItemIdToName.TryGetValue(id, out value) ? value : "Unknown Item";
		}

		public static long GetItemId(string name)
		{
			long value;
			return ItemNameToId.TryGetValue(name, out value) ? value : (-1);
		}

		public static long GetLocationId(string name)
		{
			long value;
			return LocationNameToId.TryGetValue(name, out value) ? value : (-1);
		}

		private static void AddItem(string name, long id)
		{
			ItemNameToId[name] = id;
			ItemIdToName[id] = name;
		}

		private static void AddLocation(string name)
		{
			LocationNameToId[name] = baseLocationCode;
			LocationIdToName[baseLocationCode] = name;
			baseLocationCode++;
		}

		private static void AddMultipleLocations(string baseName, int count)
		{
			for (int i = 1; i <= count; i++)
			{
				AddLocation(baseName + " " + i);
			}
		}

		private static void InitializeItems()
		{
			AddItem("Chaos Fragment", 871120001L);
			AddItem("Boss Key", 871120002L);
			AddItem("Shop Upgrade", 871120003L);
			AddItem("Fire Biome Key", 871120010L);
			AddItem("Water Biome Key", 871120011L);
			AddItem("Earth Biome Key", 871120012L);
			AddItem("Air Biome Key", 871120013L);
			AddItem("Lightning Biome Key", 871120014L);
			AddItem("Outfit: Hope", 871121001L);
			AddItem("Outfit: Patience", 871121002L);
			AddItem("Outfit: Vigor", 871121003L);
			AddItem("Outfit: Grit", 871121004L);
			AddItem("Outfit: Greed", 871121005L);
			AddItem("Outfit: Pink", 871121006L);
			AddItem("Outfit: Pace", 871121007L);
			AddItem("Outfit: Tempo", 871121008L);
			AddItem("Outfit: Switch", 871121009L);
			AddItem("Outfit: Awe", 871121010L);
			AddItem("Outfit: Fury", 871121011L);
			AddItem("Outfit: Rule", 871121012L);
			AddItem("Outfit: Level", 871121013L);
			AddItem("Outfit: Venture", 871121014L);
			AddItem("Outfit: Fall", 871121015L);
			AddItem("Outfit: Pride", 871121016L);
			AddItem("Standard Arcana Slot", 871122001L);
			AddItem("Signature Arcana Slot", 871122002L);
			AddItem("Bonus Arcana Slot 1", 871122003L);
			AddItem("Bonus Arcana Slot 2", 871122004L);
			AddItem("Fire Element License", 871122010L);
			AddItem("Water Element License", 871122011L);
			AddItem("Earth Element License", 871122012L);
			AddItem("Air Element License", 871122013L);
			AddItem("Lightning Element License", 871122014L);
			AddItem("Max HP Boost", 871123001L);
			AddItem("Gold Pack", 871123002L);
			AddItem("Chaos Gems Pack", 871123003L);
			AddItem("Relic Slot Upgrade", 871123004L);
			AddItem("Cursed Trap", 871124001L);
			AddItem("Relic Tier 1", 871125001L);
			AddItem("Relic Tier 2", 871125002L);
			AddItem("Relic Tier 3", 871125003L);
			AddItem("Relic Tier 4", 871125004L);
			AddItem("Relic Tier 5", 871125005L);
			AddItem("Arcana Tier 1", 871126001L);
			AddItem("Arcana Tier 2", 871126002L);
			AddItem("Arcana Tier 3", 871126003L);
			AddItem("Arcana Tier 4", 871126004L);
			AddItem("Arcana Tier 5", 871126005L);
		}

		private static void InitializeLocations()
		{
			string[] array = new string[5] { "AirBoss", "EarthBoss", "FireBoss", "IceBoss", "LightningBoss" };
			string[] array2 = array;
			foreach (string text in array2)
			{
				AddLocation(text + " Defeated");
				AddLocation(text + " Defeated 5 times");
			}
			AddLocation("FinalBoss Defeated");
			string[] array3 = new string[7] { "SuperArcher", "SuperCoffin", "SuperKnight", "SuperLancer", "SuperMage", "SuperRogue", "SuperSummoner" };
			string[] array4 = array3;
			foreach (string text2 in array4)
			{
				AddLocation(text2 + " Defeated");
				AddLocation(text2 + " Defeated 5 times");
			}
			string[] array5 = new string[13]
			{
				"Blob", "BlobRoller", "BlobSpitter", "Ghoul", "GhoulLarge", "Coffin", "EnemyTurret", "Archer", "Knight", "Mage",
				"Rogue", "Lancer", "Summoner"
			};
			string[] array6 = array5;
			foreach (string text3 in array6)
			{
				AddLocation(text3 + " Defeated 25 times");
				AddLocation(text3 + " Defeated 50 times");
			}
			AddLocation("MovingStatue Defeated 10 times");
			AddLocation("MovingStatue Defeated 20 times");
			AddLocation("MimicEnemy Defeated 5 times");
			AddLocation("MimicEnemy Defeated 10 times");
			AddLocation("Pinata Defeated");
			AddLocation("Pinata Defeated 5 times");
			string[] array7 = new string[5] { "Fire", "Water", "Earth", "Air", "Lightning" };
			string[] array8 = array7;
			foreach (string text4 in array8)
			{
				AddLocation(text4 + " Enemies Defeated 25 times");
				AddLocation(text4 + " Enemies Defeated 50 times");
			}
			AddMultipleLocations("Outfit Shop Slot", 16);
			AddMultipleLocations("Relic Shop Slot", 64);
			AddMultipleLocations("Arcana Shop Slot", 64);
			AddMultipleLocations("Doctor Song Slot", 10);
			AddMultipleLocations("Savile the Tailor Slot", 10);
			AddMultipleLocations("Nox the Unfortunate Slot", 40);
			AddMultipleLocations("Nocturne the Cardist Slot", 10);
			AddMultipleLocations("Cremire the Collector Slot", 10);
			AddMultipleLocations("Doki the Banker Slot", 10);
			AddMultipleLocations("Strange Time Keeper Slot", 2);
			string[] array9 = array7;
			foreach (string text5 in array9)
			{
				AddMultipleLocations(text5 + " Chest Slot", 20);
			}
			AddMultipleLocations("MiniBoss Chest Slot", 40);
			AddMultipleLocations("Boss Chest Slot", 20);
			int[] array10 = new int[5] { 10, 25, 50, 75, 100 };
			int[] array11 = array10;
			foreach (int num in array11)
			{
				AddLocation($"Open {num} Total Chests");
			}
			AddLocation("Stage 1-1 Cleared");
			AddLocation("Stage 1-2 Cleared");
			AddLocation("Stage 2-1 Cleared");
			AddLocation("Stage 2-2 Cleared");
			AddLocation("Stage 3-1 Cleared");
			AddLocation("Stage 3-2 Cleared");
			AddLocation("First Council Member Defeated");
			AddLocation("Second Council Member Defeated");
			AddLocation("Third Council Member Defeated");
			AddLocation("Break 50 Paintings");
			AddLocation("Break 100 Paintings");
			AddLocation("Die 5 times");
			AddLocation("Die 10 times");
			AddLocation("Fall 25 times");
			AddLocation("Fall 50 times");
			AddLocation("Dash 100 times");
			AddLocation("Dash 500 times");
			AddLocation("Have 500 chaos gems");
			AddLocation("Have 1000 gold");
			AddLocation("Perfect a boss");
		}
	}
	public class APManager
	{
		private readonly object _lockObject = new object();

		private readonly StorageService _storageService = new StorageService();

		private readonly HashSet<long> _checkedLocations = new HashSet<long>();

		private readonly Queue<long> _itemsToProcess = new Queue<long>();

		private TlsProxyServer _tlsProxy;

		private Queue<long> _offlineCheckQueue = new Queue<long>();

		private int _itemsReceivedIndex;

		private bool _pendingGoalCompletion;

		private string _lastHost;

		private int _lastPort;

		private string _lastSlot;

		private string _lastPassword;

		public ArchipelagoSession Session { get; private set; }

		public Dictionary<long, ScoutedItemInfo> ScoutedLocations { get; private set; }

		public bool IsConnected { get; private set; }

		public string StatusMessage { get; private set; }

		public static int StartingArcanaMode { get; private set; }

		public static int ElementLicensesMode { get; private set; }

		public static int ChaosFragmentsRequired { get; private set; }

		public static string StartingElement { get; private set; }

		public static string CurrentAPSavePrefix => StorageService.CurrentAPSavePrefix;

		public APManager()
		{
			ScoutedLocations = new Dictionary<long, ScoutedItemInfo>();
			StatusMessage = "Disconnected";
			StorageService.InitProfile();
			ItemHandler.CachedItems.Clear();
			_offlineCheckQueue = _storageService.LoadPendingChecks(_checkedLocations);
			_itemsReceivedIndex = _storageService.LoadItemIndex();
			_pendingGoalCompletion = DataManager.IsGoalCompleted;
		}

		public void Connect(string host, int port, string slotName, string password = null, bool isReconnecting = false)
		{
			_lastHost = host;
			_lastPort = port;
			_lastSlot = slotName;
			_lastPassword = password;
			StatusMessage = "Connecting...";
			ThreadPool.QueueUserWorkItem(delegate
			{
				try
				{
					DisconnectInternal();
					string text = PrepareConnectUri(host, port);
					Session = ArchipelagoSessionFactory.CreateSession(text, 38281);
					LoginResult val = Session.TryConnectAndLogin("Wizard of Legend", slotName, (ItemsHandlingFlags)7, (Version)null, (string[])null, (string)null, password, true);
					LoginSuccessful val2 = (LoginSuccessful)(object)((val is LoginSuccessful) ? val : null);
					if (val2 != null)
					{
						HandleSuccessfulLogin(val2, slotName, isReconnecting);
					}
					else
					{
						LoginFailure val3 = (LoginFailure)(object)((val is LoginFailure) ? val : null);
						if (val3 != null)
						{
							HandleLoginFailure(val3);
						}
					}
				}
				catch (Exception ex)
				{
					IsConnected = false;
					StatusMessage = $"Error: {ex.Message}";
					Plugin.Log.LogError((object)$"[AP] Connection error: {ex}");
				}
			});
		}

		private string PrepareConnectUri(string host, int port)
		{
			if (host.Contains("archipelago.gg") || host.StartsWith("wss://"))
			{
				string targetHost = host.Replace("wss://", "").Replace("ws://", "");
				_tlsProxy = new TlsProxyServer(targetHost, port);
				_tlsProxy.Start();
				return $"ws://127.0.0.1:{_tlsProxy.LocalPort}";
			}
			return host.StartsWith("ws://") ? host : $"ws://{host}:{port}";
		}

		private void HandleSuccessfulLogin(LoginSuccessful loginSuccess, string slotName, bool isReconnecting)
		{
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Expected O, but got Unknown
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			IsConnected = true;
			StatusMessage = "Online";
			_storageService.SetAPSaveProfile(Session.RoomState.Seed, slotName);
			ParseSlotData(loginSuccess.SlotData);
			Plugin.ExecuteOnMainThread(delegate
			{
				GameDataManager.Load();
				if ((Object)(object)ArchipelagoUI.Instance != (Object)null)
				{
					ArchipelagoUI.Instance.ToggleHasConnectedOnce();
				}
			});
			lock (_lockObject)
			{
				_checkedLocations.Clear();
				_offlineCheckQueue.Clear();
				_itemsToProcess.Clear();
				ItemHandler.CachedItems.Clear();
				_itemsReceivedIndex = _storageService.LoadItemIndex();
				_offlineCheckQueue = _storageService.LoadPendingChecks(_checkedLocations);
				ItemHandler.CachedItems.AddRange(Session.Items.AllItemsReceived);
				while (_itemsReceivedIndex < Session.Items.AllItemsReceived.Count)
				{
					ItemInfo val = Session.Items.AllItemsReceived[_itemsReceivedIndex];
					_itemsToProcess.Enqueue(val.ItemId);
					_itemsReceivedIndex++;
					_storageService.SaveItemIndex(_itemsReceivedIndex);
				}
				DataManager.LoadData();
			}
			Session.Locations.ScoutLocationsAsync((Action<Dictionary<long, ScoutedItemInfo>>)delegate(Dictionary<long, ScoutedItemInfo> scoutedInfo)
			{
				lock (_lockObject)
				{
					ScoutedLocations = scoutedInfo;
				}
			}, new List<long>(Session.Locations.AllLocations).ToArray());
			Session.Items.ItemReceived += new ItemReceivedHandler(OnItemReceived);
			Session.Socket.SocketClosed += new SocketClosedHandler(OnSocketClosed);
			foreach (long item in Session.Locations.AllLocationsChecked)
			{
				_checkedLocations.Add(item);
			}
			if (!isReconnecting)
			{
				Plugin.StartAPRun();
			}
			FlushOfflineQueue();
		}

		private void HandleLoginFailure(LoginFailure failure)
		{
			IsConnected = false;
			StatusMessage = string.Format("Failed: {0}", string.Join(", ", failure.Errors));
			Plugin.Log.LogError((object)$"[AP] {StatusMessage}");
		}

		public Tuple<string, string> GetLocationInfo(long locationId)
		{
			lock (_lockObject)
			{
				if (ScoutedLocations != null && ScoutedLocations.TryGetValue(locationId, out var value))
				{
					string text = "Unknown Player";
					if (value.Player != (PlayerInfo)null)
					{
						text = value.Player.Alias ?? value.Player.Name ?? "Unknown Player";
					}
					string text2 = ((!string.IsNullOrEmpty(((ItemInfo)value).ItemDisplayName)) ? ((ItemInfo)value).ItemDisplayName : ((ItemInfo)value).ItemName);
					return new Tuple<string, string>(text, text2);
				}
			}
			return new Tuple<string, string>("Unknown Player", "Emplacement Archipelago");
		}

		public void Disconnect()
		{
			DisconnectInternal();
			StatusMessage = "Disconnected (Offline Mode)";
		}

		private void DisconnectInternal()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			if (Session != null)
			{
				Session.Items.ItemReceived -= new ItemReceivedHandler(OnItemReceived);
				if (Session.Socket != null)
				{
					Session.Socket.SocketClosed -= new SocketClosedHandler(OnSocketClosed);
				}
				Session = null;
			}
			if (_tlsProxy != null)
			{
				_tlsProxy.Stop();
				_tlsProxy = null;
			}
			IsConnected = false;
			StatusMessage = "Offline";
			Plugin.Log.LogWarning((object)"[AP] Offline mode active.");
		}

		private void OnSocketClosed(string reason)
		{
			IsConnected = false;
			StatusMessage = "Connection Lost!";
			Plugin.Log.LogWarning((object)$"[AP] Connection lost: {reason}. Attempting reconnect...");
			Plugin.ExecuteOnMainThread(delegate
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(AutoReconnectCoroutine());
			});
		}

		private IEnumerator AutoReconnectCoroutine()
		{
			yield return (object)new WaitForSeconds(3f);
			if (!IsConnected)
			{
				Connect(_lastHost, _lastPort, _lastSlot, _lastPassword, isReconnecting: true);
			}
		}

		public void CompleteGoal()
		{
			lock (_lockObject)
			{
				_pendingGoalCompletion = true;
				DataManager.IsGoalCompleted = true;
				DataManager.SaveData();
				if (IsConnected && Session != null)
				{
					Plugin.Log.LogInfo((object)"[AP] Goal completed!");
					Session.SetGoalAchieved();
				}
				else
				{
					Plugin.Log.LogWarning((object)"[AP] Goal completed offline. Saved for next connection.");
				}
			}
		}

		public HashSet<long> GetCheckedLocation()
		{
			return _checkedLocations;
		}

		public void SendLocationCheck(long locationId)
		{
			lock (_lockObject)
			{
				if (_checkedLocations.Add(locationId))
				{
					if (IsConnected && Session != null)
					{
						Plugin.Log.LogInfo((object)$"[AP] Sending check: {locationId}");
						Session.Locations.CompleteLocationChecks(new long[1] { locationId });
					}
					else
					{
						_offlineCheckQueue.Enqueue(locationId);
						_storageService.SavePendingChecks(_offlineCheckQueue);
					}
				}
			}
		}

		private void FlushOfflineQueue()
		{
			lock (_lockObject)
			{
				if (IsConnected && Session != null)
				{
					if (_offlineCheckQueue.Count > 0)
					{
						long[] array = _offlineCheckQueue.ToArray();
						_offlineCheckQueue.Clear();
						Session.Locations.CompleteLocationChecks(array);
						_storageService.SavePendingChecks(_offlineCheckQueue);
					}
					if (_pendingGoalCompletion)
					{
						Session.SetGoalAchieved();
					}
				}
			}
		}

		private void OnItemReceived(IReceivedItemsHelper helper)
		{
			lock (_lockObject)
			{
				ItemHandler.CachedItems.Clear();
				ItemHandler.CachedItems.AddRange(helper.AllItemsReceived);
				while (_itemsReceivedIndex < helper.AllItemsReceived.Count)
				{
					ItemInfo val = helper.AllItemsReceived[_itemsReceivedIndex];
					_itemsToProcess.Enqueue(val.ItemId);
					_itemsReceivedIndex++;
					_storageService.SaveItemIndex(_itemsReceivedIndex);
				}
			}
		}

		public void ProcessIncomingItems()
		{
			lock (_lockObject)
			{
				while (_itemsToProcess.Count > 0)
				{
					long apItemId = _itemsToProcess.Dequeue();
					ItemHandler.GrantPlayerAPItem(apItemId);
				}
			}
		}

		private static void ParseSlotData(Dictionary<string, object> slotData)
		{
			if (slotData == null)
			{
				return;
			}
			try
			{
				if (slotData.TryGetValue("starting_arcana_mode", out var value))
				{
					StartingArcanaMode = Convert.ToInt32(value);
				}
				if (slotData.TryGetValue("element_licenses_mode", out var value2))
				{
					ElementLicensesMode = Convert.ToInt32(value2);
				}
				if (slotData.TryGetValue("chaos_fragments_required", out var value3))
				{
					ChaosFragmentsRequired = Convert.ToInt32(value3);
				}
				if (slotData.TryGetValue("starting_element", out var value4) && value4 != null)
				{
					StartingElement = value4.ToString();
				}
				Plugin.Log.LogInfo((object)$"[AP] SlotData loaded: ArcanaMode={StartingArcanaMode}, LicensesMode={ElementLicensesMode}, ChaosRequired={ChaosFragmentsRequired}, StartingElement={StartingElement}");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[AP] Error parsing SlotData: {arg}");
			}
		}
	}
	public class APShopSlot : MonoBehaviour
	{
		public string LocationName { get; private set; }

		public long LocationId { get; private set; }

		public void Initialize(string locationName)
		{
			LocationName = locationName;
			LocationId = APItemLocationDatabase.GetLocationId(locationName);
		}
	}
	public static class APSpriteManager
	{
		private const string ApSpriteResourcePath = "WoLArchipelago.UI.Sprites.AP.png";

		private const float PixelsPerUnit = 230f;

		private static readonly Vector2 DefaultPivot = new Vector2(0.5f, 0.5f);

		private static Sprite _apSprite;

		public static Sprite APSprite
		{
			get
			{
				if ((Object)(object)_apSprite == (Object)null)
				{
					_apSprite = LoadSpriteFromResource("WoLArchipelago.UI.Sprites.AP.png");
				}
				return _apSprite;
			}
		}

		private static Sprite LoadSpriteFromResource(string resourceName)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			using Stream stream = executingAssembly.GetManifestResourceStream(resourceName);
			if (stream == null)
			{
				Plugin.Log.LogError((object)$"[AP] Embedded resource not found: {resourceName}");
				return null;
			}
			byte[] array = new byte[stream.Length];
			if (stream.Read(array, 0, array.Length) == 0)
			{
				Plugin.Log.LogError((object)$"[AP] Failed to read resource stream: {resourceName}");
				return null;
			}
			Texture2D val = new Texture2D(2, 2);
			if (val.LoadImage(array))
			{
				((Texture)val).filterMode = (FilterMode)0;
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), DefaultPivot, 230f);
			}
			Plugin.Log.LogError((object)$"[AP] Failed to decode image data into Texture2D for resource: {resourceName}");
			return null;
		}
	}
	[BepInPlugin("com.skelrin.wolarchipelago", "WoL Archipelago", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.skelrin.wolarchipelago";

		public const string PluginName = "WoL Archipelago";

		public const string PluginVersion = "1.0.0";

		private static readonly Queue<Action> _mainThreadActions = new Queue<Action>();

		private static readonly object _queueLock = new object();

		private Harmony _harmony;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		public static APManager AP { get; private set; }

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			StorageService.InitProfile();
			AP = new APManager();
			((Component)this).gameObject.AddComponent<ArchipelagoUI>();
		}

		private void OnEnable()
		{
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		private void OnDisable()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}

		private void Start()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			try
			{
				_harmony = new Harmony("com.skelrin.wolarchipelago");
				_harmony.PatchAll();
				Log.LogInfo((object)string.Format("{0} v{1} initialized successfully!", "WoL Archipelago", "1.0.0"));
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to apply Harmony patches: {arg}");
			}
		}

		private void Update()
		{
			lock (_queueLock)
			{
				while (_mainThreadActions.Count > 0)
				{
					try
					{
						_mainThreadActions.Dequeue()?.Invoke();
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"Main thread execution error: {arg}");
					}
				}
			}
			AP?.ProcessIncomingItems();
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			ShopService.ClearSceneAssignments();
		}

		public static void ExecuteOnMainThread(Action action)
		{
			if (action == null)
			{
				return;
			}
			lock (_queueLock)
			{
				_mainThreadActions.Enqueue(action);
			}
		}

		public static void StartAPRun()
		{
			ExecuteOnMainThread(delegate
			{
				GameController.LoadLevel("PlayerRoom");
				ArchipelagoUI.Instance?.Hide();
			});
		}
	}
	public class ArchipelagoUI : MonoBehaviour
	{
		private bool _showUI;

		private Rect _windowRect = new Rect(40f, 40f, 350f, 300f);

		private string _uiHost = "archipelago.gg";

		private string _uiPort = "";

		private string _uiSlot = "";

		private string _uiPassword = "";

		private bool _hasConnectedOnce;

		public static ArchipelagoUI Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		public void Update()
		{
			if (Input.GetKeyDown((KeyCode)282))
			{
				Instance?.Toggle();
			}
		}

		public void ToggleHasConnectedOnce()
		{
			_hasConnectedOnce = !_hasConnectedOnce;
		}

		public void Toggle()
		{
			_showUI = !_showUI;
		}

		public void Hide()
		{
			_showUI = false;
		}

		private void OnGUI()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_006e: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			if (_showUI)
			{
				GUI.skin = null;
				_windowRect = GUI.Window(999, _windowRect, new WindowFunction(DrawArchipelagoWindow), "Archipelago Connection");
			}
			if (Plugin.AP != null && !Plugin.AP.IsConnected && _hasConnectedOnce)
			{
				GUIStyle val = new GUIStyle(GUI.skin.box)
				{
					alignment = (TextAnchor)4,
					fontSize = 12,
					fontStyle = (FontStyle)0
				};
				val.normal.textColor = new Color(1f, 0.4f, 0.4f);
				float num = 220f;
				float num2 = 22f;
				float num3 = ((float)Screen.width - num) / 2f;
				float num4 = 10f;
				GUI.Box(new Rect(num3, num4, num, num2), "Archipelago : Connection Lost", val);
			}
		}

		private void DrawArchipelagoWindow(int windowID)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			GUI.Box(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height), "", GUI.skin.box);
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Space(10f);
			GUILayout.Label("Host:", (GUILayoutOption[])(object)new GUILayoutOption[0]);
			_uiHost = GUILayout.TextField(_uiHost, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label("Port:", (GUILayoutOption[])(object)new GUILayoutOption[0]);
			_uiPort = GUILayout.TextField(_uiPort, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label("Slot Name:", (GUILayoutOption[])(object)new GUILayoutOption[0]);
			_uiSlot = GUILayout.TextField(_uiSlot, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label("Password:", (GUILayoutOption[])(object)new GUILayoutOption[0]);
			_uiPassword = GUILayout.PasswordField(_uiPassword, '*', (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Space(10f);
			if (Plugin.AP != null && !Plugin.AP.IsConnected)
			{
				string text = (_hasConnectedOnce ? "Reconnect" : "Connect");
				if (GUILayout.Button(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
				{
					if (int.TryParse(_uiPort, out var result))
					{
						Plugin.AP.Connect(_uiHost, result, _uiSlot, _uiPassword, _hasConnectedOnce);
					}
					else
					{
						Plugin.Log.LogError((object)"[AP] Invalid Port entered in UI.");
					}
				}
			}
			else
			{
				_hasConnectedOnce = true;
				if (GUILayout.Button("Disconnect", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }) && Plugin.AP != null)
				{
					Plugin.AP.Disconnect();
				}
			}
			GUILayout.Space(10f);
			string arg = ((Plugin.AP != null) ? Plugin.AP.StatusMessage : "Disconnected");
			GUILayout.Label($"Status: {arg}", (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.EndVertical();
			GUI.DragWindow();
		}
	}
	public class TlsProxyServer
	{
		private readonly string _targetHost;

		private readonly int _targetPort;

		private TcpListener _listener;

		private bool _isRunning;

		public int LocalPort { get; private set; }

		public TlsProxyServer(string targetHost, int targetPort)
		{
			_targetHost = targetHost;
			_targetPort = targetPort;
		}

		public void Start()
		{
			if (_isRunning)
			{
				return;
			}
			_listener = new TcpListener(IPAddress.Loopback, 0);
			_listener.Start();
			LocalPort = ((IPEndPoint)_listener.LocalEndpoint).Port;
			_isRunning = true;
			ThreadPool.QueueUserWorkItem(delegate
			{
				while (_isRunning)
				{
					try
					{
						TcpClient clientSocket = _listener.AcceptTcpClient();
						ThreadPool.QueueUserWorkItem(delegate
						{
							HandleClient(clientSocket);
						});
					}
					catch
					{
						break;
					}
				}
			});
		}

		public void Stop()
		{
			_isRunning = false;
			if (_listener != null)
			{
				try
				{
					_listener.Stop();
				}
				catch
				{
				}
				_listener = null;
			}
		}

		private void HandleClient(TcpClient clientSocket)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			try
			{
				using TcpClient tcpClient = new TcpClient(_targetHost, _targetPort);
				TlsClientProtocol val = new TlsClientProtocol((Stream)tcpClient.GetStream(), new SecureRandom());
				val.Connect((TlsClient)(object)new ArchipelagoTlsClient());
				Stream clientStream = clientSocket.GetStream();
				Stream serverStream = ((TlsProtocol)val).Stream;
				ThreadPool.QueueUserWorkItem(delegate
				{
					CopyStream(clientStream, serverStream);
				});
				CopyStream(serverStream, clientStream);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[TLS Proxy] Client connection error: {ex.Message}");
			}
			finally
			{
				try
				{
					clientSocket.Close();
				}
				catch
				{
				}
			}
		}

		private static void CopyStream(Stream input, Stream output)
		{
			byte[] array = new byte[8192];
			try
			{
				int count;
				while ((count = input.Read(array, 0, array.Length)) > 0)
				{
					output.Write(array, 0, count);
					output.Flush();
				}
			}
			catch
			{
			}
		}
	}
	public class ArchipelagoTlsClient : DefaultTlsClient
	{
		public override TlsAuthentication GetAuthentication()
		{
			return (TlsAuthentication)(object)new AlwaysValidTlsAuthentication();
		}
	}
	public class AlwaysValidTlsAuthentication : TlsAuthentication
	{
		public void NotifyServerCertificate(Certificate serverCertificate)
		{
		}

		public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
		{
			return null;
		}
	}
}
namespace WoLArchipelago.Services
{
	public class BiomesHandler
	{
		private static readonly Dictionary<string, string> BiomeToItemNameMap = new Dictionary<string, string>
		{
			{ "Fire", "Fire Biome Key" },
			{ "Ice", "Water Biome Key" },
			{ "Earth", "Earth Biome Key" },
			{ "Air", "Air Biome Key" },
			{ "Lightning", "Lightning Biome Key" }
		};

		public static readonly Dictionary<string, long> BiomeKeyMap = BiomeToItemNameMap.ToDictionary((KeyValuePair<string, string> kvp) => kvp.Key, (KeyValuePair<string, string> kvp) => APItemLocationDatabase.GetItemId(kvp.Value));

		public static readonly List<string> AllBiomes;

		private static int _lastOrganizedTier;

		public static List<string> GetUnlockedBiomes()
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, long> item in BiomeKeyMap)
			{
				if (ItemHandler.IsItemUnlocked(item.Value))
				{
					list.Add(item.Key);
				}
			}
			return list;
		}

		public static void ResetRun()
		{
			_lastOrganizedTier = -1;
		}

		public static void OrganizeLevelList()
		{
			int tierCount = GameController.tierCount;
			if (tierCount < 0 || tierCount >= GameController.maxBaseTierCount || _lastOrganizedTier != -1)
			{
				return;
			}
			List<string> list = GetUnlockedBiomes();
			if (list.Count == 0)
			{
				List<string> allBiomes = AllBiomes;
				List<string> list2 = new List<string>(allBiomes.Count);
				foreach (string item in allBiomes)
				{
					list2.Add(item);
				}
				list = list2;
			}
			List<string> levelNameList = GenerateRunBiomes(list);
			GameController.levelNameList = levelNameList;
			_lastOrganizedTier = tierCount;
			if ((Object)(object)GameController.loadingScreen != (Object)null && (Object)(object)GameController.loadingScreen.gameProgressBoard != (Object)null)
			{
				GameController.loadingScreen.gameProgressBoard.InitializeTierOrders();
			}
		}

		private static List<string> GenerateRunBiomes(List<string> unlockedBiomes)
		{
			List<string> list = new List<string>();
			for (int i = 0; i < GameController.maxBaseTierCount; i++)
			{
				List<string> candidates = GetCandidates(unlockedBiomes, list);
				ShuffleList(candidates);
				list.Add(candidates[0]);
			}
			foreach (string allBiome in AllBiomes)
			{
				if (!list.Contains(allBiome))
				{
					list.Add(allBiome);
				}
			}
			return list;
		}

		private static List<string> GetCandidates(List<string> unlockedBiomes, List<string> alreadySelected)
		{
			List<string> list = new List<string>();
			for (int i = 0; i < unlockedBiomes.Count; i++)
			{
				string item = unlockedBiomes[i];
				if (!alreadySelected.Contains(item))
				{
					list.Add(item);
				}
			}
			if (list.Count > 0)
			{
				return list;
			}
			List<string> list2 = new List<string>(unlockedBiomes.Count);
			foreach (string unlockedBiome in unlockedBiomes)
			{
				list2.Add(unlockedBiome);
			}
			return list2;
		}

		private static void ShuffleList(List<string> listToShuffle)
		{
			for (int num = listToShuffle.Count - 1; num > 0; num--)
			{
				int index = Random.Range(0, num + 1);
				string value = listToShuffle[index];
				listToShuffle[index] = listToShuffle[num];
				listToShuffle[num] = value;
			}
		}

		static BiomesHandler()
		{
			Dictionary<string, long>.KeyCollection keys = BiomeKeyMap.Keys;
			List<string> list = new List<string>(keys.Count);
			foreach (string item in keys)
			{
				list.Add(item);
			}
			AllBiomes = list;
			_lastOrganizedTier = -1;
		}
	}
	public class CheckHandler
	{
		public static void SendNpcCheck(string locNameFormat, int maxSlots)
		{
			HashSet<long> checkedLocation = Plugin.AP.GetCheckedLocation();
			for (int i = 1; i <= maxSlots; i++)
			{
				string text = string.Format(locNameFormat, i);
				long locationId = APItemLocationDatabase.GetLocationId(text);
				if (!checkedLocation.Contains(locationId) && CheckLocation(text))
				{
					SoundManager.PlayAudio("MenuBuy", 1f, false, -1f, -1f);
					break;
				}
			}
		}

		public static bool CheckLocation(string locationName)
		{
			long locationId = APItemLocationDatabase.GetLocationId(locationName);
			if (locationId != -1)
			{
				Plugin.AP.SendLocationCheck(locationId);
				return true;
			}
			return false;
		}
	}
	public static class DataManager
	{
		public static int TotalDashes { get; set; }

		public static int TotalChestsOpened { get; set; }

		public static bool IsStartingInventoryApplied { get; set; }

		public static bool IsGoalCompleted { get; set; }

		public static Dictionary<string, int> ChestCounts { get; private set; }

		public static Dictionary<string, int> EnemyCounts { get; private set; }

		public static List<string> SavedHubRelics { get; set; }

		static DataManager()
		{
			ChestCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			EnemyCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			SavedHubRelics = new List<string>();
		}

		public static void LoadData()
		{
			TotalDashes = 0;
			TotalChestsOpened = 0;
			IsStartingInventoryApplied = false;
			IsGoalCompleted = false;
			ChestCounts.Clear();
			EnemyCounts.Clear();
			SavedHubRelics.Clear();
			string dataFilePath = StorageService.GetDataFilePath();
			if (!File.Exists(dataFilePath))
			{
				SaveData();
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(dataFilePath);
				string[] array2 = array;
				foreach (string text in array2)
				{
					string text2 = text.Trim();
					if (string.IsNullOrEmpty(text2) || text2.StartsWith("#") || text2.StartsWith("//"))
					{
						continue;
					}
					string[] array3 = text2.Split(new char[1] { '=' }, 2);
					if (array3.Length != 2)
					{
						continue;
					}
					string text3 = array3[0].Trim();
					string text4 = array3[1].Trim();
					bool result2;
					if (int.TryParse(text4, out var result))
					{
						if (text3 == "TotalDashes")
						{
							TotalDashes = result;
						}
						else if (text3 == "TotalChestsOpened")
						{
							TotalChestsOpened = result;
						}
						else if (text3.StartsWith("Chest_"))
						{
							ChestCounts[text3.Substring(6)] = result;
						}
						else if (text3.StartsWith("Enemy_"))
						{
							EnemyCounts[text3.Substring(6)] = result;
						}
					}
					else if (bool.TryParse(text4, out result2))
					{
						if (text3 == "IsStartingInventoryApplied")
						{
							IsStartingInventoryApplied = result2;
						}
						else if (text3 == "IsGoalCompleted")
						{
							IsGoalCompleted = result2;
						}
					}
					else
					{
						if (!(text3 == "SavedHubRelics"))
						{
							continue;
						}
						List<string> savedHubRelics;
						if (string.IsNullOrEmpty(text4))
						{
							savedHubRelics = new List<string>();
						}
						else
						{
							string[] array4 = text4.Split(new char[1] { ',' });
							List<string> list = new List<string>(array4.Length);
							string[] array5 = array4;
							foreach (string item in array5)
							{
								list.Add(item);
							}
							savedHubRelics = list;
						}
						SavedHubRelics = savedHubRelics;
					}
				}
				ManualLogSource log = Plugin.Log;
				List<string> savedHubRelics2 = SavedHubRelics;
				int num = 0;
				string[] array6 = new string[savedHubRelics2.Count];
				foreach (string item2 in savedHubRelics2)
				{
					array6[num] = item2;
					num++;
				}
				log.LogInfo((object)string.Format("[DataManager] Loaded SavedHubRelics: {0}", string.Join(",", array6)));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[DataManager] Error loading data: {ex.Message}");
			}
		}

		public static void SaveData()
		{
			try
			{
				List<string> list = new List<string>(5)
				{
					$"TotalDashes={TotalDashes}",
					$"TotalChestsOpened={TotalChestsOpened}",
					$"IsStartingInventoryApplied={IsStartingInventoryApplied}",
					$"IsGoalCompleted={IsGoalCompleted}",
					string.Format("SavedHubRelics={0}", string.Join(",", SavedHubRelics.ToArray()))
				};
				foreach (KeyValuePair<string, int> chestCount in ChestCounts)
				{
					list.Add($"Chest_{chestCount.Key}={chestCount.Value}");
				}
				foreach (KeyValuePair<string, int> enemyCount in EnemyCounts)
				{
					list.Add($"Enemy_{enemyCount.Key}={enemyCount.Value}");
				}
				File.WriteAllLines(StorageService.GetDataFilePath(), list.ToArray());
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[DataManager] Error saving data: {ex.Message}");
			}
		}

		public static int IncrementEnemyCount(string enemyType)
		{
			EnemyCounts.TryGetValue(enemyType, out var value);
			int num = value + 1;
			EnemyCounts[enemyType] = num;
			SaveData();
			return num;
		}

		public static int IncrementChestCount(string chestType)
		{
			TotalChestsOpened++;
			ChestCounts.TryGetValue(chestType, out var value);
			int num = value + 1;
			ChestCounts[chestType] = num;
			SaveData();
			return num;
		}
	}
	public static class ItemHandler
	{
		private static List<string> _cachedCursedItemIDs;

		private static string[] _cachedEnemyNames;

		public static List<ItemInfo> CachedItems { get; set; }

		private static List<string> CursedItemIDs
		{
			get
			{
				if (_cachedCursedItemIDs == null || _cachedCursedItemIDs.Count == 0)
				{
					_cachedCursedItemIDs = new List<string>();
					if (LootManager.completeItemDict != null)
					{
						foreach (KeyValuePair<string, Item> item in LootManager.completeItemDict)
						{
							if (item.Value != null && item.Value.isCursed)
							{
								_cachedCursedItemIDs.Add(item.Key);
							}
						}
					}
				}
				return _cachedCursedItemIDs;
			}
		}

		private static string[] EnemyNames
		{
			get
			{
				if (_cachedEnemyNames == null || _cachedEnemyNames.Length == 0)
				{
					_cachedEnemyNames = Enum.GetNames(typeof(EName));
				}
				return _cachedEnemyNames;
			}
		}

		static ItemHandler()
		{
			CachedItems = new List<ItemInfo>();
		}

		public static Player GetActivePlayer(bool mustBeAlive)
		{
			Player[] activePlayers = GameController.activePlayers;
			if (activePlayers == null)
			{
				return null;
			}
			for (int i = 0; i < activePlayers.Count(); i++)
			{
				Player val = activePlayers[i];
				if (!((Object)(object)val == (Object)null) && (!mustBeAlive || ((Entity)val).fsm == null || !((Entity)val).fsm.currentStateName.Contains("Dead")))
				{
					return val;
				}
			}
			return null;
		}

		public static Player GetActivePlayer()
		{
			return GetActivePlayer(mustBeAlive: false);
		}

		public static bool IsItemUnlocked(long itemId)
		{
			return CachedItems != null && CachedItems.Exists((ItemInfo item) => item.ItemId == itemId);
		}

		public static int GetItemCountByName(string name)
		{
			int num = 0;
			int count = CachedItems.Count;
			for (int i = 0; i < count; i++)
			{
				if (APItemLocationDatabase.GetItemName(CachedItems[i].ItemId) == name)
				{
					num++;
				}
			}
			return num;
		}

		public static int GetTotalBossKeys()
		{
			return GetItemCountByName("Boss Key");
		}

		public static int GetTotalChaosFragment()
		{
			return GetItemCountByName("Chaos Fragment");
		}

		public static int GetTotalShopUpgrade()
		{
			return GetItemCountByName("Shop Upgrade");
		}

		public static bool CanAccessBossStage(int tierCount, int stageCount)
		{
			if (stageCount == 2 && tierCount >= 0 && tierCount <= 2)
			{
				return GetTotalBossKeys() >= tierCount + 1;
			}
			return true;
		}

		public static bool CanAccessFinalBossStage()
		{
			return GetTotalChaosFragment() >= APManager.ChaosFragmentsRequired;
		}

		public static void SyncPlayerMaxHP(Player player, bool restoreAllHP)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			if (!((Object)(object)((Entity)(player?)).health == (Object)null))
			{
				int itemCountByName = GetItemCountByName("Max HP Boost");
				int num = itemCountByName * 50;
				NumVarStatMod val = new NumVarStatMod("AP_MaxHP_Mod", (float)num, 10, (VarStatModType)3, restoreAllHP);
				((VarStat<float>)(object)((Entity)player).health.healthStat).AddMod((VarStatMod<float>)(object)val);
			}
		}

		public static void ProcessHPBoostItem()
		{
			Player activePlayer = GetActivePlayer();
			if ((Object)(object)((Entity)(activePlayer?)).health != (Object)null)
			{
				float modifiedValue = ((VarStat<float>)(object)((Entity)activePlayer).health.healthStat).ModifiedValue;
				SyncPlayerMaxHP(activePlayer, restoreAllHP: false);
				float modifiedValue2 = ((VarStat<float>)(object)((Entity)activePlayer).health.healthStat).ModifiedValue;
				int num = Mathf.RoundToInt(modifiedValue2 - modifiedValue);
				if (num > 0)
				{
					((Entity)activePlayer).health.RestoreHealth(num, true, true, false, true);
				}
				GameUI.BroadcastNoticeMessage("Max HP Boost Received!", 3f);
			}
		}

		public static void TriggerCursedTrap()
		{
			((MonoBehaviour)Plugin.Instance).StartCoroutine(CursedTrapCoroutine());
		}

		private static IEnumerator CursedTrapCoroutine()
		{
			Player player = GetActivePlayer(mustBeAlive: true);
			if ((Object)(object)player == (Object)null || !GameController.inGameScene)
			{
				yield break;
			}
			GameUI.BroadcastNoticeMessage("You've been caught in a trap!", 2f);
			SoundManager.PlayAudio("BuyCursedRelic", 1f, false, -1f, -1f);
			yield return (object)new WaitForSeconds(2f);
			for (int i = 5; i > 0; i--)
			{
				GameUI.BroadcastNoticeMessage($"Trap triggering in {i}...", 3f);
				yield return (object)new WaitForSeconds(1f);
				if ((Object)(object)GetActivePlayer(mustBeAlive: true) == (Object)null || !GameController.inGameScene)
				{
					yield break;
				}
			}
			player = GetActivePlayer(mustBeAlive: true);
			if ((Object)(object)player == (Object)null)
			{
				yield break;
			}
			if (Random.value < 0.5f && !((Entity)player).inventory.IsFull)
			{
				List<string> cursedPool = CursedItemIDs;
				if (cursedPool.Count > 0)
				{
					string randomCursedID = cursedPool[Random.Range(0, cursedPool.Count)];
					((Entity)player).inventory.AddItem(randomCursedID, true, false);
					if ((Object)(object)((Entity)player).health != (Object)null)
					{
						int targetHealth = Mathf.Max(1, ((Entity)player).health.CurrentHealthValue - 100);
						((Entity)player).health.CurrentHealthValue = targetHealth;
					}
					yield break;
				}
			}
			SpawnRandomEnemy(((Component)player).transform.position);
		}

		private static void SpawnRandomEnemy(Vector3 position)
		{
			//IL_002d: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			string[] enemyNames = EnemyNames;
			if (enemyNames.Length != 0)
			{
				int num = Random.Range(3, 9);
				for (int i = 0; i < num; i++)
				{
					string text = enemyNames[Random.Range(0, enemyNames.Length)];
					Vector2 val = Random.insideUnitCircle * 1.5f;
					Vector3 val2 = position + new Vector3(val.x, 0f, val.y);
					DebugMenu.SpawnEnemy(text, val2);
				}
			}
		}

		private static void GenerateRandomRelic(int relicTier)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			if (!LootManager.itemTierDict.TryGetValue(relicTier - 1, out var value) || value == null)
			{
				Plugin.Log.LogError((object)$"[AP] No relics found for tier {relicTier}.");
				return;
			}
			List<string> list = new List<string>();
			for (int i = 0; i < value.Count; i++)
			{
				string text = value[i];
				if (!Item.IsUnlocked(text, false))
				{
					list.Add(text);
				}
			}
			if (list.Count == 0)
			{
				GameUI.BroadcastNoticeMessage($"All relics in tier {relicTier} are already unlocked!", 3f);
				return;
			}
			string text2 = list[Random.Range(0, list.Count)];
			Item.IsUnlocked(text2, true);
			GameUI.BroadcastNoticeMessage($"Unlocked {TextManager.GetItemName(text2)} relic", 3f);
			Player activePlayer = GetActivePlayer(mustBeAlive: true);
			if ((Object)(object)activePlayer != (Object)null && GameController.inGameScene)
			{
				LootManager.DropItem(((Component)activePlayer).transform.position, 1, text2, false, 0f);
				SoundManager.PlayAudio("DropItem", 1f, false, -1f, -1f);
			}
		}

		private static bool IsSkillUnlocked(Player player, string skillName)
		{
			if ((Object)(object)player == (Object)null || player.skillStates == null)
			{
				return false;
			}
			for (int i = 0; i < player.skillStates.Length; i++)
			{
				SkillState val = player.skillStates[i];
				if (val != null && ((object)val).GetType().Name.IndexOf(skillName, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return val.isUnlocked;
				}
			}
			return false;
		}

		private static void GenerateRandomSkill(int skillTier)
		{
			//IL_0122: 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)
			Player activePlayer = GetActivePlayer(mustBeAlive: true);
			if ((Object)(object)activePlayer == (Object)null)
			{
				return;
			}
			if (!LootManager.skillTierDict.TryGetValue(skillTier - 1, out var value) || value == null)
			{
				Plugin.Log.LogError((object)$"[AP] No arcana found for tier {skillTier}.");
				return;
			}
			List<string> list = new List<string>();
			for (int i = 0; i < value.Count; i++)
			{
				string text = value[i];
				if (!IsSkillUnlocked(activePlayer, text))
				{
					list.Add(text);
				}
			}
			if (list.Count == 0)
			{
				GameUI.BroadcastNoticeMessage($"All arcanas in tier {skillTier} are already unlocked!", 3f);
				return;
			}
			string text2 = list[Random.Range(0, list.Count)];
			activePlayer.HandleSkillUnlock(text2, true);
			if (GameController.inGameScene)
			{
				ItemSpawner val = GameController.itemSpawner ?? ItemSpawner.Instance;
				if ((Object)(object)val != (Object)null)
				{
					val.SpawnItem((PoolType)3, Vector2.op_Implicit(((Component)activePlayer).transform.position), true, 1.5f, text2, false, false, true, false);
					SoundManager.PlayAudio("DropSpell", 1f, false, -1f, -1f);
				}
			}
		}

		public static void GrantPlayerAPItem(long apItemId)
		{
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			string itemName = APItemLocationDatabase.GetItemName(apItemId);
			Plugin.Log.LogInfo((object)$"[AP] Processing Item: {itemName} (ID: {apItemId})");
			switch (itemName)
			{
			case "Boss Key":
				GameUI.BroadcastNoticeMessage($"[AP] Boss Key Received! ({GetTotalBossKeys()}/3)", 3f);
				return;
			case "Chaos Fragment":
				GameUI.BroadcastNoticeMessage($"[AP] Chaos Fragment Received! ({GetTotalChaosFragment()}/{APManager.ChaosFragmentsRequired})", 3f);
				return;
			case "Shop Upgrade":
				GameUI.BroadcastNoticeMessage($"[AP] Shop Upgrade Received! ({GetTotalShopUpgrade()}/4)", 3f);
				return;
			case "Chaos Gems Pack":
			{
				PlatWallet platWallet = Player.platWallet;
				if (platWallet != null)
				{
					((Wallet)platWallet).Deposit(100);
				}
				GameUI.BroadcastNoticeMessage("[AP] 100 Chaos Gems Received!", 3f);
				return;
			}
			case "Gold Pack":
			{
				GoldWallet goldWallet = Player.goldWallet;
				if (goldWallet != null)
				{
					((Wallet)goldWallet).Deposit(200);
				}
				GameUI.BroadcastNoticeMessage("[AP] 200 Gold Received!", 3f);
				return;
			}
			case "Max HP Boost":
				ProcessHPBoostItem();
				return;
			case "Cursed Trap":
				GameUI.BroadcastNoticeMessage("[AP] Cursed Trap Received!", 3f);
				TriggerCursedTrap();
				return;
			}
			int tier;
			int tier2;
			if (itemName.Contains("Outfit"))
			{
				int num = itemName.IndexOf(':');
				string text = ((num >= 0) ? itemName.Substring(num + 1).Trim() : itemName);
				Outfit.UnlockOutfit(text, true);
				GameUI.BroadcastNoticeMessage($"[AP] Unlocked {TextManager.GetOutfitName(text)} outfit", 3f);
			}
			else if (itemName.Contains("Relic") && TryExtractTier(itemName, out tier))
			{
				GenerateRandomRelic(tier);
			}
			else if (itemName.Contains("Arcana") && !itemName.Contains("Bonus") && TryExtractTier(itemName, out tier2))
			{
				GenerateRandomSkill(tier2);
			}
			else if (itemName.Contains("Doctor") || itemName.Contains("Heal"))
			{
				GameUI.BroadcastNoticeMessage($"[AP] {TextManager.GetItemName(itemName)} Received", 3f);
				Item.IsUnlocked(itemName, true);
				Player activePlayer = GetActivePlayer();
				if ((Object)(object)activePlayer != (Object)null && GameController.inGameScene)
				{
					LootManager.DropItem(((Component)activePlayer).transform.position, 1, itemName, false, 0f);
					SoundManager.PlayAudio("DropItem", 1f, false, -1f, -1f);
				}
			}
			else
			{
				GameUI.BroadcastNoticeMessage($"[AP] {itemName} Received", 3f);
				GameUI.RefreshCDUI();
			}
		}

		private static bool TryExtractTier(string itemName, out int tier)
		{
			int num = itemName.LastIndexOf(' ');
			if (num >= 0 && int.TryParse(itemName.Substring(num + 1), out tier))
			{
				return true;
			}
			tier = 0;
			return false;
		}
	}
	public class ShopService
	{
		private static readonly HashSet<long> assignedLocationsInScene = new HashSet<long>();

		public static void ClearSceneAssignments()
		{
			assignedLocationsInScene.Clear();
		}

		public static int GetMaxShopSlots()
		{
			return Mathf.Min(ItemHandler.GetTotalShopUpgrade() * 16, 64);
		}

		public static bool TryGetNextLocation(string slotNameFormat, int maxSlots, out long locId, out string locName)
		{
			locId = -1L;
			locName = string.Empty;
			for (int i = 1; i <= maxSlots; i++)
			{
				string text = string.Format(slotNameFormat, i);
				long locationId = APItemLocationDatabase.GetLocationId(text);
				if (locationId != -1)
				{
					bool flag = Plugin.AP.GetCheckedLocation().Contains(locationId);
					bool flag2 = assignedLocationsInScene.Contains(locationId);
					if (!flag && !flag2)
					{
						locId = locationId;
						locName = text;
						assignedLocationsInScene.Add(locationId);
						return true;
					}
				}
			}
			return false;
		}

		public static void DestroyShopItem(GameObject gameObject, Component priceMarker)
		{
			if ((Object)(object)priceMarker != (Object)null)
			{
				Object.Destroy((Object)(object)priceMarker.gameObject);
			}
			if ((Object)(object)gameObject != (Object)null)
			{
				Object.Destroy((Object)(object)gameObject);
			}
		}

		public static void SetupAPShopItem(GameObject gameObject, string locationName, long locationId, SpriteRenderer itemSpriteRenderer, Action<string, string> setTextAction)
		{
			gameObject.AddComponent<APShopSlot>().Initialize(locationName);
			if ((Object)(object)itemSpriteRenderer != (Object)null)
			{
				itemSpriteRenderer.sprite = APSpriteManager.APSprite;
			}
			Tuple<string, string> locationInfo = Plugin.AP.GetLocationInfo(locationId);
			setTextAction?.Invoke(locationInfo.First, locationInfo.Second);
		}

		public static bool ProcessPurchase(GameObject gameObject, Component priceMarker, int cost, bool usePlatinumCost, Action onSuccess = null, Action onFailure = null)
		{
			APShopSlot component = gameObject.GetComponent<APShopSlot>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			bool num;
			if (!usePlatinumCost)
			{
				if (Player.goldWallet != null)
				{
					num = ((Wallet)Player.goldWallet).balance >= cost;
					goto IL_0053;
				}
			}
			else if (Player.platWallet != null)
			{
				num = ((Wallet)Player.platWallet).balance >= cost;
				goto IL_0053;
			}
			goto IL_00c2;
			IL_0116:
			return false;
			IL_00c2:
			if (onFailure != null)
			{
				onFailure();
			}
			else
			{
				SoundManager.PlayAudio("MenuError", 1f, false, -1f, -1f);
			}
			string text = (usePlatinumCost ? "Not enough chaos gems !" : "Not enough gold !");
			GameUI.BroadcastNoticeMessage(text, 3f);
			goto IL_0116;
			IL_0053:
			if (!num)
			{
				goto IL_00c2;
			}
			if (usePlatinumCost)
			{
				((Wallet)Player.platWallet).Withdraw(cost);
			}
			else
			{
				((Wallet)Player.goldWallet).Withdraw(cost);
			}
			Plugin.AP.SendLocationCheck(component.LocationId);
			SoundManager.PlayAudio("MenuBuy", 1f, false, -1f, -1f);
			onSuccess?.Invoke();
			DestroyShopItem(gameObject, priceMarker);
			goto IL_0116;
		}
	}
	public class SlotManager
	{
		public static readonly int[] SlotItemIds = new int[6] { 0, 0, 871122001, 871122002, 871122003, 871122004 };

		public static readonly Dictionary<ElementType, int> LicenseItemIds = new Dictionary<ElementType, int>
		{
			[(ElementType)0] = 871122010,
			[(ElementType)1] = 871122011,
			[(ElementType)4] = 871122012,
			[(ElementType)2] = 871122013,
			[(ElementType)3] = 871122014
		};

		public static bool IsSlotUnlocked(int slotIndex)
		{
			if (slotIndex < 0 || slotIndex >= 6)
			{
				return true;
			}
			if (slotIndex < 2)
			{
				return true;
			}
			return ItemHandler.IsItemUnlocked(SlotItemIds[slotIndex]);
		}

		public static int GetFirstUnlockedSlot()
		{
			for (int i = 0; i < 6; i++)
			{
				if (IsSlotUnlocked(i))
				{
					return i;
				}
			}
			return 0;
		}

		public static int GetLastUnlockedSlot()
		{
			for (int num = 5; num >= 0; num--)
			{
				if (IsSlotUnlocked(num))
				{
					return num;
				}
			}
			return 0;
		}

		public static bool PlayerHasLicenseToPickupSkill(SkillState skillState)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (APManager.ElementLicensesMode == 0)
			{
				return true;
			}
			return ItemHandler.IsItemUnlocked(LicenseItemIds[skillState.element]);
		}
	}
	public class StartingInventoryHandler
	{
		public static void ApplyArchipelagoStartingSkills()
		{
			if (DataManager.IsStartingInventoryApplied || Plugin.AP == null || !Plugin.AP.IsConnected)
			{
				return;
			}
			Player activePlayer = ItemHandler.GetActivePlayer();
			if (activePlayer?.skillsDict == null || activePlayer.skillsDict.Count == 0)
			{
				return;
			}
			GameData gameData = GameDataManager.gameData;
			LockStartingItems(gameData);
			if (APManager.StartingArcanaMode == 0)
			{
				DataManager.IsStartingInventoryApplied = true;
				DataManager.SaveData();
			}
			else if (APManager.StartingArcanaMode == 1)
			{
				LockStartingSkillsOnDataDictionary(gameData);
				LockStartingSkillsOnPlayer(activePlayer);
				ElementType? targetElement = GetTargetElement();
				List<SkillState> skills = activePlayer.skillsDict.Values.ToList();
				SkillState randomSkill = GetRandomSkill(FilterSkills(skills, (SkillState s) => s.isBasic, targetElement));
				SkillState randomSkill2 = GetRandomSkill(FilterSkills(skills, (SkillState s) => s.isDash, targetElement));
				SkillState randomSkill3 = GetRandomSkill(FilterSkills(skills, (SkillState s) => !s.isBasic && !s.isDash && !s.hasSignatureVariant, targetElement));
				SkillState randomSkill4 = GetRandomSkill(FilterSkills(skills, (SkillState s) => s.hasSignatureVariant, targetElement));
				Plugin.Log.LogInfo((object)("Chosen Skills: " + randomSkill?.skillID + " | " + randomSkill2?.skillID + " | " + randomSkill3?.skillID + " | " + randomSkill4?.skillID));
				if (activePlayer.assignedSkills == null || activePlayer.assignedSkills.Length < 4)
				{
					activePlayer.assignedSkills = (SkillState[])(object)new SkillState[4];
				}
				AssignSkillToSlot(activePlayer, gameData, 0, randomSkill, isSignature: false);
				AssignSkillToSlot(activePlayer, gameData, 1, randomSkill2, isSignature: false);
				AssignSkillToSlot(activePlayer, gameData, 2, randomSkill3, isSignature: false);
				AssignSkillToSlot(activePlayer, gameData, 3, randomSkill4, isSignature: true);
				DataManager.IsStartingInventoryApplied = true;
				DataManager.SaveData();
				GameUI.RefreshCDUI();
			}
		}

		private static ElementType? GetTargetElement()
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (APManager.ElementLicensesMode != 1 || string.IsNullOrEmpty(APManager.StartingElement))
			{
				return null;
			}
			string startingElement = APManager.StartingElement;
			string[] names = Enum.GetNames(typeof(ElementType));
			foreach (string text in names)
			{
				if (string.Equals(text, startingElement, StringComparison.OrdinalIgnoreCase))
				{
					return (ElementType)Enum.Parse(typeof(ElementType), text);
				}
			}
			return null;
		}

		private static List<SkillState> FilterSkills(List<SkillState> skills, Func<SkillState, bool> categoryPredicate, ElementType? targetElement)
		{
			List<SkillState> list = skills.Where(categoryPredicate).ToList();
			if (targetElement.HasValue)
			{
				List<SkillState> list2 = list.Where((SkillState s) => s.element == targetElement.Value).ToList();
				if (list2.Count > 0)
				{
					return list2;
				}
			}
			return list;
		}

		private static SkillState GetRandomSkill(List<SkillState> pool)
		{
			if (pool == null || pool.Count == 0)
			{
				return null;
			}
			return pool[Random.Range(0, pool.Count)];
		}

		private static void AssignSkillToSlot(Player player, GameData gameData, int slotIndex, SkillState skill, bool isSignature)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			if (skill == null)
			{
				return;
			}
			skill.isUnlocked = true;
			if (isSignature)
			{
				skill.signatureUnlocked = true;
				skill.isSignature = true;
			}
			player.assignedSkills[slotIndex] = skill;
			if (player.playerData?.skills != null && player.playerData.skills.Length > slotIndex)
			{
				player.playerData.skills[slotIndex] = skill.skillID;
			}
			if (gameData?.skillDataDictionary != null)
			{
				if (!gameData.skillDataDictionary.TryGetValue(skill.skillID, out var value))
				{
					value = new StoredSkillData();
					gameData.skillDataDictionary[skill.skillID] = value;
				}
				value.isUnlocked = true;
				if (isSignature)
				{
					value.signatureUnlocked = true;
				}
			}
		}

		private static void SetStoredSkillLockState(GameData gameData, string skillId, bool isUnlocked, bool signatureUnlocked)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if (!gameData.skillDataDictionary.TryGetValue(skillId, out var value))
			{
				value = new StoredSkillData();
				gameData.skillDataDictionary[skillId] = value;
			}
			value.isUnlocked = isUnlocked;
			value.signatureUnlocked = signatureUnlocked;
		}

		private static void LockStartingSkillsOnDataDictionary(GameData gameData)
		{
			if (gameData?.skillDataDictionary == null)
			{
				return;
			}
			foreach (string startingSkillID in Globals.startingSkillIDList)
			{
				SetStoredSkillLockState(gameData, startingSkillID, isUnlocked: false, signatureUnlocked: false);
			}
			foreach (string startingOverdriveID in Globals.startingOverdriveIDList)
			{
				SetStoredSkillLockState(gameData, startingOverdriveID, isUnlocked: false, signatureUnlocked: false);
			}
		}

		private static void LockStartingSkillsOnPlayer(Player player)
		{
			if (player?.skillsDict == null)
			{
				return;
			}
			foreach (string startingSkillID in Globals.startingSkillIDList)
			{
				if (player.skillsDict.TryGetValue(startingSkillID, out var value))
				{
					value.isUnlocked = false;
				}
			}
			foreach (string startingOverdriveID in Globals.startingOverdriveIDList)
			{
				if (player.skillsDict.TryGetValue(startingOverdriveID, out var value2))
				{
					value2.isUnlocked = false;
					value2.signatureUnlocked = false;
				}
			}
		}

		private static void LockStartingItems(GameData gameData)
		{
			gameData.UpdateItemDataEntry(BuffWithFriendship.staticID, false);
			gameData.UpdateItemDataEntry(WaterChargeFamiliarItem.staticID, false);
		}
	}
	public class StorageService
	{
		private static readonly string LastProfileFilePath;

		public static string CurrentAPSavePrefix { get; private set; }

		private string PendingChecksFilePath => Path.Combine(GetProfileFolderPath(), "PendingChecks.txt");

		private string ItemIndexFilePath => Path.Combine(GetProfileFolderPath(), "ItemIndex.txt");

		static StorageService()
		{
			LastProfileFilePath = Path.Combine(Paths.ConfigPath, "WoL_AP_LastProfile.txt");
			CurrentAPSavePrefix = "AP_Default";
		}

		public static void InitProfile()
		{
			if (!File.Exists(LastProfileFilePath))
			{
				return;
			}
			try
			{
				string text = File.ReadAllText(LastProfileFilePath).Trim();
				if (!string.IsNullOrEmpty(text))
				{
					CurrentAPSavePrefix = text;
					Plugin.Log.LogInfo((object)$"[AP Save] Loaded active AP Profile: {CurrentAPSavePrefix}");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[AP Save] Error reading last profile: {ex.Message}");
			}
		}

		public void SetAPSaveProfile(string seed, string slotName)
		{
			string arg = string.Join("_", slotName.Split(Path.GetInvalidFileNameChars()));
			CurrentAPSavePrefix = $"AP_{seed}_{arg}";
			try
			{
				File.WriteAllText(LastProfileFilePath, CurrentAPSavePrefix);
				Plugin.Log.LogInfo((object)$"[AP Save] AP Profile set to: {CurrentAPSavePrefix}");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[AP Save] Failed to persist active profile: {ex.Message}");
			}
		}

		public void SavePendingChecks(IEnumerable<long> pendingChecks)
		{
			try
			{
				List<string> list = new List<string>();
				foreach (long pendingCheck in pendingChecks)
				{
					list.Add(pendingCheck.ToString());
				}
				string pendingChecksFilePath = PendingChecksFilePath;
				List<string> list2 = list;
				int num = 0;
				string[] array = new string[list2.Count];
				foreach (string item in list2)
				{
					array[num] = item;
					num++;
				}
				File.WriteAllLines(pendingChecksFilePath, array);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[AP] Error saving pending checks: {ex.Message}");
			}
		}

		public Queue<long> LoadPendingChecks(HashSet<long> checkedLocations)
		{
			Queue<long> queue = new Queue<long>();
			try
			{
				string pendingChecksFilePath = PendingChecksFilePath;
				if (File.Exists(pendingChecksFilePath))
				{
					string[] array = File.ReadAllLines(pendingChecksFilePath);
					string[] array2 = array;
					foreach (string s in array2)
					{
						if (long.TryParse(s, out var result) && !queue.Contains(result))
						{
							queue.Enqueue(result);
							checkedLocations.Add(result);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[AP] Error loading pending checks: {ex.Message}");
			}
			return queue;
		}

		public void SaveItemIndex(int index)
		{
			try
			{
				File.WriteAllText(ItemIndexFilePath, index.ToString());
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[AP] Error saving item index: {ex.Message}");
			}
		}

		public int LoadItemIndex()
		{
			try
			{
				string itemIndexFilePath = ItemIndexFilePath;
				if (File.Exists(itemIndexFilePath) && int.TryParse(File.ReadAllText(itemIndexFilePath), out var result))
				{
					return result;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"[AP] Error loading item index: {ex.Message}");
			}
			return 0;
		}

		public static string GetProfileFolderPath()
		{
			string text = Path.Combine(Application.persistentDataPath, "AP_Saves");
			if (!string.IsNullOrEmpty(CurrentAPSavePrefix))
			{
				text = Path.Combine(text, CurrentAPSavePrefix);
			}
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			return text;
		}

		public static string GetDataFilePath()
		{
			return Path.Combine(GetProfileFolderPath(), "Stats.txt");
		}
	}
}
namespace WoLArchipelago.Patches
{
	[HarmonyPatch(typeof(AchievementManager), "UnlockAchievement")]
	public static class AchievementManagerPatches
	{
		[HarmonyPrefix]
		public static bool UnlockAchievementPrefix()
		{
			return false;
		}
	}
	[HarmonyPatch(typeof(BossRoomEventHandler), "CheckForPerfectFight")]
	public static class BossRoomEventHandlerPatches
	{
		[HarmonyPostfix]
		public static void CheckForPerfectFightPostfix(int[] ___playerStartHealthValues)
		{
			if (___playerStartHealthValues == null || ___playerStartHealthValues.Length == 0)
			{
				return;
			}
			Player[] activePlayers = GameController.activePlayers;
			if (activePlayers == null || activePlayers.Length == 0)
			{
				return;
			}
			bool flag = false;
			bool flag2 = false;
			Player[] array = activePlayers;
			foreach (Player val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Entity)val).health == (Object)null))
				{
					flag = true;
					if (val.playerID < ___playerStartHealthValues.Length && ((Entity)val).health.CurrentHealthValue < ___playerStartHealthValues[val.playerID])
					{
						flag2 = true;
						break;
					}
				}
			}
			if (flag && !flag2)
			{
				CheckHandler.CheckLocation("Perfect a boss");
			}
		}
	}
	[HarmonyPatch(typeof(Gold), "OnTriggerStay2D")]
	public static class GoldPatches
	{
		[HarmonyPostfix]
		public static void OnTriggerStay2DPostfix()
		{
			if (Player.goldWallet != null && ((Wallet)Player.goldWallet).balance >= 1000)
			{
				CheckHandler.CheckLocation("Have 1000 gold");
			}
		}
	}
	[HarmonyPatch(typeof(Platinum), "OnTriggerStay2D")]
	public static class PlatinumPatches
	{
		[HarmonyPostfix]
		public static void OnTriggerStay2DPostfix()
		{
			if (Player.platWallet != null && ((Wallet)Player.platWallet).balance >= 500)
			{
				CheckHandler.CheckLocation("Have 500 chaos gems");
			}
		}
	}
	[HarmonyPatch(typeof(GameDataManager))]
	public static class APSavePatches
	{
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static bool GetSavePathStrPrefix(ref string __result)
		{
			return OverrideSavePath(ref __result);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		public static bool GetBasePathStrPrefix(ref string __result)
		{
			return OverrideSavePath(ref __result);
		}

		private static bool OverrideSavePath(ref string result)
		{
			if (!string.IsNullOrEmpty(APManager.CurrentAPSavePrefix))
			{
				string text = StorageService.GetProfileFolderPath();
				if (!text.EndsWith("/") && !text.EndsWith("\\"))
				{
					text += "/";
				}
				result = text;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Application), "Quit")]
	public class SaveOnQuitPatch
	{
		[HarmonyPrefix]
		public static void Prefix()
		{
			DataManager.SaveData();
		}
	}
	[HarmonyPatch(typeof(BankerNpc))]
	public static class BankerNpcPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("TradeGold")]
		public static void TradeGoldPostfix(BankerNpc __instance)
		{
			bool value = Traverse.Create((object)__instance).Field<bool>("goldTraded").Value;
			string locNameFormat = "Doki the Banker Slot {0}";
			if (value)
			{
				CheckHandler.SendNpcCheck(locNameFormat, 10);
			}
		}
	}
	public static class BossRushPatches
	{
		[HarmonyPatch(typeof(BossRushNpc), "Start")]
		public static class BossRushNpcStartPatch
		{
			private static readonly FieldRef<RunModifier, Dictionary<string, RunMod>> RunModsRef = AccessTools.FieldRefAccess<RunModifier, Dictionary<string, RunMod>>("runMods");

			[HarmonyPostfix]
			public static void Postfix(BossRushNpc __instance)
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return;
				}
				GameObject bossRushTP = __instance.bossRushTP;
				if (bossRushTP != null)
				{
					bossRushTP.SetActive(false);
				}
				GameObject bossRushTPDisabled = __instance.bossRushTPDisabled;
				if (bossRushTPDisabled != null)
				{
					bossRushTPDisabled.SetActive(true);
				}
				if (!((Object)(object)RunModifier.Instance != (Object)null))
				{
					return;
				}
				Dictionary<string, RunMod> dictionary = RunModsRef.Invoke(RunModifier.Instance);
				if (dictionary != null && dictionary.ContainsKey("RushRunMod"))
				{
					RunMod val = dictionary["RushRunMod"];
					if (val != null)
					{
						val.Deactivate();
					}
					dictionary.Remove("RushRunMod");
				}
			}
		}

		[HarmonyPatch(typeof(BossRushNpc), "HandleConditionalInteraction")]
		public static class BossRushNpcInteractionPatch
		{
			[HarmonyPrefix]
			public static bool Prefix(BossRushNpc __instance, ref bool __result)
			{
				GameUI.BroadcastNoticeMessage("Boss Rush deactivated in Archipelago mode !", 3f);
				SoundManager.PlayAudio("MenuError", 1f, false, -1f, -1f);
				((Npc)__instance).playerLeavingDialog = true;
				__result = true;
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(CollectorNpc))]
	public class CollectorNpcPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("OnRelicCollect")]
		public static void OnRelicCollectPostfix()
		{
			string locNameFormat = "Cremire the Collector Slot {0}";
			CheckHandler.SendNpcCheck(locNameFormat, 10);
		}
	}
	[HarmonyPatch(typeof(DoctorNpc))]
	public class DoctorNpcPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("WritePrescription")]
		public static void WritePrescriptionPostfix()
		{
			string locNameFormat = "Doctor Song Slot {0}";
			CheckHandler.SendNpcCheck(locNameFormat, 10);
		}
	}
	[HarmonyPatch(typeof(Npc), "Interacted")]
	public static class NpcInteractedPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(Npc __instance)
		{
			if (__instance is EpicBeardWizardNpc)
			{
				return false;
			}
			return true;
		}
	}
	public static class GameStatsPatches
	{
		[HarmonyPatch(typeof(GameStats), "EnemyDefeated")]
		public class GameStatsEnemyDefeatedPatch
		{
			private static readonly Dictionary<string, string> ElementMap = new Dictionary<string, string>
			{
				{ "Frost", "Water" },
				{ "Earth", "Earth" },
				{ "Flame", "Fire" },
				{ "Wind", "Air" },
				{ "Thunder", "Lightning" }
			};

			[HarmonyPostfix]
			public static void Postfix(GameStats __instance, string name)
			{
				foreach (KeyValuePair<string, string> item in ElementMap)
				{
					if (name.Contains(item.Key))
					{
						int num = DataManager.IncrementEnemyCount(item.Value);
						CheckHandler.CheckLocation($"{item.Value} Enemies Defeated {num} times");
						break;
					}
				}
				int enemyDefeatedCount = __instance.GetEnemyDefeatedCount(name);
				string text = ((enemyDefeatedCount == 1) ? (name + " Defeated") : $"{name} Defeated {enemyDefeatedCount} times");
				if (CheckHandler.CheckLocation(text) && text == "FinalBoss Defeated")
				{
					Plugin.AP.CompleteGoal();
				}
			}
		}

		[HarmonyPatch(typeof(GameStats), "UpdateIntStatValue")]
		public class GameStatsUpdateIntStatValuePatch
		{
			[HarmonyPostfix]
			public static void Postfix(GameStats __instance, Stat givenStat)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Invalid comparison between Unknown and I4
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Invalid comparison between Unknown and I4
				int intStatValue = __instance.GetIntStatValue(givenStat);
				if (1 == 0)
				{
				}
				string text = (((int)givenStat == 0) ? $"Die {intStatValue} times" : (((int)givenStat == 3) ? $"Fall {intStatValue} times" : (((int)givenStat != 8) ? null : $"Break {intStatValue} Paintings")));
				if (1 == 0)
				{
				}
				string text2 = text;
				if (text2 != null)
				{
					CheckHandler.CheckLocation(text2);
				}
			}
		}

		[HarmonyPatch(typeof(SkillState), "BaseOnEnter")]
		public class DashTrackerPatch
		{
			[HarmonyPostfix]
			public static void Postfix(SkillState __instance)
			{
				if (__instance != null && __instance.isDash && DataManager.TotalDashes < 500)
				{
					DataManager.TotalDashes++;
					int totalDashes = DataManager.TotalDashes;
					if ((totalDashes == 100 || totalDashes == 500) ? true : false)
					{
						CheckHandler.CheckLocation($"Dash {DataManager.TotalDashes} times");
					}
				}
			}
		}
	}
	public static class InventoryPatches
	{
		[HarmonyPatch(typeof(Inventory), "DropItem")]
		public class InventoryDropItemPatch
		{
			[HarmonyPrefix]
			public static bool Prefix(Inventory __instance, string givenItemID, ref bool __result)
			{
				string currentLevelName = GameController.currentLevelName;
				if (currentLevelName == "PlayerRoom" || currentLevelName == "Hub")
				{
					if (string.IsNullOrEmpty(givenItemID))
					{
						givenItemID = __instance.GetItemIDList().FirstOrDefault();
					}
					if (!string.IsNullOrEmpty(givenItemID) && __instance.RemoveItem(givenItemID, true, false))
					{
						Entity value = Traverse.Create((object)__instance).Field("parentEntity").GetValue<Entity>();
						Player val = (Player)(object)((value is Player) ? value : null);
						if ((Object)(object)val != (Object)null && val.designatedItemID == givenItemID)
						{
							string text = __instance.GetItemIDList().FirstOrDefault((string id) => id != givenItemID);
							val.designatedItemID = text ?? string.Empty;
						}
						DataManager.SavedHubRelics.Remove(givenItemID);
						DataManager.SaveData();
						__result = true;
					}
					else
					{
						__result = false;
					}
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Player), "GiveDesignatedItem")]
		public class PlayerGiveDesignatedItemPatch
		{
			[HarmonyPostfix]
			public static void Postfix(Player __instance)
			{
				string currentLevelName = GameController.currentLevelName;
				if (!(currentLevelName == "PlayerRoom") && !(currentLevelName == "Hub"))
				{
					return;
				}
				foreach (string savedHubRelic in DataManager.SavedHubRelics)
				{
					if (savedHubRelic != __instance.designatedItemID && !((Entity)__instance).inventory.ContainsItem(savedHubRelic))
					{
						((Entity)__instance).inventory.AddItem(savedHubRelic, false, true);
					}
				}
			}
		}
	}
	public static class ItemDropPatches
	{
		[HarmonyPatch(typeof(ItemDrop), "OnInteract")]
		public static class ItemDropOnInteractPatch
		{
			[HarmonyPrefix]
			public static bool Prefix(ItemDrop __instance, Player player)
			{
				return TryMakeRoomForPickup(player, __instance, playAudioOnError: true);
			}
		}

		[HarmonyPatch(typeof(ItemDrop), "OnTriggerStay2D")]
		public static class ItemDropOnTriggerStay2DPatch
		{
			[HarmonyPrefix]
			public static bool Prefix(ItemDrop __instance, Collider2D col, bool ___noPickUpMode)
			{
				if ((Object)(object)col == (Object)null || ((Component)col).gameObject.tag != "Player" || ___noPickUpMode)
				{
					return true;
				}
				Player val = (((Object)(object)((Component)col).transform.parent != (Object)null) ? ((Component)((Component)col).transform.parent).GetComponent<Player>() : null);
				if ((Object)(object)val == (Object)null || ((PickupItem)__instance).requireInteract)
				{
					return true;
				}
				return TryMakeRoomForPickup(val, __instance, playAudioOnError: false);
			}
		}

		private static bool TryMakeRoomForPickup(Player player, ItemDrop itemDrop, bool playAudioOnError)
		{
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)itemDrop == (Object)null || ((Entity)player).inventory.ContainsItem(itemDrop.itemID))
			{
				return true;
			}
			int num = 1 + ItemHandler.GetItemCountByName("Relic Slot Upgrade");
			if (((Entity)player).inventory.Count < num)
			{
				return true;
			}
			string text = null;
			foreach (KeyValuePair<string, Item> item in ((Entity)player).inventory.itemDict)
			{
				if (item.Value != null && !item.Value.isCursed)
				{
					text = item.Key;
					break;
				}
			}
			if (text != null)
			{
				if (((Entity)player).inventory.RemoveItem(text, false, false))
				{
					LootManager.DropItem(((Component)player).transform.position, 1, text, false, 0f);
				}
				return true;
			}
			if (playAudioOnError)
			{
				SoundManager.PlayErrorAudio();
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ItemStoreItem))]
	public class ItemStoreItemPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		public static void StartPostfix(ItemStoreItem __instance)
		{
			string slotNameFormat;
			int maxSlots;
			if (__instance.cursedOnly)
			{
				slotNameFormat = "Nox the Unfortunate Slot {0}";
				maxSlots = 40;
			}
			else
			{
				slotNameFormat = "Relic Shop Slot {0}";
				maxSlots = ShopService.GetMaxShopSlots();
			}
			if (!ShopService.TryGetNextLocation(slotNameFormat, maxSlots, out var locId, out var locName))
			{
				ShopService.DestroyShopItem(((Component)__instance).gameObject, (Component)(object)((StoreItem)__instance).priceMarker);
				return;
			}
			if (((StoreItem)__instance).usePlatinumCost)
			{
				((VarStat<float>)(object)((StoreItem)__instance).costStat)?.Initialize(20f, true);
			}
			PriceMarker priceMarker = ((StoreItem)__instance).priceMarker;
			if (priceMarker != null)
			{
				priceMarker.SetText(((StoreItem)__instance).Cost.ToString());
			}
			ShopService.SetupAPShopItem(((Component)__instance).gameObject, locName, locId, ((StoreItem)__instance).itemSpriteRenderer, delegate(string title, string desc)
			{
				if ((Object)(object)((StoreItem)__instance).itemText != (Object)null)
				{
					((StoreItem)__instance).itemText.text = title;
				}
				if ((Object)(object)((StoreItem)__instance).descText != (Object)null)
				{
					((StoreItem)__instance).descText.text = desc;
				}
			});
		}

		[HarmonyPrefix]
		[HarmonyPatch("Buy")]
		public static bool BuyPrefix(ItemStoreItem __instance, Player player)
		{
			return ShopService.ProcessPurchase(((Component)__instance).gameObject, (Component)(object)((StoreItem)__instance).priceMarker, ((StoreItem)__instance).Cost, ((StoreItem)__instance).usePlatinumCost, (Action)delegate
			{
				Npc parentNpc = ((StoreItem)__instance).parentNpc;
				if (parentNpc != null)
				{
					parentNpc.PlayDefaultEmote();
				}
				if (__instance.cursedOnly)
				{
					Player val = player ?? ItemHandler.GetActivePlayer();
					if ((Object)(object)((Entity)(val?)).health != (Object)null)
					{
						((Entity)val).health.CurrentHealthValue = Mathf.Max(1, ((Entity)val).health.CurrentHealthValue - 50);
					}
				}
			}, (Action)__instance.PlayDenyBuyEffects);
		}
	}
	public static class LevelPatches
	{
		[HarmonyPatch(typeof(NextLevelLoader), "OnTriggerStay2D")]
		public static class NextLevelLoaderPatches
		{
			private const float MessageCooldown = 1f;

			private static float lastMessageTime;

			private static readonly FieldRef<NextLevelLoader, Player> playerRef = AccessTools.FieldRefAccess<NextLevelLoader, Player>("player");

			private static readonly FieldRef<NextLevelLoader, bool> portalEnteredRef = AccessTools.FieldRefAccess<NextLevelLoader, bool>("portalEntered");

			private static readonly string[] CouncilLevels = new string[3] { "First", "Second", "Third" };

			[HarmonyPrefix]
			public static bool OnTriggerStay2DPrefix(NextLevelLoader __instance, Collider2D col)
			{
				int tierCount = GameController.tierCount;
				int stageCount = GameController.stageCount;
				if (IsStageLocked(tierCount, stageCount))
				{
					Player val = Player.CheckForPlayer(col);
					if ((Object)(object)val != (Object)null && val.IsAvailable && val.inputDevice.GetButtonDown("Interact"))
					{
						if (Time.time - lastMessageTime > 1f)
						{
							DisplayRequirements(tierCount, stageCount);
							lastMessageTime = Time.time;
						}
						playerRef.Invoke(__instance) = null;
						portalEnteredRef.Invoke(__instance) = false;
					}
					return false;
				}
				ProcessLocationCheck(tierCount, stageCount);
				return true;
			}

			private static bool IsStageLocked(int tier, int stage)
			{
				if (stage == 2)
				{
					return !ItemHandler.CanAccessBossStage(tier, stage);
				}
				if (tier == 2 && stage == 3)
				{
					return !ItemHandler.CanAccessFinalBossStage();
				}
				return false;
			}

			private static void DisplayRequirements(int tier, int stage)
			{
				SoundManager.PlayAudio("MenuError", 1f, false, -1f, -1f);
				if (stage == 2)
				{
					int num = tier + 1;
					int totalBossKeys = ItemHandler.GetTotalBossKeys();
					GameUI.BroadcastNoticeMessage($"Boss locked! Requires {num} Key(s) (Have: {totalBossKeys}/3)", 3f);
				}
				else
				{
					int chaosFragmentsRequired = APManager.ChaosFragmentsRequired;
					int totalChaosFragment = ItemHandler.GetTotalChaosFragment();
					GameUI.BroadcastNoticeMessage($"Final Boss locked! Requires {chaosFragmentsRequired} Chaos Fragment(s) (Have: {totalChaosFragment}/{chaosFragmentsRequired})", 3f);
				}
			}

			private static void ProcessLocationCheck(int tier, int stage)
			{
				string locationName = ((stage != 3) ? $"Stage {tier + 1}-{stage} Cleared" : (GetCouncilLevelName(tier) + " Council Member Defeated"));
				CheckHandler.CheckLocation(locationName);
			}

			private static string GetCouncilLevelName(int tier)
			{
				return (tier >= 0 && tier < CouncilLevels.Length) ? CouncilLevels[tier] : "Unknown";
			}
		}

		[HarmonyPatch(typeof(GameController), "LoadLevel")]
		public static class LoadLevelPatches
		{
			[HarmonyPrefix]
			public static void LoadLevelPrefix(ref string givenLevelName)
			{
				if (givenLevelName == "Hub" || givenLevelName == "PlayerRoom" || givenLevelName == "TitleScreen" || givenLevelName == "InitialLoad" || givenLevelName == "Tutorial" || givenLevelName == "Credits")
				{
					BiomesHandler.ResetRun();
				}
				else if (BiomesHandler.AllBiomes.Contains(givenLevelName))
				{
					BiomesHandler.OrganizeLevelList();
					givenLevelName = GameController.levelNameList[GameController.tierCount];
				}
				DataManager.SaveData();
			}
		}

		[HarmonyPatch(typeof(GameController))]
		public static class DisableNextLevelShufflePatches
		{
			[HarmonyPatch(/*Could not decode attribute arguments.*/)]
			[HarmonyPrefix]
			public static bool NextLevelNameSetterPrefix()
			{
				if ((Object)(object)GameController.loadingScreen != (Object)null && (Object)(object)GameController.loadingScreen.gameProgressBoard != (Object)null)
				{
					GameController.loadingScreen.gameProgressBoard.InitializeTierOrders();
				}
				return false;
			}
		}
	}
	public static class LoadingScreenPatches
	{
		[HarmonyPatch(typeof(Portal), "OnTriggerEnter2D")]
		public static class PortalTriggerPatch
		{
			[HarmonyPrefix]
			public static void Prefix(Collider2D col)
			{
				if ((Object)(object)col != (Object)null && ((Component)col).gameObject.CompareTag("Player") && (((Object)((Component)col).gameObject).name == "AllyFloorContact" || ((Object)((Component)col).gameObject).name == "IgnoreFloorContact"))
				{
					CleanUIBiomesClones();
				}
			}
		}

		[HarmonyPatch(typeof(GameProgressBoard), "InitializeTierOrders")]
		public static class GameProgressBoardInitializeTierOrdersPatch
		{
			[HarmonyPrefix]
			public static bool Prefix(GameProgressBoard __instance, bool ___initializationReady, ref int ___curMaxStagesPerTier, ref int ___curMaxTierCount, ref int ___curTotalStageCount, ref int ___curFinalStageIndex, ref Vector2[] ___curStagePositions, ref RectTransform[] ___stagesInOrder, ref Image[] ___dimmersInOrder, ref RectTransform[] ___bossesInOrder, Dictionary<string, RectTransform> ___bossCircles, Dictionary<string, Image> ___bossDimmers, Dictionary<string, RectTransform[]> ___levelMarkers, Dictionary<string, Image[]> ___levelDimmers)
			{
				//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
				if (!___initializationReady)
				{
					return false;
				}
				CleanUIBiomesClones();
				Traverse.Create((object)__instance).Method("SetModeVars", new object[0]).GetValue();
				__instance.HideRegularStages();
				___curTotalStageCount = ___curMaxTierCount * ___curMaxStagesPerTier + 2;
				___curFinalStageIndex = ___curTotalStageCount - 1;
				___stagesInOrder[___curFinalStageIndex] = __instance.finalBossCircle;
				___dimmersInOrder[___curFinalStageIndex] = __instance.finalBossDimmer;
				HashSet<string> hashSet = new HashSet<string>();
				for (int i = 1; i < ___curTotalStageCount - 1; i += ___curMaxStagesPerTier)
				{
					int num = (i - 1) / ___curMaxStagesPerTier;
					string currentTierScene = NextLevelLoader.GetCurrentTierScene(num);
					if (string.IsNullOrEmpty(currentTierScene) || !___bossCircles.ContainsKey(currentTierScene))
					{
						continue;
					}
					RectTransform val = ___bossCircles[currentTierScene];
					Image val2 = ___bossDimmers[currentTierScene];
					RectTransform[] array = ___levelMarkers[currentTierScene];
					Image[] array2 = ___levelDimmers[currentTierScene];
					if (hashSet.Contains(currentTierScene))
					{
						RectTransform val3 = val;
						val = Object.Instantiate<RectTransform>(val3, ((Transform)val3).parent);
						createdUIBiomesClones.Add(((Component)val).gameObject);
						Image val4 = val2;
						Transform val5 = FindChildRecursive((Transform)(object)val, ((Object)((Component)val4).gameObject).name);
						val2 = (((Object)(object)val5 != (Object)null) ? ((Component)val5).GetComponent<Image>() : ((Component)val).GetComponent<Image>());
						RectTransform[] array3 = (RectTransform[])(object)new RectTransform[array.Length];
						Image[] array4 = (Image[])(object)new Image[array2.Length];
						for (int j = 0; j < array.Length; j++)
						{
							if ((Object)(object)array[j] != (Object)null)
							{
								Transform val6 = FindChildRecursive((Transform)(object)val, ((Object)((Component)array[j]).gameObject).name);
								if ((Object)(object)val6 != (Object)null)
								{
									array3[j] = ((Component)val6).GetComponent<RectTransform>();
									array4[j] = ((Component)val6).GetComponent<Image>();
								}
							}
						}
						array = array3;
						array2 = array4;
					}
					else
					{
						hashSet.Add(currentTierScene);
					}
					if (__instance.visible)
					{
						((Component)val).gameObject.SetActive(true);
					}
					val.anchoredPosition = ___curStagePositions[num];
					int num2 = array2.Length - 1;
					int num3 = ___curMaxStagesPerTier - 2;
					while (num2 >= 0)
					{
						if (num3 >= 0)
						{
							___dimmersInOrder[i + num3] = array2[num2];
							___stagesInOrder[i + num3] = array[num2];
							Image obj = array2[num2];
							if (obj != null)
							{
								((Component)obj).gameObject.SetActive(true);
							}
							RectTransform obj2 = array[num2];
							if (obj2 != null)
							{
								((Component)obj2).gameObject.SetActive(true);
							}
						}
						else
						{
							Image obj3 = array2[num2];
							if (obj3 != null)
							{
								((Component)obj3).gameObject.SetActive(false);
							}
							RectTransform obj4 = array[num2];
							if (obj4 != null)
							{
								((Component)obj4).gameObject.SetActive(false);
							}
						}
						num2--;
						num3--;
					}
					___dimmersInOrder[i + ___curMaxStagesPerTier - 1] = val2;
					___stagesInOrder[i + ___curMaxStagesPerTier - 1] = val;
					___bossesInOrder[num] = val;
				}
				Canvas.ForceUpdateCanvases();
				return false;
			}

			private static Transform FindChildRecursive(Transform parent, string name)
			{
				Transform[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Transform>(true);
				foreach (Transform val in componentsInChildren)
				{
					if (((Object)val).name == name)
					{
						return val;
					}
				}
				return null;
			}
		}

		private static readonly List<GameObject> createdUIBiomesClones = new List<GameObject>();

		public static void CleanUIBiomesClones()
		{
			if (createdUIBiomesClones != null && createdUIBiomesClones.Count < 0)
			{
				return;
			}
			foreach (GameObject createdUIBiomesClone in createdUIBiomesClones)
			{
				if ((Object)(object)createdUIBiomesClone != (Object)null)
				{
					Object.Destroy((Object)(object)createdUIBiomesClone);
				}
			}
			createdUIBiomesClones.Clear();
		}
	}
	[HarmonyPatch(typeof(LoopNpc))]
	public class LoopNpcPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch("InitiateLoop")]
		public static void InitiateLoopPrefix()
		{
			string locNameFormat = "Strange Time Keeper Slot {0}";
			CheckHandler.SendNpcCheck(locNameFormat, 5);
		}
	}
	[HarmonyPatch(typeof(OutfitStoreItem))]
	public class OutfitStoreItemPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		public static void StartPostfix(OutfitStoreItem __instance)
		{
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: 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)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			string slotNameFormat = "Outfit Shop Slot {0}";
			int maxSlots = ((ItemHandler.GetTotalShopUpgrade() != 0) ? 16 : 0);
			if (!ShopService.TryGetNextLocation(slotNameFormat, maxSlots, out var locId, out var locName))
			{
				ShopService.DestroyShopItem(((Component)__instance).gameObject, (Component)(object)((StoreItem)__instance).priceMarker);
				return;
			}
			((StoreItem)__instance).overheadPromptHeight = 3.8f;
			((StoreItem)__instance).usePlatinumCost = true;
			((VarStat<float>)(object)((StoreItem)__instance).costStat)?.Initialize(20f, true);
			PriceMarker priceMarker = ((StoreItem)__instance).priceMarker;
			if (priceMarker != null)
			{
				priceMarker.SetText(((StoreItem)__instance).Cost.ToString());
			}
			if ((Object)(object)((StoreItem)__instance).itemBGSpriteRenderer != (Object)null)
			{
				((Renderer)((StoreItem)__instance).itemBGSpriteRenderer).enabled = false;
			}
			if ((Object)(object)((StoreItem)__instance).itemSpriteRenderer != (Object)null)
			{
				((Renderer)((StoreItem)__instance).itemSpriteRenderer).material = new Material(Shader.Find("Sprites/Default"));
				Vector3 localPosition = ((Component)((StoreItem)__instance).itemSpriteRenderer).transform.localPosition;
				((Component)((StoreItem)__instance).itemSpriteRenderer).transform.localPosition = new Vector3(localPosition.x, localPosition.y + 1f, localPosition.z);
			}
			ShopService.SetupAPShopItem(((Component)__instance).gameObject, locName, locId, ((StoreItem)__instance).itemSpriteRenderer, delegate(string title, string desc)
			{
				if ((Object)(object)((StoreItem)__instance).itemText != (Object)null)
				{
					((StoreItem)__instance).itemText.text = title;
				}
				if ((Object)(object)((StoreItem)__instance).descText != (Object)null)
				{
					((StoreItem)__instance).descText.text = desc;
				}
			});
		}

		[HarmonyPrefix]
		[HarmonyPatch("Buy")]
		public static bool BuyPrefix(OutfitStoreItem __instance)
		{
			return ShopService.ProcessPurchase(((Component)__instance).gameObject, (Component)(object)((StoreItem)__instance).priceMarker, ((StoreItem)__instance).Cost, usePlatinumCost: true, delegate
			{
				Npc parentNpc = ((StoreItem)__instance).parentNpc;
				if (parentNpc != null)
				{
					parentNpc.PlayDefaultEmote();
				}
			}, delegate
			{
				SoundManager.PlayAudio("MenuError", 1f, false, -1f, -1f);
				Npc parentNpc = ((StoreItem)__instance).parentNpc;
				if (parentNpc != null)
				{
					parentNpc.PlayEmote((EmoteType)9);
				}
			});
		}
	}
	[HarmonyPatch(typeof(Player), "Start")]
	public class PlayerInitPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				StartingInventoryHandler.ApplyArchipelagoStartingSkills();
				ItemHandler.SyncPlayerMaxHP(__instance, restoreAllHP: true);
			}
		}
	}
	[HarmonyPatch(typeof(RelicChestUI), "AssignPlayerItem")]
	public class RelicChestUIAssignPlayerItemPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(ref bool __result, List<string> ___currentSelectionList, int ___currentStartIndex, int ___currentSelectionIndex, Player ___currentPlayer)
		{
			string text = ___currentSelectionList[___currentStartIndex + ___currentSelectionIndex];
			if (Item.IsUnlocked(text, false))
			{
				int num = 1 + ItemHandler.GetItemCountByName("Relic Slot Upgrade");
				int count = ((Entity)___currentPlayer).inventory.Count;
				if (count == 0)
				{
					return true;
				}
				if (count < num)
				{
					((Entity)___currentPlayer).inventory.AddItem(text, true, false);
					if (!DataManager.SavedHubRelics.Contains(text))
					{
						DataManager.SavedHubRelics.Add(text);
						DataManager.SaveData();
					}
					SoundManager.PlayAudio("WoodPickUp", 1f, false, -1f, -1f);
					__result = true;
					return false;
				}
				return true;
			}
			SoundManager.PlayAudio("MenuError", 1f, false, -1f, -1f);
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(ShufflerNpc))]
	public class ShufflerNpcPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("InitCardShuffle")]
		public static void InitCardShufflePostfix()
		{
			string locNameFormat = "Nocturne the Cardist Slot {0}";
			CheckHandler.SendNpcCheck(locNameFormat, 10);
		}
	}
	public static class SkillSlotsPatches
	{
		[HarmonyPatch(typeof(CooldownUI), "RefreshEntries")]
		public static class CooldownUIRefreshEntriesPatch
		{
			public static void Postfix(CooldownUI __instance)
			{
				for (int i = 0; i < 6; i++)
				{
					Transform val = ((Component)__instance).transform.Find("Skill" + i);
					if ((Object)(object)val != (Object)null)
					{
						((Component)val).gameObject.SetActive(SlotManager.IsSlotUnlocked(i));
					}
				}
			}
		}

		[HarmonyPatch(typeof(EquipMenu), "LoadEquipMenu")]
		public static class EquipMenuLoadEquipMenuPatch
		{
			public static void Postfix(EquipMenu __instance, ref int ___navigationIndex)
			{
				Transform val = ((Component)__instance).transform.Find("EquipBar");
				if ((Object)(object)val != (Object)null)
				{
					for (int i = 0; i < 6; i++)
					{
						bool active = SlotManager.IsSlotUnlocked(i);
						Transform val2 = val.Find("Skills/Skill" + (i + 1));
						if ((Object)(object)val2 != (Object)null)
						{
							((Component)val2).gameObject.SetActive(active);
						}
						Transform val3 = val.Find("ButtonIcons/Button" + (i + 1));
						if ((Object)(object)val3 != (Object)null)
						{
							((Component)val3).gameObject.SetActive(active);
						}
					}
				}
				if (!SlotManager.IsSlotUnlocked(___navigationIndex))
				{
					___navigationIndex = SlotManager.GetFirstUnlockedSlot();
					__instance.SelectSlot(___navigationIndex);
				}
			}
		}

		[HarmonyPatch(typeof(EquipMenu), "ChangeHorizontalIndex")]
		public static class EquipMenuChangeHorizontalIndexPatch
		{
			public static bool Prefix(int navIndex, bool increment, ref int __result)
			{
				int num = (increment ? 1 : (-1));
				int num2 = (navIndex + num + 6) % 6;
				int num3 = 0;
				while (!SlotManager.IsSlotUnlocked(num2) && num3 < 6)
				{
					num2 = (num2 + num + 6) % 6;
					num3++;
				}
				__result = num2;
				return false;
			}
		}

		[HarmonyPatch(typeof(EquipMenu), "SelectSlot")]
		public static class EquipMenuSelectSlotPatch
		{
			public static void Prefix(ref int navIndex)
			{
				if (navIndex >= 0 && navIndex < 6 && !SlotManager.IsSlotUnlocked(navIndex))
				{
					navIndex = SlotManager.GetLastUnlockedSlot();
				}
			}
		}

		[HarmonyPatch(typeof(EquipMenu), "HandleQuickSwap")]
		public static class EquipMenuHandleQuickSwapPatch
		{
			public static bool Prefix(EquipMenu __instance, ref bool __result)
			{
				//IL_0085: Unknown result type (might be due to invalid IL or missing references)
				//IL_027b: Unknown result type (might be due to invalid IL or missing references)
				//IL_028a: Unknown result type (might be due to invalid IL or missing references)
				Traverse val = Traverse.Create((object)__instance);
				bool value = val.Field("quickSwapCompleted").GetValue<bool>();
				Player player = __instance.player;
				if (!value && player.inputDevice.GetButton("EquipMenu") && player.stockSkill != null)
				{
					Image[] value2 = val.Field("skillBorders").GetValue<Image[]>();
					Text value3 = val.Field("swapText").GetValue<Text>();
					GameObject value4 = val.Field("swapPrompt").GetValue<GameObject>();
					((Graphic)value2[6]).color = Color.green;
					((Component)value3).gameObject.SetActive(true);
					value4.SetActive(false);
					for (int i = 0; i < player.assignedSkills.Length; i++)
					{
						if (player.inputDevice.GetButton("Skill" + i))
						{
							if (!SlotManager.IsSlotUnlocked(i))
							{
								SoundManager.PlayAudio("MenuError", 1f, false, -1f, -1f);
								break;
							}
							if (!val.Method("IsSignificantSkill", new object[2]
							{
								player.assignedSkills[i],
								player.stockSkill
							}).GetValue<bool>())
							{
								SkillState stockSkill = player.stockSkill;
								player.stockSkill = player.assignedSkills[i];
								player.AssignSkillSlot(i, stockSkill.skillID, false, false);
								SoundManager.PlayAudio("Equip", 1f, false, -1f, -1f);
								__instance.parentHUD.cooldownUI.RefreshEntries();
								val.Field("quickSwapCompleted").SetValue((object)true);
								break;
							}
							SoundManager.PlayAudio("MenuError", 1f, false, -1f, -1f);
						}
					}
					__result = true;
					return false;
				}
				if (!val.Field("currentlySwapping").GetValue<bool>())
				{
					GameObject value5 = val.Field("skillSelectedBorder").GetValue<GameObject>();
					Image[] value6 = val.Field("skillBorders").GetValue<Image[]>();
					Text value7 = val.Field("swapText").GetValue<Text>();
					GameObject value8 = val.Field("swapPrompt").GetValue<GameObject>();
					int value9 = val.Field("navigationIndex").GetValue<int>();
					value5.transform.position = ((Component)value6[value9]).transform.position;
					((Graphic)value6[6]).color = Color.white;
					((Component)value7).gameObject.SetActive(false);
					value8.SetActive(true);
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(EquipMenu), "SkillClicked")]
		public static class EquipMenuSkillClickedPatch
		{
			public static bool Prefix(int slotNumber)
			{
				return slotNumber >= 6 || SlotManager.IsSlotUnlocked(slotNumber);
			}
		}

		[HarmonyPatch(typeof(EquipMenu), "SkillHovered")]
		public static class EquipMenuSkillHoveredPatch
		{
			public static bool Prefix(int slotNumber)
			{
				return slotNumber >= 6 || SlotManager.IsSlotUnlocked(slotNumber);
			}
		}

		[HarmonyPatch(typeof(ChaosInputDevice))]
		public static class ChaosInputDeviceBlockLockedSlotsPatch
		{
			private static bool IsLockedSkillButton(string buttonName)
			{
				if (string.IsNullOrEmpty(buttonName) || !buttonName.StartsWith("Skill"))
				{
					return false;
				}
				if (int.TryParse(buttonName.Substrin