Decompiled source of CupheadArchipelago v0.2.100207130
BepInEx/plugins/Archipelago.MultiClient.Net.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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/CupheadArchipelago.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.BounceFeatures.DeathLink; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Exceptions; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.MessageLog.Messages; using Archipelago.MultiClient.Net.Models; using Archipelago.MultiClient.Net.Packets; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CupheadArchipelago.AP; using CupheadArchipelago.Config; using CupheadArchipelago.Helpers.FVerParser; using CupheadArchipelago.Hooks; using CupheadArchipelago.Hooks.AssetHooks; using CupheadArchipelago.Hooks.AudioHooks; using CupheadArchipelago.Hooks.CutsceneHooks; using CupheadArchipelago.Hooks.LevelHooks; using CupheadArchipelago.Hooks.MapHooks; using CupheadArchipelago.Hooks.MapHooks.MapNPCHooks; using CupheadArchipelago.Hooks.MapHooks.MapUIHooks; using CupheadArchipelago.Hooks.MenuHooks; using CupheadArchipelago.Hooks.Mitigations; using CupheadArchipelago.Hooks.PlayerHooks; using CupheadArchipelago.Hooks.PlayerHooks.LevelPlayerHooks; using CupheadArchipelago.Hooks.PlayerHooks.PlanePlayerHooks; using CupheadArchipelago.Hooks.ShopHooks; using CupheadArchipelago.Interfaces; using CupheadArchipelago.Mapping; using CupheadArchipelago.Resources; using CupheadArchipelago.TestEnv; using CupheadArchipelago.Unity; using CupheadArchipelago.Util; using FVer; using HarmonyLib; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TMPro; using UnityEngine; using UnityEngine.U2D; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: InternalsVisibleTo("CupheadArchipelago.Tests")] [assembly: AssemblyCompany("CupheadArchipelago")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyCopyright("Copyright 2025-2026 JKLeckr")] [assembly: AssemblyDescription("The Cuphead Archipelago Mod")] [assembly: AssemblyFileVersion("0.2.2.7")] [assembly: AssemblyInformationalVersion("0.2.2.7+bfbb14c69c183d93e4986aa6b388a58a603c0a9d")] [assembly: AssemblyProduct("CupheadArchipelago")] [assembly: AssemblyTitle("CupheadArchipelago")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.2.2.7")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 FVer { public sealed class FVersion : IComparable<FVersion> { public readonly int Baseline; public readonly int Release; public readonly string Prefix; public readonly string Postfix; private readonly int _revision; public string Revision => IntToRevision(_revision); public int RevisionNumber => _revision; public FVersion(int baseline, string revision, int release = 0, string prefix = "", string postfix = "") { if (baseline < 0 || release < 0) { throw new ArgumentOutOfRangeException("Version numbers must not be negative"); } if (string.IsNullOrEmpty(revision)) { throw new ArgumentNullException("Revision cannot be null or empty"); } _revision = RevisionToInt(revision); Baseline = baseline; Release = release; Prefix = prefix ?? ""; Postfix = postfix ?? ""; } public FVersion(int baseline, int revision, int release = 0, string prefix = "", string postfix = "") { if (baseline < 0 || revision < 0 || release < 0) { throw new ArgumentOutOfRangeException("Version numbers must not be negative"); } _revision = revision; Baseline = baseline; Release = release; Prefix = prefix ?? ""; Postfix = postfix ?? ""; } public FVersion(string version) { if (string.IsNullOrEmpty(version) || version.Trim().Length == 0) { throw new ArgumentNullException("version"); } string postfix = ""; int num = version.IndexOf('-'); if (num >= 0) { postfix = version.Substring(num + 1); version = version.Substring(0, num); } string prefix = ""; int num2 = -1; for (int i = 0; i < version.Length; i++) { if (char.IsDigit(version[i])) { num2 = i; break; } } if (num2 > 0) { prefix = version.Substring(0, num2); } else if (num2 < 0) { throw new FormatException("No baseline digits found in version string."); } version = version.Substring(num2); int j; for (j = 0; j < version.Length && char.IsDigit(version[j]); j++) { } if (j < 2) { throw new FormatException("Baseline must contain at least two digits."); } int baseline = int.Parse(version.Substring(0, j)); int num3 = j; int k; for (k = 0; num3 + k < version.Length && char.IsLetter(version[num3 + k]); k++) { } if (k == 0) { throw new FormatException("No alphabetical revision segment found."); } string rev = version.Substring(num3, k); int release = 0; int l = num3 + k; if (l < version.Length && version[l] == '.') { l++; int num4 = l; for (; l < version.Length && char.IsDigit(version[l]); l++) { } if (num4 == l) { throw new FormatException("Expected release number after '.'"); } release = int.Parse(version.Substring(num4, l - num4)); } if (l < version.Length) { throw new FormatException("Unexpected trailing characters in version string after release number: '" + version.Substring(l) + "'"); } Baseline = baseline; _revision = RevisionToInt(rev); Release = release; Prefix = prefix; Postfix = postfix; } private static int RevisionToInt(string rev) { int num = 0; foreach (char c in rev) { if (c < 'a' || c > 'z') { throw new FormatException("Invalid revision character"); } int num2 = c - 97 + 1; if (num > (int.MaxValue - num2) / 26) { throw new OverflowException("Revision string is too large to fit in Int32"); } num = num * 26 + num2; } return num - 1; } private static string IntToRevision(int i) { if (i < 0) { throw new ArgumentOutOfRangeException("i"); } int num = i + 1; char[] array = new char[16]; int num2 = array.Length; while (num > 0) { num--; array[--num2] = (char)(97 + num % 26); num /= 26; } return new string(array, num2, array.Length - num2); } public static FVersion Zero() { return new FVersion(0, 0); } public int CompareTo(FVersion other) { if (other == null) { return 1; } int num = string.IsNullOrEmpty(Prefix).CompareTo(string.IsNullOrEmpty(other.Prefix)); if (num != 0) { return num; } num = string.Compare(Prefix, other.Prefix, StringComparison.Ordinal); if (num != 0) { return num; } int baseline = Baseline; num = baseline.CompareTo(other.Baseline); if (num != 0) { return num; } num = RevisionToInt(Revision).CompareTo(RevisionToInt(other.Revision)); if (num != 0) { return num; } baseline = Release; num = baseline.CompareTo(other.Release); if (num != 0) { return num; } num = string.IsNullOrEmpty(Postfix).CompareTo(string.IsNullOrEmpty(other.Postfix)); if (num != 0) { return num; } return ComparePostfix(Postfix, other.Postfix); } private static int ComparePostfix(string a, string b) { string[] array = a.Split(new char[1] { '.' }); string[] array2 = b.Split(new char[1] { '.' }); int num = Math.Max(a.Length, b.Length); for (int i = 0; i < num; i++) { bool flag = i >= array.Length; bool flag2 = i >= array2.Length; if (flag && flag2) { return 0; } if (flag) { return -1; } if (flag2) { return 1; } string text = array[i]; string text2 = array2[i]; int result; bool flag3 = int.TryParse(text, out result); int result2; bool flag4 = int.TryParse(text2, out result2); if (flag3 && flag4) { int num2 = result.CompareTo(result2); if (num2 != 0) { return num2; } continue; } if (flag3) { return -1; } if (flag4) { return 1; } if (string.Compare(text, text2, StringComparison.Ordinal) != 0) { return 0; } } return 0; } public override bool Equals(object obj) { if (obj is FVersion fVersion) { return Baseline == fVersion.Baseline && _revision == fVersion._revision && Release == fVersion.Release && Prefix == fVersion.Prefix && Postfix == fVersion.Postfix; } return false; } public override int GetHashCode() { int num = 17; int num2 = num; int baseline = Baseline; num = num2 * (29 + baseline.GetHashCode()); int num3 = num; baseline = _revision; num = num3 * (29 + baseline.GetHashCode()); int num4 = num; baseline = Release; num = num4 * (29 + baseline.GetHashCode()); num *= 29 + (Prefix?.GetHashCode() ?? 0); return num * (29 + (Postfix?.GetHashCode() ?? 0)); } public override string ToString() { string text = string.Format("{0}{1:D2}{2}", Prefix ?? "", Baseline, Revision); if (Release > 0) { text += $".{Release}"; } if (!string.IsNullOrEmpty(Postfix)) { text = text + "-" + Postfix; } return text; } public static bool operator ==(FVersion a, FVersion b) { return a.Equals(b); } public static bool operator !=(FVersion a, FVersion b) { return !(a == b); } public static bool operator <(FVersion a, FVersion b) { return a.CompareTo(b) < 0; } public static bool operator >(FVersion a, FVersion b) { return a.CompareTo(b) > 0; } public static bool operator <=(FVersion a, FVersion b) { return a.CompareTo(b) <= 0; } public static bool operator >=(FVersion a, FVersion b) { return a.CompareTo(b) >= 0; } public static implicit operator string(FVersion v) { return v.ToString(); } } } namespace CupheadArchipelago { public class LogFiles { private const string LOG_FILE_EXTENSION = ".log"; public static string LogDirPath { get; private set; } public static string LogName { get; private set; } public static string LogFile { get; private set; } public static string LogFullPath { get; private set; } public static int LogFileMax { get; private set; } public static void Setup(string logName, string logDirName, int fileMax) { LogName = logName; LogFileMax = fileMax; if (fileMax == 0) { Logging.Log("Mod log file max set to 0. Not logging."); return; } string text = Path.Combine(Paths.BepInExRootPath, "logs"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, logDirName); Directory.CreateDirectory(text2); LogDirPath = text2; SetupLogName(logName, text2, fileMax); } private static void SetupLogName(string logName, string logDir, int fileMax) { Logging.LogDebug(logDir); var list = (from name in Directory.GetFiles(logDir, logName + ".*.log").Select(Path.GetFileName) select new { FileName = name, Number = GetLogFileNumber(name, logName) } into x where x.Number.HasValue orderby x.Number.Value select x).ToList(); while (fileMax > 0 && list.Count >= fileMax) { var anon = list.First(); File.Delete(Path.Combine(logDir, anon.FileName)); list.RemoveAt(0); } uint value; if (list.Any()) { value = list.Last().Number.Value; value++; } else { value = 0u; } LogFile = string.Format("{0}.{1}{2}", logName, value, ".log"); LogFullPath = Path.Combine(LogDirPath, LogFile); } private static uint? GetLogFileNumber(string fileName, string logName) { if (fileName.StartsWith(logName, StringComparison.Ordinal) && fileName.EndsWith(".log", StringComparison.Ordinal)) { string s = fileName.Substring(logName.Length + 1, fileName.Length - logName.Length - ".log".Length - 1); if (uint.TryParse(s, out var result)) { return result; } } return null; } } public class Logging { private static bool init; private static ManualLogSource logSource; private static Action<LogLevel, object> logAction; private static LoggingFlags loggingFlags; private static LoggingFlags permLoggingFlags; internal static void Init(ManualLogSource logSource, LoggingFlags loggingFlags) { if (init) { Console.WriteLine("Reinitializing Logging..."); } Logging.logSource = logSource ?? throw new ArgumentNullException("logSource cannot be null."); logAction = logSource.Log; Logging.loggingFlags = loggingFlags; permLoggingFlags = loggingFlags; init = true; } internal static void Init(Action<LogLevel, object> logAction, LoggingFlags loggingFlags) { if (init) { Console.WriteLine("Reinitializing Logging..."); } logSource = null; Logging.logAction = logAction; Logging.loggingFlags = loggingFlags; permLoggingFlags = loggingFlags; init = true; } internal static bool IsLoggingInitialized() { return init; } public static void Log(object data) { Log(data, (LogLevel)16); } public static void Log(object data, LogLevel logLevel) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Invalid comparison between Unknown and I4 //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_000e: Unknown result type (might be due to invalid IL or missing references) Log(data, ((int)logLevel != 1 && (int)logLevel != 2) ? LoggingFlags.Info : LoggingFlags.None, logLevel); } public static void Log(object data, LoggingFlags requiredFlags) { Log(data, requiredFlags, (LogLevel)16); } public static void Log(object data, LoggingFlags requiredFlags, LogLevel logLevel) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (!init) { throw new Exception("Logging not initialized."); } if (IsLoggingFlagsEnabled(requiredFlags)) { logAction(logLevel, data); } } public static void LogMessage(object data) { LogMessage(data, LoggingFlags.Message); } public static void LogMessage(object data, LoggingFlags requiredFlags) { Log(data, requiredFlags, (LogLevel)8); } public static void LogWarning(object data) { LogWarning(data, LoggingFlags.Warning); } public static void LogWarning(object data, LoggingFlags requiredFlags) { Log(data, requiredFlags, (LogLevel)4); } public static void LogError(object data) { LogError(data, LoggingFlags.None); } public static void LogError(object data, LoggingFlags requiredFlags) { Log(data, requiredFlags, (LogLevel)2); } public static void LogFatal(object data) { LogFatal(data, LoggingFlags.None); } public static void LogFatal(object data, LoggingFlags requiredFlags) { Log(data, requiredFlags, (LogLevel)1); } public static void LogDebug(object data) { LogDebug(data, LoggingFlags.Debug); } public static void LogDebug(object data, LoggingFlags requiredFlags) { Log(data, requiredFlags, (LogLevel)(MConf.IsDebugLogsInfo() ? 16 : 32)); } public static bool IsLoggingFlagsEnabled(LoggingFlags flags) { return (flags & loggingFlags) == flags; } public static bool IsDebugEnabled() { return IsLoggingFlagsEnabled(LoggingFlags.Debug); } internal static void SetLoggingFlags(LoggingFlags flags) { loggingFlags = flags; } internal static void AddLoggingFlags(LoggingFlags flags) { loggingFlags |= flags; } internal static void RemoveLoggingFlags(LoggingFlags flags) { loggingFlags &= (LoggingFlags)(byte)(~(int)flags); } internal static void ResetLoggingFlags() { loggingFlags = permLoggingFlags; } public static string GetLogSourceName() { ManualLogSource obj = logSource; return (obj != null) ? obj.SourceName : null; } } [Flags] public enum LoggingFlags : byte { None = 0, PluginInfo = 1, Info = 2, Message = 4, Warning = 8, Network = 0x10, Debug = 0x20 } public class ModLogListener : ILogListener, IDisposable { public string LogSourceName { get; protected set; } public LogLevel DisplayedLogLevel { get; set; } public TextWriter LogWriter { get; protected set; } public Timer FlushTimer { get; protected set; } public bool WriteFromUnityLog { get; set; } public ModLogListener(string logFile, string logPath, string logSourceName, LogLevel displayedLogLevel = (LogLevel)63, bool includeUnityLog = true) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) LogSourceName = logSourceName; WriteFromUnityLog = includeUnityLog; DisplayedLogLevel = displayedLogLevel; FileStream stream = default(FileStream); if (!Utility.TryOpenFileStream(Path.Combine(logPath, logFile), FileMode.Create, ref stream, FileAccess.Write, FileShare.Read)) { Logging.LogError("Could not open \"" + logFile + "\" for writing. Not logging."); return; } Logging.Log("Logging to " + logFile); LogWriter = TextWriter.Synchronized(new StreamWriter(stream, Utility.UTF8NoBom)); FlushTimer = new Timer(delegate { LogWriter?.Flush(); }, null, 2000, 2000); } public void LogEvent(object sender, LogEventArgs eventArgs) { //IL_002f: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 if ((WriteFromUnityLog && eventArgs.Source is UnityLogSource) || (eventArgs.Source.SourceName == LogSourceName && (eventArgs.Level & DisplayedLogLevel) > 0)) { LogWriter.WriteLine(((object)eventArgs).ToString()); } } public void Dispose() { FlushTimer?.Dispose(); LogWriter?.Flush(); LogWriter?.Dispose(); } ~ModLogListener() { Dispose(); } } public enum LicenseLogModes { Off = 0, FirstParty = 1, All = 3 } public static class ModInfo { public class ModLicense { public readonly string PLUGIN_NOTICE = "CupheadArchipelago\n Copyright (C) 2025-2026 JKLeckr\n\n The CupheadArchipelago project is free software: you can redistribute it and/or\n modify it under the terms of the GNU General Public License as published by the\n Free Software Foundation, either version 3 of the License, or (at your option)\n any later version.\n\n This program is distributed in the hope that it will be useful, but\n WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License along with this program.\n If not, see <https://www.gnu.org/licenses/>.\n\n ------------------------------------------------------------------------------------------\n\n The assets the CupheadArchipelago project have their own license:\n\n CupheadArchipelago Assets (c) 2025-2026 by JKLeckr is licensed under CC BY-SA 4.0.\n To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/\n"; public readonly string PLUGIN_LIB_NOTICE = "\n This mod uses third party libraries.\n For their notices, see the accompanying LICENSE.third-party.txt or a copy at\n You can set \"LogLicense = All\" in the config to print the third party notice."; } public class ModLicenseThirdParty { public readonly string PLUGIN_LIB_FULL_NOTICE = "CupheadArchipelago uses third party libraries listed in this document.\n\n FVer\n\n Copyright 2025-2026 JKLeckr\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n\n Archipelago.MultiClient.NET\n\n MIT License\n\n Copyright (c) 2022 Hussein Farran, Jarno Westhof\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n\n Archipelago.MultiClient.NET also includes:\n\n A modified fork of Newtonsoft Json.NET\n\n The MIT License (MIT)\n\n Copyright (c) 2007 James Newton-King\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n\n\n native-websocket-sharp\n\n Copyright (c) 2026 JKLeckr\n\n Mozilla Public License Version 2.0\n ==================================\n\n 1. Definitions\n --------------\n\n 1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n 1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n 1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n 1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n 1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n 1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n 1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in\n a separate file or files, that is not Covered Software.\n\n 1.8. \"License\"\n means this document.\n\n 1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n 1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n 1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n 1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n 1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n 1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n 2. License Grants and Conditions\n --------------------------------\n\n 2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n (a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n (b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n 2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution\n become effective for each Contribution on the date the Contributor first\n distributes such Contribution.\n\n 2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under\n this License. No additional rights or licenses will be implied from the\n distribution or licensing of Covered Software under this License.\n Notwithstanding Section 2.1(b) above, no patent license is granted by a\n Contributor:\n\n (a) for any code that a Contributor has removed from Covered Software;\n or\n\n (b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n (c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\n This License does not grant any rights in the trademarks, service marks,\n or logos of any Contributor (except as may be necessary to comply with\n the notice requirements in Section 3.4).\n\n 2.4. Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to\n distribute the Covered Software under a subsequent version of this\n License (see Section 10.2) or under the terms of a Secondary License (if\n permitted under the terms of Section 3.3).\n\n 2.5. Representation\n\n Each Contributor represents that the Contributor believes its\n Contributions are its original creation(s) or it has sufficient rights\n to grant the rights to its Contributions conveyed by this License.\n\n 2.6. Fair Use\n\n This License is not intended to limit any rights You have under\n applicable copyright doctrines of fair use, fair dealing, or other\n equivalents.\n\n 2.7. Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\n in Section 2.1.\n\n 3. Responsibilities\n -------------------\n\n 3.1. Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any\n Modifications that You create or to which You contribute, must be under\n the terms of this License. You must inform recipients that the Source\n Code Form of the Covered Software is governed by the terms of this\n License, and how they can obtain a copy of this License. You may not\n attempt to alter or restrict the recipients' rights in the Source Code\n Form.\n\n 3.2. Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n (a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n (b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n 3.3. Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice,\n provided that You also comply with the requirements of this License for\n the Covered Software. If the Larger Work is a combination of Covered\n Software with a work governed by one or more Secondary Licenses, and the\n Covered Software is not Incompatible With Secondary Licenses, this\n License permits You to additionally distribute such Covered Software\n under the terms of such Secondary License(s), so that the recipient of\n the Larger Work may, at their option, further distribute the Covered\n Software under the terms of either this License or such Secondary\n License(s).\n\n 3.4. Notices\n\n You may not remove or alter the substance of any license notices\n (including copyright notices, patent notices, disclaimers of warranty,\n or limitations of liability) contained within the Source Code Form of\n the Covered Software, except that You may alter any license notices to\n the extent required to remedy known factual inaccuracies.\n\n 3.5. Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support,\n indemnity or liability obligations to one or more recipients of Covered\n Software. However, You may do so only on Your own behalf, and not on\n behalf of any Contributor. You must make it absolutely clear that any\n such warranty, support, indemnity, or liability obligation is offered by\n You alone, and You hereby agree to indemnify every Contributor for any\n liability incurred by such Contributor as a result of warranty, support,\n indemnity or liability terms You offer. You may include additional\n disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n\n 4. Inability to Comply Due to Statute or Regulation\n ---------------------------------------------------\n\n If it is impossible for You to comply with any of the terms of this\n License with respect to some or all of the Covered Software due to\n statute, judicial order, or regulation then You must: (a) comply with\n the terms of this License to the maximum extent possible; and (b)\n describe the limitations and the code they affect. Such description must\n be placed in a text file included with all distributions of the Covered\n Software under this License. Except to the extent prohibited by statute\n or regulation, such description must be sufficiently detailed for a\n recipient of ordinary skill to be able to understand it.\n\n 5. Termination\n --------------\n\n 5.1. The rights granted under this License will terminate automatically\n if You fail to comply with any of its terms. However, if You become\n compliant, then the rights granted under this License from a particular\n Contributor are reinstated (a) provisionally, unless and until such\n Contributor explicitly and finally terminates Your grants, and (b) on an\n ongoing basis, if such Contributor fails to notify You of the\n non-compliance by some reasonable means prior to 60 days after You have\n come back into compliance. Moreover, Your grants from a particular\n Contributor are reinstated on an ongoing basis if such Contributor\n notifies You of the non-compliance by some reasonable means, this is the\n first time You have received notice of non-compliance with this License\n from such Contributor, and You become compliant prior to 30 days after\n Your receipt of the notice.\n\n 5.2. If You initiate litigation against any entity by asserting a patent\n infringement claim (excluding declaratory judgment actions,\n counter-claims, and cross-claims) alleging that a Contributor Version\n directly or indirectly infringes any patent, then the rights granted to\n You by any and all Contributors for the Covered Software under Section\n 2.1 of this License shall terminate.\n\n 5.3. In the event of termination under Sections 5.1 or 5.2 above, all\n end user license agreements (excluding distributors and resellers) which\n have been validly granted by You or Your distributors under this License\n prior to termination shall survive termination.\n\n ************************************************************************\n * *\n * 6. Disclaimer of Warranty *\n * ------------------------- *\n * *\n * Covered Software is provided under this License on an \"as is\" *\n * basis, without warranty of any kind, either expressed, implied, or *\n * statutory, including, without limitation, warranties that the *\n * Covered Software is free of defects, merchantable, fit for a *\n * particular purpose or non-infringing. The entire risk as to the *\n * quality and performance of the Covered Software is with You. *\n * Should any Covered Software prove defective in any respect, You *\n * (not any Contributor) assume the cost of any necessary servicing, *\n * repair, or correction. This disclaimer of warranty constitutes an *\n * essential part of this License. No use of any Covered Software is *\n * authorized under this License except under this disclaimer. *\n * *\n ************************************************************************\n\n ************************************************************************\n * *\n * 7. Limitation of Liability *\n * -------------------------- *\n * *\n * Under no circumstances and under no legal theory, whether tort *\n * (including negligence), contract, or otherwise, shall any *\n * Contributor, or anyone who distributes Covered Software as *\n * permitted above, be liable to You for any direct, indirect, *\n * special, incidental, or consequential damages of any character *\n * including, without limitation, damages for lost profits, loss of *\n * goodwill, work stoppage, computer failure or malfunction, or any *\n * and all other commercial damages or losses, even if such party *\n * shall have been informed of the possibility of such damages. This *\n * limitation of liability shall not apply to liability for death or *\n * personal injury resulting from such party's negligence to the *\n * extent applicable law prohibits such limitation. Some *\n * jurisdictions do not allow the exclusion or limitation of *\n * incidental or consequential damages, so this exclusion and *\n * limitation may not apply to You. *\n * *\n ************************************************************************\n\n 8. Litigation\n -------------\n\n Any litigation relating to this License may be brought only in the\n courts of a jurisdiction where the defendant maintains its principal\n place of business and such litigation shall be governed by laws of that\n jurisdiction, without reference to its conflict-of-law provisions.\n Nothing in this Section shall prevent a party's ability to bring\n cross-claims or counter-claims.\n\n 9. Miscellaneous\n ----------------\n\n This License represents the complete agreement concerning the subject\n matter hereof. If any provision of this License is held to be\n unenforceable, such provision shall be reformed only to the extent\n necessary to make it enforceable. Any law or regulation which provides\n that the language of a contract shall be construed against the drafter\n shall not be used to construe this License against a Contributor.\n\n 10. Versions of the License\n ---------------------------\n\n 10.1. New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section\n 10.3, no one other than the license steward has the right to modify or\n publish new versions of this License. Each version will be given a\n distinguishing version number.\n\n 10.2. Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version\n of the License under which You originally received the Covered Software,\n or under the terms of any subsequent version published by the license\n steward.\n\n 10.3. Modified Versions\n\n If you create software not governed by this License, and you want to\n create a new license for such software, you may create and use a\n modified version of this License if you rename the license and remove\n any references to the name of the license steward (except to note that\n such modified license differs from this License).\n\n 10.4. Distributing Source Code Form that is Incompatible With Secondary\n Licenses\n\n If You choose to distribute Source Code Form that is Incompatible With\n Secondary Licenses under the terms of this version of the License, the\n notice described in Exhibit B of this License must be attached.\n\n Exhibit A - Source Code Form License Notice\n -------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\n If it is not possible or desirable to put the notice in a particular\n file, then You may include the notice in a location (such as a LICENSE\n file in a relevant directory) where a recipient would be likely to look\n for such a notice.\n\n You may add additional accurate notices of copyright ownership.\n\n Exhibit B - \"Incompatible With Secondary Licenses\" Notice\n ---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n\n\n native-websocket-sharp uses the following third party libraries and/or attributions:\n\n For the managed component (websocket-sharp):\n\n Some of native-websocket-sharp is derived from the original websocket-sharp:\n\n The MIT License (MIT)\n\n Copyright (c) 2010-2026 sta.blockhead\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n THE SOFTWARE.\n\n\n For the native component (nativews):\n\n tungstenite-rs (signal fork) 0.27.0\n\n Repository: https://github.com/signalapp/tungstenite-rs\n License: MIT OR Apache-2.0\n\n Included license texts:\n\n MIT\n\n Copyright (c) 2017 Alexey Galakhov\n Copyright (c) 2016 Jason Housley\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n THE SOFTWARE.\n\n rustls 0.23.0\n\n Repository: https://github.com/rustls/rustls\n License: Apache-2.0 OR ISC OR MIT\n\n Included license texts:\n\n MIT\n\n Copyright (c) 2016 Joseph Birr-Pixton <[email protected]>\n\n Permission is hereby granted, free of charge, to any\n person obtaining a copy of this software and associated\n documentation files (the \"Software\"), to deal in the\n Software without restriction, including without\n limitation the rights to use, copy, modify, merge,\n publish, distribute, sublicense, and/or sell copies of\n the Software, and to permit persons to whom the Software\n is furnished to do so, subject to the following\n conditions:\n\n The above copyright notice and this permission notice\n shall be included in all copies or substantial portions\n of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\n SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\n IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n DEALINGS IN THE SOFTWARE.\n\n\n aws-lc-rs 1.15.4\n\n Repository: https://github.com/aws/aws-lc-rs\n License: Apache-2.0 AND (Apache-2.0 OR ISC)\n\n Included license texts:\n\n Apache-2.0\n\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n\n ISC\n\n Copyright Amazon.com, Inc. or its affiliates.\n\n Permission to use, copy, modify, and/or distribute this software for any\n purpose with or without fee is hereby granted, provided that the above\n copyright notice and this permission notice appear in all copies.\n\n THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n Each of these libraries have their own dependencies with their own licenses.\n Visit the repositories for each library for info on their dependencies.\n\n Binary distributions should have a generated nativews-THIRDPARTY.yml that has\n the complete list of libraries with attributions and/or licenses.\n If not, you can generate it using a tool like cargo-bundle-licenses inside the\n c-wspp-rs source project. You can get the source project from\n https://github.com/JKLeckr/native-websocket-sharp.\n\n\n CupheadArchipelago partially uses code from BepInEx for writing its log files\n\n MIT License\n\n Copyright (c) 2018 Bepis\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE."; } internal static bool IsMacOS() { return Environment.OSVersion.Platform switch { PlatformID.MacOSX => true, PlatformID.Unix => Directory.Exists("/System/Library/CoreServices"), _ => false, }; } } [BepInPlugin("com.JKLeckr.CupheadArchipelago", "CupheadArchipelago", "0.2.2.7")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInProcess("Cuphead.exe")] public class Plugin : BaseUnityPlugin { internal const string DEP_SAVECONFIG_MOD_GUID = "com.JKLeckr.CupheadSaveConfig"; protected const string MOD_NAME = "CupheadArchipelago"; protected const string MOD_GUID = "com.JKLeckr.CupheadArchipelago"; protected const string MOD_BASE_VERSION = "0.2.2.7"; protected const ushort MOD_VERSION_REL = 3; protected const string MOD_VERSION_POSTFIX = ""; protected const string PLUGIN_DIST = ""; protected static readonly string MOD_VERSION = GetModVersion("0.2.2.7", 0, ""); protected static readonly string MOD_FRIENDLY_VERSION = GetFVer("0.2.2.7", 3, ""); private const long CONFIG_VERSION = 1L; private static readonly string verPath = Path.Combine(Path.Combine(Paths.PluginPath, "CupheadArchipelago"), "configver"); private long configVer; private ConfigEntry<bool> configEnabled; private MConf config; public static string Name => "CupheadArchipelago"; public static string Version => MOD_VERSION; public static string SimpleFullVersion => MOD_FRIENDLY_VERSION + " (0.2.2.7)"; public static string FullVersion => MOD_FRIENDLY_VERSION + " (" + MOD_VERSION + ")"; public static int State { get; private set; } = 0; public static string StateMessage { get; private set; } = ""; internal static Plugin Current { get; private set; } = null; private void Awake() { if ((Object)(object)Current != (Object)null) { throw new Exception("Plugin is already loaded!"); } Current = this; SetupConfigVersion(); configEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Main", "Enabled", true, "Mod Master Switch"); if (configEnabled.Value) { config = new MConf(((BaseUnityPlugin)this).Config); SetupLogging(); Logging.Log("----------------------------------------"); Logging.Log("CupheadArchipelago " + FullVersion); if ("".Length > 0) { Logging.Log(" Build"); } Logging.Log("Created by JKLeckr"); Logging.Log("----------------------------------------"); Logging.Log("Game Build Version " + Application.version); if (configVer != 1) { Logging.LogWarning($"Config version changed ({configVer} -> {1L})! You may want to check the config."); configVer = 1L; } if (config.LogLicense > LicenseLogModes.Off) { ModInfo.ModLicense modLicense = new ModInfo.ModLicense(); string text = "License:\n--- LICENSE ---\n" + modLicense.PLUGIN_NOTICE + "\n"; if (config.LogLicense == LicenseLogModes.All) { ModInfo.ModLicenseThirdParty modLicenseThirdParty = new ModInfo.ModLicenseThirdParty(); text = text + "\n -- Third Party --\n" + modLicenseThirdParty.PLUGIN_LIB_FULL_NOTICE + "\n\n -- End Third Party --"; } else { text = text + "\n" + modLicense.PLUGIN_LIB_NOTICE; } text += "\n\n--- END LICENSE"; Logging.Log(text); } if (!IsPluginLoaded("com.JKLeckr.CupheadSaveConfig")) { try { Main.HookSaveKeyUpdater(config.SaveKeyName); Logging.Log("Using Save Key: " + config.SaveKeyName); } catch (Exception e) { Fail(e, -2); } } else { Logging.Log("[CupheadArchipelago] Plugin com.JKLeckr.CupheadSaveConfig is loaded, skipping SaveConfig", LoggingFlags.PluginInfo); } try { SaveData.Init(config.SaveKeyName); Main.HookMain(); ResourceLoader.LoadResources(); } catch (Exception e2) { Fail(e2, -1); } State = 1; StateMessage = ""; Logging.Log("Plugin com.JKLeckr.CupheadArchipelago is loaded!", LoggingFlags.PluginInfo); } else { Logging.Log("Plugin com.JKLeckr.CupheadArchipelago is loaded, but disabled!", LoggingFlags.PluginInfo); } } internal MConf GetConfig() { return config; } private bool IsPluginLoaded(string plugin) { return FindPlugin(plugin) >= 0; } private int FindPlugin(string plugin) { int num = 0; foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { BepInPlugin metadata = pluginInfo.Value.Metadata; if (metadata.GUID.Equals(plugin)) { return num; } num++; } return -1; } private static string GetModVersion(string vbase, ushort vrel, string vpostfix) { return vbase + ((vrel > 0) ? $"r{vrel}" : "") + ((vpostfix.Length > 0) ? ("-" + vpostfix) : ""); } private static string GetFVer(string ver, ushort rel, string postfix) { string text = ver + ((postfix.Length > 0) ? "-" : "") + postfix; RawFVer rawFVer = FVerParse.GetRawFVer(text, rel); FVersion fVersion = new FVersion(rawFVer.baseline, rawFVer.revision, rawFVer.release, rawFVer.prefix, rawFVer.postfix); return fVersion; } private void SetupConfigVersion() { if (File.Exists(verPath)) { try { string text = File.ReadAllText(verPath); configVer = (long)JsonConvert.DeserializeObject(text); ((BaseUnityPlugin)this).Logger.Log((LogLevel)16, (object)$"Config version {configVer}"); return; } catch (Exception) { ((BaseUnityPlugin)this).Logger.Log((LogLevel)16, (object)"Config version not found."); } } configVer = 1L; SaveConfigVersion(); ((BaseUnityPlugin)this).Logger.Log((LogLevel)16, (object)$"Config version {configVer}"); } private void SaveConfigVersion() { try { File.WriteAllText(verPath, JsonConvert.SerializeObject((object)configVer)); } catch (Exception) { ((BaseUnityPlugin)this).Logger.Log((LogLevel)16, (object)"Config version could not be written."); } } private void SetupLogging() { Logging.Init(((BaseUnityPlugin)this).Logger, config.LoggingFlags); if (config.ModLogs) { Logging.Log("Setting up mod logging..."); try { LogFiles.Setup("CupheadAPLog", "CupheadArchipelago", config.ModFileMax); } catch (Exception ex) { Logging.LogError("Mod logging set up failure: " + ex.Message); return; } ModLogListener item = new ModLogListener(LogFiles.LogFile, LogFiles.LogDirPath, ((BaseUnityPlugin)this).Logger.SourceName, (LogLevel)63); Logger.Listeners.Add((ILogListener)(object)item); Logging.Log("Mod logging started"); Logging.Log("This mod log is " + LogFiles.LogFile + " in " + LogFiles.LogDirPath); } } private void Fail(Exception e, int failCode) { Logging.LogError("An exception occured while loading."); Logging.LogFatal(string.Format("Plugin {0} failed to load! (Code: {1})", "com.JKLeckr.CupheadArchipelago", failCode)); string message = e.GetBaseException().Message; State = failCode; StateMessage = message; Logging.LogFatal("Exception: " + message); Logging.LogFatal("Throwing Exception..."); throw new Exception("Plugin com.JKLeckr.CupheadArchipelago: Exceptions occurred!", e); } } public static class ModPluginInfo { public const string PLUGIN_GUID = "com.JKLeckr.CupheadArchipelago"; public const string PLUGIN_NAME = "CupheadArchipelago"; public const string PLUGIN_VERSION = "0.2.2.7"; public const string PLUGIN_VERSION_SUFFIX = ""; public const ushort PLUGIN_VERSION_REL = 3; public const string PLUGIN_DIST = ""; } } namespace CupheadArchipelago.Util { public static class Aux { public static string CollectionToString(IEnumerable collection) { bool flag = true; StringBuilder stringBuilder = new StringBuilder("["); foreach (object item in collection) { string text = ((!flag) ? ", " : ""); if (flag) { flag = false; } stringBuilder.Append(text + item.ToString()); } stringBuilder.Append("]"); return stringBuilder.ToString(); } public static int ArrayNullCount(object[] arr) { int num = 0; foreach (object obj in arr) { if (obj == null) { num++; } } return num; } public static T[] ArrayRange<T>(T[] arr, int start, int end) { if (start >= end || start < 0 || end > arr.Length) { throw new IndexOutOfRangeException(); } T[] array = new T[end - start]; for (int i = 0; i < array.Length; i++) { array[i] = arr[start + i]; } return array; } public static T[] ArrayRange<T>(T[] arr, int end) { return ArrayRange(arr, 0, end); } public static bool IsAny<T>(T item, T[] values) { foreach (T val in values) { if (item.Equals(val)) { return true; } } return false; } public static void Shuffle<T>(this IList<T> list, Random rand = null) { if (rand == null) { rand = new Random(); } int num = list.Count; while (num > 1) { num--; int index = rand.Next(num + 1); T value = list[index]; list[index] = list[num]; list[num] = value; } } } internal static class Converter { private static class TypeConverter<T> { internal static readonly Func<object, T> ConvertTo; static TypeConverter() { Type T_Type = typeof(T); if ((object)T_Type == typeof(string)) { ConvertTo = (object value) => (T)(object)value.ToString(); } else if ((object)T_Type == typeof(bool)) { ConvertTo = (object value) => (T)(object)Convert.ToBoolean((long)value); } else if ((object)T_Type == typeof(sbyte)) { ConvertTo = (object value) => (T)(object)Convert.ToSByte(value); } else if ((object)T_Type == typeof(int)) { ConvertTo = (object value) => (T)(object)Convert.ToInt32(value); } else if (T_Type.IsEnum) { ConvertTo = (object value) => (T)Enum.Parse(T_Type, value.ToString()); } else { ConvertTo = (object value) => (T)value; } } } internal static T ConvertTo<T>(this object value) { return TypeConverter<T>.ConvertTo(value); } } public static class Ext { public static Levels[] LMapped(this Levels[] levels) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected I4, but got Unknown if (!LevelMap.IsInitted()) { Logging.LogWarning("LevelMap is not initted! Returning unmapped levels."); return levels; } Levels[] array = (Levels[])(object)new Levels[levels.Length]; for (int i = 0; i < array.Length; i++) { array[i] = (Levels)(int)LevelMap.GetMappedLevel(levels[i]); } return array; } public static bool CheckAnyLevelComplete(this Levels[] levels) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) PlayerData data = PlayerData.Data; foreach (Levels val in levels) { if (data.CheckLevelCompleted(val)) { return true; } } return false; } public static bool CheckLevelsComplete(this Levels[] levels) { if (levels.Length < 1) { return false; } return PlayerData.Data.CheckLevelsCompleted(levels); } } public static class Reflection { public static Type GetEnumeratorType(MethodBase enumerator) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown if ((object)enumerator == null) { throw new ArgumentNullException("GetEnumeratorType: Argument cannot be null!"); } Type type = null; MethodDefinition definition = new DynamicMethodDefinition(enumerator).Definition; ILContext val = new ILContext(definition); if (((MemberReference)((MethodReference)val.Method).ReturnType).Name.StartsWith("UniTask")) { VariableDefinition? obj = ((IEnumerable<VariableDefinition>)val.Body.Variables).FirstOrDefault(); TypeReference val2 = ((obj != null) ? ((VariableReference)obj).VariableType : null); if (val2 != null && !((MemberReference)val2).Name.Contains(enumerator.Name)) { Logging.LogWarning("GetEnumeratorType: First Var name invalid: " + ((MemberReference)val2).Name); return null; } type = ReflectionHelper.ResolveReflection(val2); } else { MethodReference ctor = null; ILCursor val3 = new ILCursor(val); val3.GotoNext(new Func<Instruction, bool>[1] { (Instruction i) => ILPatternMatchingExt.MatchNewobj(i, ref ctor) }); if (ctor == null || ((MemberReference)ctor).Name != ".ctor") { Logging.LogWarning("GetEnumeratorType: Invalid enumerator ctor: " + GeneralExtensions.FullDescription(enumerator)); } type = ReflectionHelper.ResolveReflection(((MemberReference)ctor).DeclaringType); } return type; } public static bool IsObsolete(this MemberInfo mi, bool inherit = false) { return mi.GetCustomAttributes(typeof(ObsoleteAttribute), inherit) == null; } } } namespace CupheadArchipelago.Unity { internal class APCore { public enum FontType { Bold, ExtraBold, Mono } public static readonly Color TEXT_COLOR = new Color(0.212f, 0.212f, 0.212f, 1f); public static readonly Color TEXT_SELECT_COLOR = new Color(0.676f, 0.212f, 0.212f, 1f); public static readonly Color TEXT_INACTIVE_COLOR = new Color(0.212f, 0.212f, 0.212f, 0.5f); public static Text CreateSettingsTextComponent(GameObject obj, FontType type = FontType.Bold, TextAnchor alignment = (TextAnchor)0, bool wrap = false) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) Text val = obj.AddComponent<Text>(); ((Graphic)val).color = TEXT_COLOR; Text val2 = val; if (1 == 0) { } Font font = (Font)(type switch { FontType.ExtraBold => FontLoader.GetFont((FontType)6), FontType.Mono => FontLoader.GetFont((FontType)19), _ => FontLoader.GetFont((FontType)5), }); if (1 == 0) { } val2.font = font; val.fontSize = 32; val.alignment = alignment; val.horizontalOverflow = (HorizontalWrapMode)(!wrap); return val; } } internal class APMain : MonoBehaviour { private static GameObject current; internal static void Create() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if ((Object)(object)current == (Object)null) { current = new GameObject("APMain", new Type[1] { typeof(APMain) }); } } private void Awake() { Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void OnDestroy() { Logging.Log("Shutting Down"); APClient.CloseArchipelagoSession(reset: false); } } public class APManager : MonoBehaviour { public enum MngrType { Normal, Level, SpecialLevel } [SerializeField] private bool debug = false; [SerializeField] private float levelApplyInterval = 1f; [SerializeField] private float mapApplyInterval = 0.025f; private bool init = false; private bool active = false; private MngrType type = MngrType.Normal; private float timer = 0f; [SerializeField] private bool deathLink = false; [SerializeField] private bool death = false; [SerializeField] private string deathMessage = ""; private bool deathExecuted = false; [SerializeField] private float fastFire = 0f; [SerializeField] private float fingerJam = 0f; [SerializeField] private float slowFire = 0f; public static APManager Current { get; private set; } public void Init(MngrType type) { Init(type, type == MngrType.Level); } public void Init(MngrType type, bool deathLink) { if (init) { return; } if ((Object)(object)Current != (Object)(object)this) { if ((Object)(object)Current != (Object)null) { Object.Destroy((Object)(object)Current); } Current = this; } Logging.Log($"[APManager] Initialized as Current {type}"); this.type = type; this.deathLink = deathLink; init = true; } public bool IsActive() { return active; } public void SetActive(bool active) { this.active = active; } public bool IsDeathTriggered() { return death; } public void TriggerDeath(string message = "Self") { if (type == MngrType.Level) { if (IsDeathTriggered()) { Logging.LogWarning("[APManager] Death already triggered!"); return; } death = true; deathMessage = message ?? "Unknown"; } } public bool IsFastFired() { return fastFire > 0f; } public void FastFire(float addTime = 5f) { if (fastFire < 0f) { fastFire = 0f; } fastFire += addTime; } public bool IsFingerJammed() { return fingerJam > 0f; } public void FingerJam(float addTime = 5f) { if (fingerJam < 0f) { fingerJam = 0f; } fingerJam += addTime; } public bool IsSlowFired() { return slowFire > 0f; } public void SlowFire(float addTime = 8f) { if (slowFire < 0f) { slowFire = 0f; } slowFire += addTime; } private void Update() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 if (!init) { return; } if ((Object)(object)Current != (Object)(object)this) { init = false; Object.Destroy((Object)(object)this); } else { if (!active || (int)PauseManager.state == 1) { return; } if (debug) { Logging.Log($"ReceiveQueue {APClient.ItemReceiveQueueCount()}"); } if (type == MngrType.Level && deathLink && death && !deathExecuted) { Logging.Log("[APManager] Killing Players."); PlayerStatsInterface.KillPlayer((PlayerId)2147483646); Logging.Log("[APManager] " + deathMessage); deathExecuted = true; active = false; return; } if (fastFire > 0f) { fastFire -= Time.deltaTime; if (fastFire < 0f) { fastFire = 0f; } } if (fingerJam > 0f) { fingerJam -= Time.deltaTime; if (fingerJam < 0f) { fingerJam = 0f; } } if (slowFire > 0f) { slowFire -= Time.deltaTime; if (slowFire < 0f) { slowFire = 0f; } } APClient.ItemUpdate(); MngrType mngrType = type; if (1 == 0) { } float num = (((uint)(mngrType - 1) > 1u) ? mapApplyInterval : levelApplyInterval); if (1 == 0) { } float num2 = num; if (type == MngrType.Level || type == MngrType.SpecialLevel) { if (debug) { Logging.Log($"ItemSpecialLevelQueue {APClient.ItemApplySpecialLevelQueueCount()}"); } if (!APClient.ItemApplySpecialLevelQueueIsEmpty()) { if (debug) { Logging.Log("ItemSpecialLevelQueue has item"); } long id = APClient.PeekItemApplySpecialLevelQueue().id; if (APClient.GetAppliedItemCount(id) >= APClient.GetReceivedItemCount(id)) { APClient.PopItemApplySpecialLevelQueue(applyItem: false); } else if (timer >= num2) { if (debug) { Logging.Log("ItemSpecialLevelQueue is applying"); } APClient.PopItemApplySpecialLevelQueue(); AudioManager.Play("level_coin_pickup"); timer = 0f; } } } if (type == MngrType.Level) { if (debug) { Logging.Log($"ItemLevelQueue {APClient.ItemApplyLevelQueueCount()}"); } if (!APClient.ItemApplyLevelQueueIsEmpty()) { if (debug) { Logging.Log("ItemLevelQueue has item"); } long id2 = APClient.PeekItemApplyLevelQueue().id; if (APClient.GetAppliedItemCount(id2) >= APClient.GetReceivedItemCount(id2)) { APClient.PopItemApplyLevelQueue(applyItem: false); } else if (timer >= num2) { if (debug) { Logging.Log("ItemLevelQueue is applying"); } APClient.PopItemApplyLevelQueue(); AudioManager.Play("level_coin_pickup"); timer = 0f; } } } if (debug) { Logging.Log($"ItemQueue {APClient.ItemApplyQueueCount()}"); } if (!APClient.ItemApplyQueueIsEmpty()) { if (debug) { Logging.Log("ItemQueue has item"); } long id3 = APClient.PeekItemApplyQueue().id; if (APClient.GetAppliedItemCount(id3) >= APClient.GetReceivedItemCount(id3)) { APClient.PopItemApplyQueue(applyItem: false); } else if (timer >= num2) { if (debug) { Logging.Log("ItemQueue is applying"); } APClient.PopItemApplyQueue(); AudioManager.Play("level_coin_pickup"); timer = 0f; } } if (timer < num2) { timer += Time.deltaTime; } } } private void OnDestroy() { Logging.Log("[APManager] Destroyed"); init = false; if (Current == this) { Current = null; } } } public class APSetupMenu : MonoBehaviour { private AnyPlayerInput input; private bool initted = false; private bool active = false; private int menuSelection = 0; private Text[] menuText; private Text headerText; private bool menuLocked; private bool promptCooldown = false; private Transform fader; private Transform prompts; private APTypingPrompt typingPrompt; private int slotSelection = 0; private APData apData; [SerializeField] private float menuDelay = 0.05f; private float menuTime = 0f; private static string[] setupFieldLabels = new string[5] { "ENABLED", "ADDRESS", "PORT", "PLAYER", "PASSWORD" }; private void Awake() { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown input = new AnyPlayerInput(false); menuTime = 0f; } private void Update() { if (!initted || !active) { return; } if (typingPrompt.IsActive()) { if (!typingPrompt.IsFinished()) { return; } switch (menuSelection) { case 1: { string text2 = typingPrompt.GetText(); if (text2.Length > 0) { apData.address = text2; break; } Logging.LogWarning("[APSetupMenu] Invalid text for address: cannot be empty."); apData.address = "archipelago.gg"; break; } case 2: try { ushort port = ushort.Parse(typingPrompt.GetText()); apData.port = port; } catch (Exception ex) { Logging.LogWarning("[APSetupMenu] Invalid text for port: " + ex.Message); apData.port = 38281; } break; case 3: { string text = typingPrompt.GetText(); if (text.Length > 0) { apData.player = typingPrompt.GetText(); break; } Logging.LogWarning("[APSetupMenu] Invalid text for player: cannot be empty."); apData.player = "Player"; break; } case 4: apData.password = typingPrompt.GetText(); break; } CloseTypingPrompt(); RefreshSettingsText(); return; } if (promptCooldown) { ((Component)prompts).gameObject.SetActive(true); promptCooldown = false; return; } if (menuSelection == 0 && !menuLocked) { if (input.GetButtonDown((CupheadButton)18) || input.GetButtonDown((CupheadButton)20)) { AudioManager.Play("level_menu_select"); apData.enabled = !apData.enabled; RefreshSettingsText(); } } else if (menuSelection == 1 && input.GetButtonDown((CupheadButton)13)) { AudioManager.Play("level_select"); OpenTypingPrompt(apData.address, APTypingPrompt.TextTypes.Text); } else if (menuSelection == 2 && input.GetButtonDown((CupheadButton)13)) { AudioManager.Play("level_select"); OpenTypingPrompt(apData.port.ToString(), APTypingPrompt.TextTypes.SixDigit); } else if (menuSelection == 3 && input.GetButtonDown((CupheadButton)13)) { AudioManager.Play("level_select"); OpenTypingPrompt(apData.player, APTypingPrompt.TextTypes.Text16); } else if (menuSelection == 4 && input.GetButtonDown((CupheadButton)13)) { AudioManager.Play("level_select"); OpenTypingPrompt(apData.password, APTypingPrompt.TextTypes.Text16); } if (menuTime >= menuDelay) { if (input.GetButtonDown((CupheadButton)16)) { menuTime = 0f; AudioManager.Play("level_menu_move"); menuSelection--; if (menuLocked && menuSelection == 0) { menuSelection = -1; } if (menuSelection < 0) { menuSelection = menuText.Length - 1; } SetSettingsTextColors(); } else if (input.GetButtonDown((CupheadButton)19)) { menuTime = 0f; AudioManager.Play("level_menu_move"); menuSelection++; if (menuSelection >= menuText.Length) { menuSelection = (menuLocked ? 1 : 0); } SetSettingsTextColors(); } } else { menuTime += Time.deltaTime; } } private void OpenTypingPrompt(string initial_str, APTypingPrompt.TextTypes type) { ((Component)prompts).gameObject.SetActive(false); promptCooldown = true; typingPrompt.OpenPrompt(initial_str, type); } private void CloseTypingPrompt() { ((Component)prompts).gameObject.SetActive(true); typingPrompt.ClosePrompt(); } private void SetSettingsTextColors() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < menuText.Length; i++) { if (i == 0 && menuLocked) { ((Graphic)menuText[i]).color = APCore.TEXT_INACTIVE_COLOR; } else { ((Graphic)menuText[i]).color = ((menuSelection == i) ? APCore.TEXT_SELECT_COLOR : APCore.TEXT_COLOR); } } } public bool IsBackSelected() { return menuSelection == 5; } public bool IsTyping() { return typingPrompt?.IsTyping() ?? false; } public void SetState(bool state) { ((Component)this).gameObject.SetActive(state); active = state; CloseTypingPrompt(); if (state) { RefreshMenu(); } menuSelection = (menuLocked ? 1 : 0); SetSettingsTextColors(); } public void SetSlotSelection(int slotSelection) { this.slotSelection = slotSelection; apData = APData.SData[slotSelection]; RefreshMenuLock(); } private void RefreshMenuLock() { menuLocked = !apData.IsEmpty(SaveDataType.Vanilla); } public bool IsInitted() { return initted; } public static void Init(APSetupMenu instance, Transform orig_options, Transform prompts) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected O, but got Unknown //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Expected O, but got Unknown //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Expected O, but got Unknown //IL_02a2: Unknown result type (might be due to invalid IL or missing references) GameObject gameObject = ((Component)instance).gameObject; Transform child = orig_options.GetChild(0); Transform child2 = orig_options.GetChild(1); Transform child3 = child2.GetChild(2); Transform child4 = child2.GetChild(6); instance.prompts = prompts; GameObject val = Object.Instantiate<GameObject>(((Component)child).gameObject, gameObject.transform); ((Object)val).name = ((Object)child).name; RectTransform component = val.GetComponent<RectTransform>(); component.sizeDelta = new Vector2(2570f, 1450f); instance.fader = val.transform; GameObject val2 = new GameObject("Card"); RectTransform val3 = val2.AddComponent<RectTransform>(); val3.sizeDelta = new Vector2(514f, 299f); val2.transform.SetParent(gameObject.transform); GameObject val4 = Object.Instantiate<GameObject>(((Component)child3).gameObject, val2.transform); ((Object)val4).name = ((Object)child3).name; val4.SetActive(true); GameObject val5 = new GameObject("Header"); RectTransform val6 = val5.AddComponent<RectTransform>(); val6.sizeDelta = new Vector2(600f, 64f); val6.anchoredPosition = new Vector2(0f, 190f); val5.transform.SetParent(val2.transform); val5.AddComponent<CanvasRenderer>(); Text val7 = APCore.CreateSettingsTextComponent(val5, APCore.FontType.Mono, (TextAnchor)1, wrap: true); val7.fontSize = 24; val7.text = "Seed: 000000000000000"; instance.headerText = val7; GameObject val8 = new GameObject("APMenu"); RectTransform val9 = val8.AddComponent<RectTransform>(); val8.AddComponent<HorizontalLayoutGroup>(); val9.sizeDelta = new Vector2(514f, 299f); val8.transform.SetParent(val2.transform); GameObject val10 = new GameObject("SettingsLabels"); RectTransform val11 = val10.AddComponent<RectTransform>(); val11.sizeDelta = new Vector2(514f, 259f); val10.AddComponent<VerticalLayoutGroup>(); val10.transform.SetParent(val8.transform); instance.SetupSettingsLabels(val10.transform); instance.menuText = (Text[])(object)new Text[6]; instance.menuLocked = false; GameObject val12 = new GameObject("SettingsTextContainer"); RectTransform val13 = val12.AddComponent<RectTransform>(); val13.sizeDelta = new Vector2(514f, 259f); VerticalLayoutGroup val14 = val12.AddComponent<VerticalLayoutGroup>(); RectOffset padding = ((LayoutGroup)val14).padding; padding.top += 2; val12.transform.SetParent(val8.transform); GameObject val15 = new GameObject("SettingsText"); RectTransform val16 = val15.AddComponent<RectTransform>(); val16.sizeDelta = new Vector2(514f, 259f); VerticalLayoutGroup val17 = val15.AddComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val17).spacing = ((HorizontalOrVerticalLayoutGroup)val17).spacing + 8f; val15.transform.SetParent(val12.transform); instance.SetupSettingsText(val15.transform); GameObject val18 = Object.Instantiate<GameObject>(((Component)child4).gameObject, val2.transform); ((Object)val18).name = ((Object)child4).name; val18.SetActive(true); instance.typingPrompt = APTypingPrompt.CreateTypingPrompt(val2.transform, orig_options); instance.RefreshMenu(); instance.initted = true; Logging.Log("APSetupMenu Initialized"); } private void RefreshMenu() { RefreshMenuLock(); SetSettingsTextColors(); RefreshSettingsText(); } private void RefreshSettingsText() { if ((Object)(object)headerText != (Object)null) { headerText.text = ((!menuLocked) ? "" : (apData.enabled ? ("Seed: " + GetAPSeed()) : "Vanilla Save\nDelete slot to enable Archipelago.")); } if ((Object)(object)menuText[0] != (Object)null) { menuText[0].text = (apData.enabled ? "YES" : "NO") + " " + (menuLocked ? "(Locked)" : ""); } if ((Object)(object)menuText[1] != (Object)null) { menuText[1].text = "[" + GetMenuString(apData.address) + "]"; } if ((Object)(object)menuText[2] != (Object)null) { menuText[2].text = $"[{apData.port}]"; } if ((Object)(object)menuText[3] != (Object)null) { menuText[3].text = "[" + GetMenuString(apData.player) + "]"; } if ((Object)(object)menuText[4] != (Object)null) { string text = new string('*', Mathf.Min(apData.password?.Length ?? 0, 16)); menuText[4].text = "[" + text + "]"; } } private string GetAPSeed() { APData aPData = APData.SData[slotSelection]; return (!aPData.IsEmpty(SaveDataType.Vanilla)) ? aPData.seed : ""; } private static string GetMenuString(string str) { if (str.Length > 14) { return str.Substring(0, 11) + "..."; } return str; } private void SetupSettingsText(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown //IL_0220: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Enabled"); RectTransform val2 = val.AddComponent<RectTransform>(); val2.sizeDelta = new Vector2(600f, 36f); val.transform.SetParent(((Component)parent).transform); val.AddComponent<CanvasRenderer>(); Text val3 = CreateSettingsTextComponent(val, bold: false, (TextAnchor)0); val3.text = "ON"; menuText[0] = val3; GameObject val4 = new GameObject("Address"); RectTransform val5 = val4.AddComponent<RectTransform>(); val5.sizeDelta = new Vector2(600f, 40f); val4.transform.SetParent(((Component)parent).transform); val4.AddComponent<CanvasRenderer>(); Text val6 = CreateSettingsTextComponent(val4, bold: false, (TextAnchor)0); val6.text = "[ARCHIPELAGOGGG]"; menuText[1] = val6; GameObject val7 = new GameObject("Port"); RectTransform val8 = val7.AddComponent<RectTransform>(); val8.s
BepInEx/plugins/FVerParser.dll
Decompiled a day agousing System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyCompany("FVerParser")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+bfbb14c69c183d93e4986aa6b388a58a603c0a9d")] [assembly: AssemblyProduct("FVerParser")] [assembly: AssemblyTitle("FVerParser")] [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 CupheadArchipelago.Helpers.FVerParser { public class FVerParse { public static RawFVer GetRawFVer(string ver, ushort rel = 0) { string[] array = ver.Split(new char[2] { '.', '-' }, 5); if (int.Parse(array[0]) > 0) { throw new Exception("Version parsing system needs to be changed for main version!"); } int num = int.Parse(array[1]); if (1 == 0) { } string text = num switch { 1 => "preview", 2 => "alpha", 3 => "beta", 4 => "rc", _ => "unknown", }; if (1 == 0) { } string prefix = text; int baseline = int.Parse(array[2]) + 1; int revision = int.Parse(array[3]); string postfix = ((array.Length > 4) ? array[4] : ""); return new RawFVer(baseline, revision, rel, prefix, postfix); } } public class RawFVer(int baseline, int revision, int release, string prefix, string postfix) { public readonly int baseline = baseline; public readonly int revision = revision; public readonly int release = release; public readonly string prefix = prefix; public readonly string postfix = postfix; } }
BepInEx/plugins/Newtonsoft.Json.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
#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/websocket-sharp.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net.Security; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.Win32.SafeHandles; using WebSocketSharp.Native; using WebSocketSharp.Net; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyCompany("websocket-sharp")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyCopyright("Copyright 2026 JKLeckr")] [assembly: AssemblyDescription("A native C# wrapper for websocket-sharp")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1.0+8c66cd9f6ccbb12b197b4116d31a840b99cfdbba")] [assembly: AssemblyProduct("websocket-sharp")] [assembly: AssemblyTitle("websocket-sharp")] [assembly: AssemblyVersion("0.1.1.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 WebSocketSharp { public class CloseEventArgs : EventArgs { private readonly bool _clean; private readonly ushort _code; private readonly string _reason; public ushort Code => _code; public string Reason => _reason; public bool WasClean => _clean; internal CloseEventArgs(ushort code, string reason, bool clean) { _code = code; _reason = reason; _clean = clean; } } public class ErrorEventArgs : EventArgs { private readonly Exception _exception; private readonly 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 ?? string.Empty; _exception = exception; } } public static class Logging { public enum NativeLogLevel { Off, Error, Warn, Info, Debug, Trace } public delegate void NativeLogHandler(NativeLogLevel level, string message); private const string TraceEnvironmentVariable = "NWS_LOGGING"; private const string TraceFileEnvironmentVariable = "NWS_LOG_FILE"; private const string TraceMarkerFileName = "nativews.log.enable"; private const string DefaultTraceFileName = "native-websocket-sharp.log"; private static readonly object Sync = new object(); private static readonly NativeLogCallback NativeLogBridge = HandleNativeLog; private static NativeLogHandler _nativeLogger; private static NativeLogLevel _nativeLogVerbosity = NativeLogLevel.Off; private static bool _initialized; private static bool _nativeLoggingSupported; private static string _traceFilePath; internal static NativeLogHandler NativeLogger { get { lock (Sync) { return _nativeLogger; } } set { lock (Sync) { EnsureInitializedLocked(); _nativeLogger = value; ApplyNativeLoggingLocked(); } } } internal static NativeLogLevel NativeLogVerbosity { get { lock (Sync) { return _nativeLogVerbosity; } } set { lock (Sync) { EnsureInitializedLocked(); _nativeLogVerbosity = value; ApplyNativeLoggingLocked(); } } } internal static bool NativeLoggingSupported { get { lock (Sync) { return _nativeLoggingSupported; } } } internal static void EnsureInitialized() { lock (Sync) { EnsureInitializedLocked(); } } internal static void Write(int socketId, string message) { Write("managed", socketId, message); } private static void EnsureInitializedLocked() { if (_initialized) { return; } _initialized = true; if (IsTraceEnabled()) { _traceFilePath = ResolveTraceFilePath(); if (_nativeLogger == null) { _nativeLogger = WriteNativeLog; } if (_nativeLogVerbosity == NativeLogLevel.Off) { _nativeLogVerbosity = NativeLogLevel.Trace; } Write("managed", 0, "trace enabled file=" + _traceFilePath); } ApplyNativeLoggingLocked(); } private static void ApplyNativeLoggingLocked() { try { WebSocketInterop.SetLogLevel((int)_nativeLogVerbosity); WebSocketInterop.SetLogHandler((_nativeLogger == null) ? null : NativeLogBridge); _nativeLoggingSupported = true; } catch (Exception ex) { _nativeLoggingSupported = false; Write("managed", 0, "native logging unavailable: " + ex.GetType().Name + ": " + ex.Message); } } private static bool IsTraceEnabled() { string environmentVariable = Environment.GetEnvironmentVariable("NWS_LOGGING"); if (!string.IsNullOrEmpty(environmentVariable) && !string.Equals(environmentVariable, "0", StringComparison.OrdinalIgnoreCase) && !string.Equals(environmentVariable, "false", StringComparison.OrdinalIgnoreCase)) { return true; } return File.Exists(Path.Combine(GetBaseDirectory(), "nativews.log.enable")) || File.Exists(Path.Combine(Environment.CurrentDirectory, "nativews.log.enable")); } private static string ResolveTraceFilePath() { string environmentVariable = Environment.GetEnvironmentVariable("NWS_LOG_FILE"); if (!string.IsNullOrEmpty(environmentVariable)) { return environmentVariable; } return Path.Combine(GetBaseDirectory(), "native-websocket-sharp.log"); } private static string GetBaseDirectory() { string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; return string.IsNullOrEmpty(baseDirectory) ? Environment.CurrentDirectory : baseDirectory; } private static void HandleNativeLog(int level, IntPtr message) { NativeLogHandler nativeLogger; lock (Sync) { nativeLogger = _nativeLogger; } if (nativeLogger == null) { return; } try { string text = ((message == IntPtr.Zero) ? string.Empty : Marshal.PtrToStringAnsi(message)); nativeLogger(ClampLogLevel(level), text ?? string.Empty); } catch { } } private static NativeLogLevel ClampLogLevel(int level) { if (level <= 0) { return NativeLogLevel.Off; } if (level >= 5) { return NativeLogLevel.Trace; } return (NativeLogLevel)level; } private static void WriteNativeLog(NativeLogLevel level, string message) { Write("native/" + level, 0, message); } private static void Write(string source, int socketId, string message) { string traceFilePath; lock (Sync) { if (!_initialized) { EnsureInitializedLocked(); } traceFilePath = _traceFilePath; } if (string.IsNullOrEmpty(traceFilePath)) { return; } string text = ((socketId == 0) ? "-" : socketId.ToString()); string text2 = $"{DateTime.UtcNow:O} [{source}] [ws {text}] [thread {Thread.CurrentThread.ManagedThreadId}] {message ?? string.Empty}"; try { lock (Sync) { File.AppendAllText(traceFilePath, text2 + Environment.NewLine); } } catch { } } } public class MessageEventArgs : EventArgs { private readonly string _data; private readonly Opcode _opcode; private readonly byte[] _rawData; internal Opcode Opcode => _opcode; public string Data => _data; public bool IsBinary => _opcode == Opcode.Binary; public bool IsPing => _opcode == Opcode.Ping; public bool IsText => _opcode == Opcode.Text; public byte[] RawData => _rawData; internal MessageEventArgs(string data) { _data = data; _rawData = null; _opcode = Opcode.Text; } internal MessageEventArgs(Opcode opcode, byte[] rawData) { _opcode = opcode; _rawData = rawData; } } internal enum Opcode : byte { Cont = 0, Text = 1, Binary = 2, Close = 8, Ping = 9, Pong = 10 } public class WebSocket : IDisposable { private static readonly byte[] EmptyBytes = new byte[0]; private static readonly TimeSpan DefaultWaitTime = TimeSpan.FromSeconds(5.0); private static readonly TimeSpan PingCacheWindow = TimeSpan.FromSeconds(1.0); private static readonly TimeSpan PingTimeout = TimeSpan.FromSeconds(5.0); private static int _lastId; private readonly ManualResetEvent _closeCompleted; private readonly ManualResetEvent _connectCompleted; private readonly object _forMessageEventQueue; private readonly object _forPing; private readonly object _forSend; private readonly object _forState; private readonly Queue<MessageEventArgs> _messageEventQueue; private readonly ManualResetEvent _openEventCompleted; private readonly ManualResetEvent _pongReceived; private NativeWebSocketHandle _nativeClient; private DateTime _lastPongUtc; private Thread _pollThread; private bool _pollThreadStarted; private volatile WebSocketState _readyState; private readonly bool _secure; private ClientSslConfiguration _sslConfiguration; private readonly Uri _uri; private bool _disposed; private bool _connectSucceeded; private bool _closeReported; private bool _openEventPending; private bool _messageDispatching; private readonly int _id; private TimeSpan _waitTime; public bool IsAlive => ping(EmptyBytes); public bool IsSecure => _secure; public WebSocketState ReadyState => _readyState; public ClientSslConfiguration SslConfiguration { get { if (!_secure) { throw new InvalidOperationException("This instance does not use a secure connection."); } return _sslConfiguration ?? (_sslConfiguration = new ClientSslConfiguration(_uri.DnsSafeHost)); } } public Uri Url => _uri; public static Logging.NativeLogHandler NativeLogger { get { return Logging.NativeLogger; } set { Logging.NativeLogger = value; } } public static Logging.NativeLogLevel NativeLogVerbosity { get { return Logging.NativeLogVerbosity; } set { Logging.NativeLogVerbosity = value; } } public static bool NativeLoggingSupported => Logging.NativeLoggingSupported; public TimeSpan WaitTime { get { return _waitTime; } set { if (value <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException("value", "Zero or less."); } lock (_forState) { if (_readyState == WebSocketState.Closed) { _waitTime = value; } } } } public event EventHandler<CloseEventArgs> OnClose; public event EventHandler<ErrorEventArgs> OnError; public event EventHandler<MessageEventArgs> OnMessage; public event EventHandler OnOpen; public WebSocket(string url, params string[] protocols) { _id = Interlocked.Increment(ref _lastId); Logging.EnsureInitialized(); log_trace("constructor begin url=" + (url ?? "<null>")); if (url == null) { throw new ArgumentNullException("url"); } if (url.Length == 0) { throw new ArgumentException("An empty string.", "url"); } if (!TryCreateWebSocketUri(url, out _uri, out var message)) { throw new ArgumentException(message, "url"); } if (protocols != null && protocols.Length != 0 && !CheckProtocols(protocols, out var message2)) { throw new ArgumentException(message2, "protocols"); } _secure = string.Equals(_uri.Scheme, "wss", StringComparison.OrdinalIgnoreCase); _readyState = WebSocketState.Closed; _closeCompleted = new ManualResetEvent(initialState: true); _connectCompleted = new ManualResetEvent(initialState: false); _messageEventQueue = new Queue<MessageEventArgs>(); _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot; _openEventCompleted = new ManualResetEvent(initialState: true); _forPing = new object(); _forSend = new object(); _forState = new object(); _pongReceived = new ManualResetEvent(initialState: false); _lastPongUtc = DateTime.MinValue; _waitTime = DefaultWaitTime; NativeResult nativeResult = WebSocketInterop.Create(_uri.ToString(), out _nativeClient); if (nativeResult != NativeResult.Ok || _nativeClient == null || _nativeClient.IsInvalid) { log_trace("constructor native create failed result=" + nativeResult); throw new InvalidOperationException("The native websocket client could not be created."); } log_trace("constructor complete secure=" + _secure + " state=" + _readyState); } public void Close() { close(1005, string.Empty); } public void Close(ushort code) { ValidateCloseCode(code); close(code, string.Empty); } public void Close(ushort code, string reason) { ValidateCloseCode(code); ValidateCloseReason(code, reason); close(code, reason ?? string.Empty); } public void CloseAsync() { closeAsync(1005, string.Empty); } public void CloseAsync(ushort code) { ValidateCloseCode(code); closeAsync(code, string.Empty); } public void CloseAsync(ushort code, string reason) { ValidateCloseCode(code); ValidateCloseReason(code, reason); closeAsync(code, reason ?? string.Empty); } public void Connect() { ThrowIfDisposed(); if (!connect()) { } } public void ConnectAsync() { ThrowIfDisposed(); ValidateConnectStart(); QueueBackground(delegate { try { Connect(); } catch { } }); } public bool Ping() { return ping(EmptyBytes); } public bool Ping(string message) { if (string.IsNullOrEmpty(message)) { return ping(EmptyBytes); } byte[] bytes; try { bytes = Encoding.UTF8.GetBytes(message); } catch (Exception) { throw new ArgumentException("It could not be UTF-8-encoded.", "message"); } if (bytes.Length > 125) { throw new ArgumentOutOfRangeException("message", "Its size is greater than 125 bytes."); } return ping(bytes); } public void Send(string data) { if (data == null) { throw new ArgumentNullException("data"); } byte[] bytes; try { bytes = Encoding.UTF8.GetBytes(data); } catch (Exception) { throw new ArgumentException("It could not be UTF-8-encoded.", "data"); } send(bytes, isBinary: false); } public void SendAsync(string data, Action<bool> completed) { if (data == null) { throw new ArgumentNullException("data"); } byte[] bytes; try { bytes = Encoding.UTF8.GetBytes(data); } catch (Exception) { throw new ArgumentException("It could not be UTF-8-encoded.", "data"); } ValidateSendState(); QueueBackground(delegate { bool flag = false; try { send(bytes, isBinary: false); flag = true; } catch { flag = false; } completed?.Invoke(flag); }); } public void Dispose() { log_trace("Dispose begin state=" + _readyState.ToString() + " disposed=" + _disposed); Dispose(disposing: true); GC.SuppressFinalize(this); log_trace("Dispose end state=" + _readyState.ToString() + " disposed=" + _disposed); } protected virtual void Dispose(bool disposing) { if (_disposed) { log_trace("Dispose(" + disposing + ") ignored; already disposed"); return; } if (!disposing) { _disposed = true; return; } try { if (_readyState == WebSocketState.Open || _readyState == WebSocketState.Connecting) { log_trace("Dispose closing active socket state=" + _readyState); close(1001, string.Empty); } else if (_readyState == WebSocketState.Closing && !_closeCompleted.WaitOne(_waitTime)) { log_trace("Dispose force closing timed out closing socket"); forceClose(1001, string.Empty); } } catch (Exception ex) { log_trace("Dispose swallowed close exception " + ex.GetType().Name + ": " + ex.Message); } _disposed = true; Thread thread = null; lock (_forState) { thread = _pollThread; } if (thread != null && thread.IsAlive && thread != Thread.CurrentThread) { log_trace("Dispose joining poll thread"); thread.Join(1000); log_trace("Dispose poll thread join complete alive=" + thread.IsAlive); } destroyNativeClient(); } protected virtual void RaiseOnOpen() { this.OnOpen?.Invoke(this, EventArgs.Empty); } protected virtual void RaiseOnClose(CloseEventArgs e) { this.OnClose?.Invoke(this, e); } protected virtual void RaiseOnError(ErrorEventArgs e) { this.OnError?.Invoke(this, e); } protected virtual void RaiseOnMessage(MessageEventArgs e) { this.OnMessage?.Invoke(this, e); } private void clearMessageEventQueue() { lock (_forMessageEventQueue) { _messageEventQueue.Clear(); _messageDispatching = false; } } private void dispatchMessageEvents() { while (true) { MessageEventArgs e; lock (_forMessageEventQueue) { if (_openEventPending || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { if (_readyState != WebSocketState.Open) { _messageEventQueue.Clear(); } _messageDispatching = false; break; } e = _messageEventQueue.Dequeue(); } log_trace("raising OnMessage"); try { RaiseOnMessage(e); } catch (Exception ex) { log_trace("OnMessage threw " + ex.GetType().Name + ": " + ex.Message); raiseOnErrorSafely(new ErrorEventArgs("An error has occurred during an OnMessage event.", ex)); } log_trace("OnMessage returned"); } } private void dispatchOpenEvent() { try { log_trace("raising OnOpen"); try { RaiseOnOpen(); } catch (Exception ex) { log_trace("OnOpen threw " + ex.GetType().Name + ": " + ex.Message); raiseOnErrorSafely(new ErrorEventArgs("An error has occurred during the OnOpen event.", ex)); } log_trace("OnOpen returned"); bool flag = false; lock (_forMessageEventQueue) { _openEventPending = false; if (!_messageDispatching && _messageEventQueue.Count != 0 && _readyState == WebSocketState.Open) { _messageDispatching = true; flag = true; } } if (flag) { log_trace("starting OnMessage dispatcher"); QueueBackground(dispatchMessageEvents); } } finally { _openEventCompleted.Set(); } } private void enqueueMessageEvent(MessageEventArgs e) { bool flag = false; lock (_forMessageEventQueue) { _messageEventQueue.Enqueue(e); if (!_openEventPending && !_messageDispatching && _readyState == WebSocketState.Open) { _messageDispatching = true; flag = true; } } if (flag) { log_trace("starting OnMessage dispatcher"); QueueBackground(dispatchMessageEvents); } } private void raiseOnCloseSafely(CloseEventArgs e) { try { RaiseOnClose(e); } catch (Exception ex) { log_trace("OnClose threw " + ex.GetType().Name + ": " + ex.Message); } } private void raiseOnErrorSafely(ErrorEventArgs e) { try { RaiseOnError(e); } catch (Exception ex) { log_trace("OnError threw " + ex.GetType().Name + ": " + ex.Message); } } private static bool CheckProtocols(string[] protocols, out string message) { message = null; for (int i = 0; i < protocols.Length; i++) { string text = protocols[i]; if (string.IsNullOrEmpty(text) || !IsToken(text)) { message = "It contains a value that is not a token."; return false; } for (int j = i + 1; j < protocols.Length; j++) { if (protocols[j] == text) { message = "It contains a value twice."; return false; } } } return true; } private static bool IsCloseStatusCode(ushort code) { return code > 999 && code < 5000; } private static bool IsToken(string value) { foreach (char c in value) { if (c < ' ' || c > '~') { return false; } switch (c) { case '\t': case ' ': case '"': case '(': case ')': case ',': case '/': case ':': case ';': case '<': case '=': case '>': case '?': case '@': case '[': case '\\': case ']': case '{': case '}': return false; } } return true; } private static bool TryCreateWebSocketUri(string uriString, out Uri result, out string message) { result = null; message = null; Uri.TryCreate(uriString, UriKind.Absolute, out Uri result2); if (result2 == null) { message = "An invalid URI string."; return false; } if (!result2.IsAbsoluteUri) { message = "A relative URI."; return false; } string scheme = result2.Scheme; if (scheme != "ws" && scheme != "wss") { message = "The scheme part is not 'ws' or 'wss'."; return false; } if (result2.Port == 0) { message = "The port part is zero."; return false; } if (result2.Fragment.Length > 0) { message = "It includes the fragment component."; return false; } if (result2.Port != -1) { result = result2; return true; } result = new Uri(string.Format("{0}://{1}:{2}{3}", scheme, result2.Host, (scheme == "ws") ? 80 : 443, result2.PathAndQuery)); return true; } private static void QueueBackground(ThreadStart action) { ThreadPool.QueueUserWorkItem(delegate { action(); }); } private void log_trace(string message) { Logging.Write(_id, message); } private void close(ushort code, string reason) { ThrowIfDisposed(); log_trace("close begin code=" + code + " reasonLength=" + (reason?.Length ?? 0) + " state=" + _readyState); if (_readyState == WebSocketState.Closed) { log_trace("close ignored; already closed"); return; } if (_readyState == WebSocketState.Closing) { log_trace("close ignored; already closing"); return; } NativeResult nativeResult; lock (_forState) { if (_readyState == WebSocketState.Closed || _readyState == WebSocketState.Closing) { return; } _closeCompleted.Reset(); _readyState = WebSocketState.Closing; startPollingLoop(); NativeWebSocketHandle nativeClient = getNativeClient(); nativeResult = ((nativeClient == null) ? NativeResult.Disposed : WebSocketInterop.Close(nativeClient, reason, code)); log_trace("native close result=" + nativeResult); } switch (nativeResult) { case NativeResult.Ok: if (!_closeCompleted.WaitOne(_waitTime)) { log_trace("close timed out after " + _waitTime.TotalMilliseconds + "ms; forcing close"); forceClose(code, reason ?? string.Empty); } log_trace("close complete state=" + _readyState); break; default: if (nativeResult != NativeResult.Disposed) { log_trace("close throwing result=" + nativeResult); throw CreateCommandException(nativeResult, "An error has occurred while attempting to close."); } goto case NativeResult.InvalidState; case NativeResult.InvalidState: log_trace("close finalizing after native result=" + nativeResult); finalizeClosedState(new CloseEventArgs(code, reason ?? string.Empty, clean: false), raiseEvent: false); break; } } private void closeAsync(ushort code, string reason) { ThrowIfDisposed(); QueueBackground(delegate { try { close(code, reason); } catch { } }); } private bool connect() { if (_readyState == WebSocketState.Open) { log_trace("connect ignored; already open"); return true; } ValidateConnectStart(); log_trace("connect begin state=" + _readyState.ToString() + " uri=" + _uri); NativeResult nativeResult; lock (_forState) { _connectSucceeded = false; _connectCompleted.Reset(); _closeCompleted.Reset(); _closeReported = false; _readyState = WebSocketState.Connecting; startPollingLoop(); NativeWebSocketHandle nativeClient = getNativeClient(); nativeResult = ((nativeClient == null) ? NativeResult.Disposed : WebSocketInterop.Connect(nativeClient)); log_trace("native connect result=" + nativeResult); } if (nativeResult != NativeResult.Ok) { _readyState = WebSocketState.Closed; log_trace("connect throwing native result=" + nativeResult); throw CreateCommandException(nativeResult, "An error has occurred while attempting to connect."); } log_trace("connect waiting for native open/error"); _connectCompleted.WaitOne(); if (_connectSucceeded) { _openEventCompleted.WaitOne(); } log_trace("connect wait complete succeeded=" + _connectSucceeded + " state=" + _readyState); return _connectSucceeded; } private void destroyNativeClient() { NativeWebSocketHandle nativeWebSocketHandle = null; lock (_forState) { if (_nativeClient == null) { log_trace("destroyNativeClient ignored; no native handle"); return; } nativeWebSocketHandle = _nativeClient; _nativeClient = null; } log_trace("destroyNativeClient disposing native handle"); nativeWebSocketHandle.Dispose(); } private void finalizeClosedState(CloseEventArgs closeEvent, bool raiseEvent) { bool flag = false; lock (_forState) { _readyState = WebSocketState.Closed; _connectSucceeded = false; _connectCompleted.Set(); _closeCompleted.Set(); _lastPongUtc = DateTime.MinValue; if (raiseEvent && !_closeReported) { _closeReported = true; flag = true; } } clearMessageEventQueue(); log_trace("finalizeClosedState code=" + closeEvent.Code + " wasClean=" + closeEvent.WasClean + " raise=" + flag); if (flag) { log_trace("raising OnClose"); raiseOnCloseSafely(closeEvent); log_trace("OnClose returned"); } } private void forceClose(ushort code, string reason) { log_trace("forceClose code=" + code + " reasonLength=" + (reason?.Length ?? 0)); abortNativeClient(code, reason); finalizeClosedState(new CloseEventArgs(code, reason ?? string.Empty, clean: false), raiseEvent: true); } private void abortNativeClient(ushort code, string reason) { NativeWebSocketHandle nativeClient; lock (_forState) { nativeClient = getNativeClient(); } if (nativeClient != null) { log_trace("native abort result=" + WebSocketInterop.Abort(nativeClient, reason, code)); } else { log_trace("native abort skipped; no handle"); } } private NativeWebSocketHandle getNativeClient() { NativeWebSocketHandle nativeClient = _nativeClient; return (nativeClient == null || nativeClient.IsInvalid || nativeClient.IsClosed) ? null : nativeClient; } private bool hasNativeClient() { return getNativeClient() != null; } private Exception CreateCommandException(NativeResult result, string defaultMessage) { if (1 == 0) { } Exception result2 = result switch { NativeResult.InvalidState => new InvalidOperationException(defaultMessage), NativeResult.NotOpen => new InvalidOperationException("The current state of the connection is not Open."), NativeResult.Disposed => new ObjectDisposedException(GetType().FullName), NativeResult.InvalidArgument => new ArgumentException(defaultMessage), NativeResult.Timeout => new TimeoutException(defaultMessage), _ => new InvalidOperationException(defaultMessage), }; if (1 == 0) { } return result2; } private Exception CreateErrorException(NativeErrorKind kind, string message) { if (1 == 0) { } Exception result; switch (kind) { case NativeErrorKind.Timeout: result = new TimeoutException(message); break; case NativeErrorKind.TlsFailed: result = new AuthenticationException(message); break; case NativeErrorKind.ConnectFailed: case NativeErrorKind.Io: result = new IOException(message); break; default: result = new InvalidOperationException(message); break; } if (1 == 0) { } return result; } private void handleNativeClose(NativeEvent nativeEvent) { string text = decodeString(nativeEvent.Data); log_trace("native event close code=" + nativeEvent.CloseCode + " wasClean=" + nativeEvent.CloseWasClean + " reasonLength=" + text.Length); finalizeClosedState(new CloseEventArgs(nativeEvent.CloseCode, text, nativeEvent.CloseWasClean), raiseEvent: true); } private void handleNativeError(NativeEvent nativeEvent) { string text = decodeString(nativeEvent.Data); Exception exception = CreateErrorException(nativeEvent.ErrorKind, text); log_trace("native event error kind=" + nativeEvent.ErrorKind.ToString() + " state=" + _readyState.ToString() + " message=" + text); lock (_forState) { if (_readyState == WebSocketState.Connecting) { _readyState = WebSocketState.Closed; _connectSucceeded = false; _connectCompleted.Set(); _closeCompleted.Set(); } } log_trace("raising OnError"); raiseOnErrorSafely(new ErrorEventArgs(text, exception)); log_trace("OnError returned"); } private void handleNativeEvent(NativeEvent nativeEvent) { log_trace("handleNativeEvent kind=" + nativeEvent.Kind.ToString() + " state=" + _readyState); switch (nativeEvent.Kind) { case NativeEventKind.Open: _openEventCompleted.Reset(); lock (_forMessageEventQueue) { _openEventPending = true; } lock (_forState) { _readyState = WebSocketState.Open; _connectSucceeded = true; _connectCompleted.Set(); } log_trace("native event open; starting OnOpen dispatcher"); QueueBackground(dispatchOpenEvent); break; case NativeEventKind.Close: handleNativeClose(nativeEvent); break; case NativeEventKind.Message: if (nativeEvent.MessageKind == NativeMessageKind.Text) { log_trace("native event text message bytes=" + ((nativeEvent.Data != null) ? nativeEvent.Data.Length : 0)); enqueueMessageEvent(new MessageEventArgs(decodeString(nativeEvent.Data))); } else { log_trace("native event binary message bytes=" + ((nativeEvent.Data != null) ? nativeEvent.Data.Length : 0)); enqueueMessageEvent(new MessageEventArgs(Opcode.Binary, nativeEvent.Data ?? new byte[0])); } break; case NativeEventKind.Error: handleNativeError(nativeEvent); break; case NativeEventKind.Pong: log_trace("native event pong bytes=" + ((nativeEvent.Data != null) ? nativeEvent.Data.Length : 0)); _lastPongUtc = DateTime.UtcNow; _pongReceived.Set(); break; } } private bool hasRecentPong() { DateTime lastPongUtc = _lastPongUtc; return lastPongUtc != DateTime.MinValue && DateTime.UtcNow - lastPongUtc <= PingCacheWindow; } private bool ping(byte[] payload) { ThrowIfDisposed(); log_trace("ping begin bytes=" + ((payload != null) ? payload.Length : 0) + " state=" + _readyState); if (_readyState != WebSocketState.Open) { log_trace("ping false; state=" + _readyState); return false; } if (hasRecentPong()) { log_trace("ping true; recent pong"); return true; } lock (_forPing) { if (_readyState != WebSocketState.Open) { return false; } if (hasRecentPong()) { return true; } _pongReceived.Reset(); NativeWebSocketHandle nativeClient = getNativeClient(); if (nativeClient == null) { log_trace("ping false; no native handle"); return false; } NativeResult nativeResult = WebSocketInterop.Ping(nativeClient, payload); if (nativeResult != NativeResult.Ok) { log_trace("native ping result=" + nativeResult); return false; } bool result = _pongReceived.WaitOne(PingTimeout); log_trace("ping wait complete pong=" + result); return result; } } private void pollLoop() { log_trace("pollLoop start"); try { do { NativeWebSocketHandle nativeClient = getNativeClient(); if (nativeClient == null) { log_trace("pollLoop exit; no native handle"); return; } NativeEvent nativeEvent; NativeResult nativeResult = WebSocketInterop.PollEvent(nativeClient, 50, out nativeEvent); switch (nativeResult) { case NativeResult.Ok: log_trace("native poll event kind=" + nativeEvent.Kind); handleNativeEvent(nativeEvent); break; default: log_trace("native poll failure result=" + nativeResult); handlePollFailure(nativeResult); return; case NativeResult.Timeout: break; } } while (_readyState != WebSocketState.Closed); log_trace("pollLoop exit; ready state closed"); } finally { log_trace("pollLoop finally"); lock (_forState) { _pollThreadStarted = false; _pollThread = null; } if (_readyState == WebSocketState.Closed) { destroyNativeClient(); } } } private void handlePollFailure(NativeResult result) { Exception ex = CreateCommandException(result, "The native websocket poller failed."); string message = ex.Message; log_trace("handlePollFailure result=" + result.ToString() + " message=" + message); raiseOnErrorSafely(new ErrorEventArgs(message, ex)); finalizeClosedState(new CloseEventArgs(1006, string.Empty, clean: false), raiseEvent: true); } private void send(byte[] data, bool isBinary) { ThrowIfDisposed(); ValidateSendState(); log_trace("send begin kind=" + (isBinary ? "binary" : "text") + " bytes=" + ((data != null) ? data.Length : 0) + " state=" + _readyState); lock (_forSend) { ValidateSendState(); NativeWebSocketHandle client = getNativeClient() ?? throw new ObjectDisposedException(GetType().FullName); NativeResult nativeResult = (isBinary ? WebSocketInterop.SendBinary(client, data) : WebSocketInterop.SendText(client, data)); log_trace("native send result=" + nativeResult); if (nativeResult != NativeResult.Ok) { throw CreateCommandException(nativeResult, "The message could not be sent."); } } } private string decodeString(byte[] data) { if (data == null || data.Length == 0) { return string.Empty; } return Encoding.UTF8.GetString(data); } private void startPollingLoop() { lock (_forState) { if (_pollThreadStarted || !hasNativeClient()) { log_trace("startPollingLoop skipped started=" + _pollThreadStarted + " hasClient=" + hasNativeClient()); return; } Thread thread = (_pollThread = new Thread(pollLoop) { IsBackground = true, Name = "websocket-sharp-native-poll" }); _pollThreadStarted = true; thread.Start(); log_trace("startPollingLoop started thread"); } } private void ThrowIfDisposed() { if (_disposed) { throw new ObjectDisposedException(GetType().FullName); } } private void ValidateCloseCode(ushort code) { if (!IsCloseStatusCode(code)) { throw new ArgumentOutOfRangeException("code", "Less than 1000 or greater than 4999."); } if (code == 1011) { throw new ArgumentException("1011 cannot be used.", "code"); } } private void ValidateCloseReason(ushort code, string reason) { if (!string.IsNullOrEmpty(reason)) { if (code == 1005) { throw new ArgumentException("1005 cannot be used.", "code"); } byte[] bytes; try { bytes = Encoding.UTF8.GetBytes(reason); } catch (Exception) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { throw new ArgumentOutOfRangeException("reason", "Its size is greater than 123 bytes."); } } } private void ValidateConnectStart() { if (_readyState == WebSocketState.Closing) { throw new InvalidOperationException("The close process is in progress."); } if (_readyState == WebSocketState.Connecting) { throw new InvalidOperationException("The connection is already in progress."); } } private void ValidateSendState() { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (!hasNativeClient()) { throw new ObjectDisposedException(GetType().FullName); } } } public enum WebSocketState : ushort { Connecting, Open, Closing, Closed } } namespace WebSocketSharp.Net { public class ClientSslConfiguration { private bool _checkCertRevocation; private LocalCertificateSelectionCallback _clientCertSelectionCallback; private X509CertificateCollection _clientCerts; private SslProtocols _enabledSslProtocols; private RemoteCertificateValidationCallback _serverCertValidationCallback; private string _targetHost; public bool CheckCertificateRevocation { get { return _checkCertRevocation; } set { _checkCertRevocation = value; } } public X509CertificateCollection ClientCertificates { get { return _clientCerts; } set { _clientCerts = value; } } public LocalCertificateSelectionCallback ClientCertificateSelectionCallback { get { if (_clientCertSelectionCallback == null) { _clientCertSelectionCallback = defaultSelectClientCertificate; } return _clientCertSelectionCallback; } set { _clientCertSelectionCallback = value; } } public SslProtocols EnabledSslProtocols { get { return _enabledSslProtocols; } set { _enabledSslProtocols = value; } } public RemoteCertificateValidationCallback ServerCertificateValidationCallback { get { if (_serverCertValidationCallback == null) { _serverCertValidationCallback = defaultValidateServerCertificate; } return _serverCertValidationCallback; } set { _serverCertValidationCallback = value; } } public string TargetHost { get { return _targetHost; } set { if (value == null) { throw new ArgumentNullException("value"); } if (value.Length == 0) { throw new ArgumentException("An empty string.", "value"); } _targetHost = value; } } public ClientSslConfiguration(string targetHost) { if (targetHost == null) { throw new ArgumentNullException("targetHost"); } if (targetHost.Length == 0) { throw new ArgumentException("An empty string.", "targetHost"); } _targetHost = targetHost; _enabledSslProtocols = SslProtocols.None; } public ClientSslConfiguration(ClientSslConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } _checkCertRevocation = configuration._checkCertRevocation; _clientCertSelectionCallback = configuration._clientCertSelectionCallback; _clientCerts = configuration._clientCerts; _enabledSslProtocols = configuration._enabledSslProtocols; _serverCertValidationCallback = configuration._serverCertValidationCallback; _targetHost = configuration._targetHost; } private static X509Certificate defaultSelectClientCertificate(object sender, string targetHost, X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string[] acceptableIssuers) { return null; } private static bool defaultValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; } } } namespace WebSocketSharp.Native { internal enum RuntimePlatform { Windows, Linux, Mac } internal enum RuntimeArchitecture { X86, X64, Arm64 } internal class NativeHelpers { internal static RuntimePlatform GetRuntimePlatform() { switch (Environment.OSVersion.Platform) { case PlatformID.Win32S: case PlatformID.Win32Windows: case PlatformID.Win32NT: case PlatformID.WinCE: return RuntimePlatform.Windows; case PlatformID.MacOSX: return RuntimePlatform.Mac; case PlatformID.Unix: return (!File.Exists("/System/Library/CoreServices/SystemVersion.plist")) ? RuntimePlatform.Linux : RuntimePlatform.Mac; default: return RuntimePlatform.Windows; } } internal static RuntimeArchitecture GetRuntimeArchitecture() { if (IntPtr.Size == 4) { return RuntimeArchitecture.X86; } string text = (Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432") ?? Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE") ?? string.Empty).ToUpperInvariant(); if (text.Contains("ARM64") || text.Contains("AARCH64")) { return RuntimeArchitecture.Arm64; } return RuntimeArchitecture.X64; } } internal static class NativeLibLoader { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate NativeResult nws_client_create_delegate(byte[] urlPtr, ulong urlLen, out IntPtr client); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void nws_client_destroy_delegate(IntPtr client); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate NativeResult nws_client_abort_delegate(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate NativeResult nws_client_connect_delegate(NativeWebSocketHandle client); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate NativeResult nws_client_close_delegate(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate NativeResult nws_client_send_text_delegate(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate NativeResult nws_client_send_binary_delegate(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate NativeResult nws_client_ping_delegate(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate NativeResult nws_client_poll_event_delegate(NativeWebSocketHandle client, int timeoutMs, out NativeEventRaw nativeEvent); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void nws_event_clear_delegate(ref NativeEventRaw nativeEvent); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void nws_set_log_handler_delegate(NativeLogCallback handler); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void nws_set_log_level_delegate(int level); private sealed class NativeFunctionTable { public IntPtr ModuleHandle; public string LibraryPath; public nws_client_create_delegate Create; public nws_client_destroy_delegate Destroy; public nws_client_abort_delegate Abort; public nws_client_connect_delegate Connect; public nws_client_close_delegate Close; public nws_client_send_text_delegate SendText; public nws_client_send_binary_delegate SendBinary; public nws_client_ping_delegate Ping; public nws_client_poll_event_delegate PollEvent; public nws_event_clear_delegate ClearEvent; public nws_set_log_handler_delegate SetLogHandler; public nws_set_log_level_delegate SetLogLevel; } private static class Linux64NLib { [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_create(byte[] urlPtr, ulong urlLen, out IntPtr client); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_client_destroy(IntPtr client); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_abort(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_connect(NativeWebSocketHandle client); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_close(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_send_text(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_send_binary(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_ping(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_poll_event(NativeWebSocketHandle client, int timeoutMs, out NativeEventRaw nativeEvent); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_event_clear(ref NativeEventRaw nativeEvent); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_set_log_handler(NativeLogCallback handler); [DllImport("nativews-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_set_log_level(int level); } private static class LinuxArm64NLib { [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_create(byte[] urlPtr, ulong urlLen, out IntPtr client); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_client_destroy(IntPtr client); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_abort(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_connect(NativeWebSocketHandle client); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_close(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_send_text(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_send_binary(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_ping(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_poll_event(NativeWebSocketHandle client, int timeoutMs, out NativeEventRaw nativeEvent); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_event_clear(ref NativeEventRaw nativeEvent); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_set_log_handler(NativeLogCallback handler); [DllImport("nativews-linux-arm64.so", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_set_log_level(int level); } private static class MacNLib { [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_create(byte[] urlPtr, ulong urlLen, out IntPtr client); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_client_destroy(IntPtr client); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_abort(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_connect(NativeWebSocketHandle client); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_close(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_send_text(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_send_binary(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_ping(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern NativeResult nws_client_poll_event(NativeWebSocketHandle client, int timeoutMs, out NativeEventRaw nativeEvent); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_event_clear(ref NativeEventRaw nativeEvent); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_set_log_handler(NativeLogCallback handler); [DllImport("nativews-macos-universal.dylib", CallingConvention = CallingConvention.Cdecl)] internal static extern void nws_set_log_level(int level); } private const string Windows32Library = "nativews-win32.dll"; private const string Windows64Library = "nativews-win64.dll"; private const string WindowsArm64Library = "nativews-winarm64.dll"; private const string Linux64Library = "nativews-linux-amd64.so"; private const string LinuxArm64Library = "nativews-linux-arm64.so"; private const string MacLibrary = "nativews-macos-universal.dylib"; private static readonly object Sync = new object(); private static NativeFunctionTable _functions; internal static NativeResult Create(byte[] urlPtr, ulong urlLen, out IntPtr client) { return GetFunctions().Create(urlPtr, urlLen, out client); } internal static void Destroy(IntPtr client) { GetFunctions().Destroy(client); } internal static NativeResult Abort(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen) { return GetFunctions().Abort(client, code, reasonPtr, reasonLen); } internal static NativeResult Connect(NativeWebSocketHandle client) { return GetFunctions().Connect(client); } internal static NativeResult Close(NativeWebSocketHandle client, ushort code, byte[] reasonPtr, ulong reasonLen) { return GetFunctions().Close(client, code, reasonPtr, reasonLen); } internal static NativeResult SendText(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen) { return GetFunctions().SendText(client, dataPtr, dataLen); } internal static NativeResult SendBinary(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen) { return GetFunctions().SendBinary(client, dataPtr, dataLen); } internal static NativeResult Ping(NativeWebSocketHandle client, byte[] dataPtr, ulong dataLen) { return GetFunctions().Ping(client, dataPtr, dataLen); } internal static NativeResult PollEvent(NativeWebSocketHandle client, int timeoutMs, out NativeEventRaw nativeEvent) { return GetFunctions().PollEvent(client, timeoutMs, out nativeEvent); } internal static void ClearEvent(ref NativeEventRaw nativeEvent) { GetFunctions().ClearEvent(ref nativeEvent); } internal static void SetLogHandler(NativeLogCallback handler) { NativeFunctionTable functions = GetFunctions(); if (functions.SetLogHandler == null) { throw CreateMissingExportException(functions.LibraryPath, "nws_set_log_handler"); } functions.SetLogHandler(handler); } internal static void SetLogLevel(int level) { NativeFunctionTable functions = GetFunctions(); if (functions.SetLogLevel == null) { throw CreateMissingExportException(functions.LibraryPath, "nws_set_log_level"); } functions.SetLogLevel(level); } private static NativeFunctionTable GetFunctions() { NativeFunctionTable functions = _functions; if (functions != null) { return functions; } lock (Sync) { if (_functions == null) { _functions = LoadFunctions(); } return _functions; } } private static NativeFunctionTable LoadFunctions() { RuntimePlatform runtimePlatform = NativeHelpers.GetRuntimePlatform(); if (1 == 0) { } NativeFunctionTable result = runtimePlatform switch { RuntimePlatform.Windows => LoadWindowsFunctions(), RuntimePlatform.Mac => LoadMacFunctions(), _ => (NativeHelpers.GetRuntimeArchitecture() == RuntimeArchitecture.Arm64) ? LoadLinuxArm64Functions() : LoadLinux64Functions(), }; if (1 == 0) { } return result; } private static NativeFunctionTable LoadWindowsFunctions() { string text = Path.Combine(GetNativeLibraryDirectory(), GetWindowsLibraryName()); if (!File.Exists(text)) { throw new DllNotFoundException("The native websocket library could not be found at '" + text + "'."); } IntPtr intPtr = LoadLibrary(text); if (intPtr == IntPtr.Zero) { int lastWin32Error = Marshal.GetLastWin32Error(); throw new DllNotFoundException("The native websocket library could not be loaded from '" + text + "' (LoadLibrary error " + lastWin32Error + ")."); } return new NativeFunctionTable { ModuleHandle = intPtr, LibraryPath = text, Create = GetDelegate<nws_client_create_delegate>(intPtr, "nws_client_create", text), Destroy = GetDelegate<nws_client_destroy_delegate>(intPtr, "nws_client_destroy", text), Abort = GetDelegate<nws_client_abort_delegate>(intPtr, "nws_client_abort", text), Connect = GetDelegate<nws_client_connect_delegate>(intPtr, "nws_client_connect", text), Close = GetDelegate<nws_client_close_delegate>(intPtr, "nws_client_close", text), SendText = GetDelegate<nws_client_send_text_delegate>(intPtr, "nws_client_send_text", text), SendBinary = GetDelegate<nws_client_send_binary_delegate>(intPtr, "nws_client_send_binary", text), Ping = GetDelegate<nws_client_ping_delegate>(intPtr, "nws_client_ping", text), PollEvent = GetDelegate<nws_client_poll_event_delegate>(intPtr, "nws_client_poll_event", text), ClearEvent = GetDelegate<nws_event_clear_delegate>(intPtr, "nws_event_clear", text), SetLogHandler = GetOptionalDelegate<nws_set_log_handler_delegate>(intPtr, "nws_set_log_handler"), SetLogLevel = GetOptionalDelegate<nws_set_log_level_delegate>(intPtr, "nws_set_log_level") }; } private static NativeFunctionTable LoadLinux64Functions() { NativeFunctionTable nativeFunctionTable = new NativeFunctionTable(); nativeFunctionTable.LibraryPath = "nativews-linux-amd64.so"; nativeFunctionTable.Create = Linux64NLib.nws_client_create; nativeFunctionTable.Destroy = Linux64NLib.nws_client_destroy; nativeFunctionTable.Abort = Linux64NLib.nws_client_abort; nativeFunctionTable.Connect = Linux64NLib.nws_client_connect; nativeFunctionTable.Close = Linux64NLib.nws_client_close; nativeFunctionTable.SendText = Linux64NLib.nws_client_send_text; nativeFunctionTable.SendBinary = Linux64NLib.nws_client_send_binary; nativeFunctionTable.Ping = Linux64NLib.nws_client_ping; nativeFunctionTable.PollEvent = Linux64NLib.nws_client_poll_event; nativeFunctionTable.ClearEvent = Linux64NLib.nws_event_clear; nativeFunctionTable.SetLogHandler = Linux64NLib.nws_set_log_handler; nativeFunctionTable.SetLogLevel = Linux64NLib.nws_set_log_level; return nativeFunctionTable; } private static NativeFunctionTable LoadLinuxArm64Functions() { NativeFunctionTable nativeFunctionTable = new NativeFunctionTable(); nativeFunctionTable.LibraryPath = "nativews-linux-arm64.so"; nativeFunctionTable.Create = LinuxArm64NLib.nws_client_create; nativeFunctionTable.Destroy = LinuxArm64NLib.nws_client_destroy; nativeFunctionTable.Abort = LinuxArm64NLib.nws_client_abort; nativeFunctionTable.Connect = LinuxArm64NLib.nws_client_connect; nativeFunctionTable.Close = LinuxArm64NLib.nws_client_close; nativeFunctionTable.SendText = LinuxArm64NLib.nws_client_send_text; nativeFunctionTable.SendBinary = LinuxArm64NLib.nws_client_send_binary; nativeFunctionTable.Ping = LinuxArm64NLib.nws_client_ping; nativeFunctionTable.PollEvent = LinuxArm64NLib.nws_client_poll_event; nativeFunctionTable.ClearEvent = LinuxArm64NLib.nws_event_clear; nativeFunctionTable.SetLogHandler = LinuxArm64NLib.nws_set_log_handler; nativeFunctionTable.SetLogLevel = LinuxArm64NLib.nws_set_log_level; return nativeFunctionTable; } private static NativeFunctionTable LoadMacFunctions() { NativeFunctionTable nativeFunctionTable = new NativeFunctionTable(); nativeFunctionTable.LibraryPath = "nativews-macos-universal.dylib"; nativeFunctionTable.Create = MacNLib.nws_client_create; nativeFunctionTable.Destroy = MacNLib.nws_client_destroy; nativeFunctionTable.Abort = MacNLib.nws_client_abort; nativeFunctionTable.Connect = MacNLib.nws_client_connect; nativeFunctionTable.Close = MacNLib.nws_client_close; nativeFunctionTable.SendText = MacNLib.nws_client_send_text; nativeFunctionTable.SendBinary = MacNLib.nws_client_send_binary; nativeFunctionTable.Ping = MacNLib.nws_client_ping; nativeFunctionTable.PollEvent = MacNLib.nws_client_poll_event; nativeFunctionTable.ClearEvent = MacNLib.nws_event_clear; nativeFunctionTable.SetLogHandler = MacNLib.nws_set_log_handler; nativeFunctionTable.SetLogLevel = MacNLib.nws_set_log_level; return nativeFunctionTable; } private static string GetNativeLibraryDirectory() { string location = typeof(NativeLibLoader).Assembly.Location; if (!string.IsNullOrEmpty(location)) { string directoryName = Path.GetDirectoryName(location); if (!string.IsNullOrEmpty(directoryName)) { return directoryName; } } return AppDomain.CurrentDomain.BaseDirectory ?? string.Empty; } private static string GetWindowsLibraryName() { RuntimeArchitecture runtimeArchitecture = NativeHelpers.GetRuntimeArchitecture(); if (1 == 0) { } string result = runtimeArchitecture switch { RuntimeArchitecture.X86 => "nativews-win32.dll", RuntimeArchitecture.Arm64 => "nativews-winarm64.dll", _ => "nativews-win64.dll", }; if (1 == 0) { } return result; } private static T GetDelegate<T>(IntPtr moduleHandle, string exportName, string libraryPath) where T : class { IntPtr procAddress = GetProcAddress(moduleHandle, exportName); if (procAddress == IntPtr.Zero) { throw new EntryPointNotFoundException("The native websocket library '" + libraryPath + "' does not export '" + exportName + "'."); } return (T)(object)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T)); } private static T GetOptionalDelegate<T>(IntPtr moduleHandle, string exportName) where T : class { IntPtr procAddress = GetProcAddress(moduleHandle, exportName); return (procAddress == IntPtr.Zero) ? null : ((T)(object)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T))); } private static EntryPointNotFoundException CreateMissingExportException(string libraryPath, string exportName) { return new EntryPointNotFoundException("The native websocket library '" + libraryPath + "' does not export '" + exportName + "'."); } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); } internal enum NativeResult { Ok = 0, Timeout = 1, InvalidState = 2, InvalidArgument = 3, NotOpen = 4, Disposed = 5, InternalError = 6, Unknown = -1 } internal enum NativeErrorKind { ConnectFailed = 1, TlsFailed = 2, Io = 3, Protocol = 4, Timeout = 5, Internal = 6, Unknown = -1 } internal enum NativeEventKind { Open = 1, Close, Message, Error, Pong } internal enum NativeMessageKind { Text = 1, Binary } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void NativeLogCallback(int level, IntPtr message); internal struct NativeEvent { public NativeEventKind Kind; public NativeMessageKind MessageKind; public NativeErrorKind ErrorKind; public ushort CloseCode; public bool CloseWasClean; public byte[] Data; } internal struct NativeEventRaw { public int kind; public int message_kind; public int error_kind; public ushort close_code; public byte close_was_clean; public IntPtr data_ptr; public ulong data_len; } internal sealed class NativeWebSocketHandle : SafeHandleZeroOrMinusOneIsInvalid { public NativeWebSocketHandle() : base(ownsHandle: true) { } public NativeWebSocketHandle(IntPtr handle) : base(ownsHandle: true) { SetHandle(handle); } protected override bool ReleaseHandle() { WebSocketInterop.Destroy(handle); handle = IntPtr.Zero; return true; } } internal static class WebSocketInterop { private static NativeResult Create(byte[] url, out NativeWebSocketHandle client) { IntPtr client2; NativeResult nativeResult = NativeLibLoader.Create(url, (ulong)url.Length, out client2); client = ((nativeResult == NativeResult.Ok && client2 != IntPtr.Zero) ? new NativeWebSocketHandle(client2) : null); return nativeResult; } public static NativeResult Create(string url, out NativeWebSocketHandle client) { return Create(Encoding.UTF8.GetBytes(url), out client); } public static NativeResult Connect(NativeWebSocketHandle client) { return NativeLibLoader.Connect(client); } public static NativeResult Abort(NativeWebSocketHandle client, string reason, ushort code) { byte[] array = EncodeNullable(reason); return NativeLibLoader.Abort(client, code, array, (ulong)array.Length); } public static NativeResult Close(NativeWebSocketHandle client, string reason, ushort code) { byte[] array = EncodeNullable(reason); return NativeLibLoader.Close(client, code, array, (ulong)array.Length); } public static void Destroy(IntPtr client) { if (!(client == IntPtr.Zero)) { NativeLibLoader.Destroy(client); } } public static NativeResult SendText(NativeWebSocketHandle client, byte[] data) { return NativeLibLoader.SendText(client, data, (ulong)data.Length); } public static NativeResult SendBinary(NativeWebSocketHandle client, byte[] data) { return NativeLibLoader.SendBinary(client, data, (ulong)data.Length); } public static NativeResult Ping(NativeWebSocketHandle client, byte[] data) { return NativeLibLoader.Ping(client, data, (ulong)data.Length); } public static NativeResult PollEvent(NativeWebSocketHandle client, int timeoutMs, out NativeEvent nativeEvent) { NativeEventRaw nativeEvent2; NativeResult nativeResult = NativeLibLoader.PollEvent(client, timeoutMs, out nativeEvent2); if (nativeResult != NativeResult.Ok) { nativeEvent = default(NativeEvent); return nativeResult; } try { nativeEvent = new NativeEvent { Kind = (NativeEventKind)nativeEvent2.kind, MessageKind = (NativeMessageKind)nativeEvent2.message_kind, ErrorKind = (NativeErrorKind)nativeEvent2.error_kind, CloseCode = nativeEvent2.close_code, CloseWasClean = (nativeEvent2.close_was_clean != 0), Data = CopyBytes(nativeEvent2.data_ptr, nativeEvent2.data_len) }; } finally { ClearEvent(ref nativeEvent2); } return nativeResult; } private static void ClearEvent(ref NativeEventRaw nativeEvent) { NativeLibLoader.ClearEvent(ref nativeEvent); } public static void SetLogHandler(NativeLogCallback handler) { NativeLibLoader.SetLogHandler(handler); } public static void SetLogLevel(int level) { NativeLibLoader.SetLogLevel(level); } private static byte[] CopyBytes(IntPtr dataPtr, ulong dataLen) { if (dataPtr == IntPtr.Zero || dataLen == 0) { return new byte[0]; } if (dataLen > int.MaxValue) { throw new InvalidOperationException("Native payload is too large for managed allocation."); } byte[] array = new byte[(uint)dataLen]; Marshal.Copy(dataPtr, array, 0, array.Length); return array; } private static byte[] EncodeNullable(string text) { return string.IsNullOrEmpty(text) ? new byte[0] : Encoding.UTF8.GetBytes(text); } } }