Decompiled source of Baby Steps Multiplayer Mod v1.2.4

Mods/BabyStepsMultiplayerClient.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Numerics;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using BabyStepsMultiplayerClient;
using BabyStepsMultiplayerClient.Audio;
using BabyStepsMultiplayerClient.Components;
using BabyStepsMultiplayerClient.Config;
using BabyStepsMultiplayerClient.Extensions;
using BabyStepsMultiplayerClient.Localization;
using BabyStepsMultiplayerClient.Networking;
using BabyStepsMultiplayerClient.Player;
using BabyStepsMultiplayerClient.UI;
using BabyStepsMultiplayerClient.UI.Elements;
using Concentus.Enums;
using Concentus.Structs;
using HarmonyLib;
using Il2Cpp;
using Il2CppBabySteps.Core.Audio;
using Il2CppCinemachine;
using Il2CppFMOD;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppNWH.DWP2.WaterObjects;
using Il2CppRewired;
using Il2CppSteamworks;
using Il2CppSystem;
using Il2CppSystem.Collections;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using LiteNetLib;
using LiteNetLib.Layers;
using LiteNetLib.Utils;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Tomlet;
using Tomlet.Models;
using Unity.LiveCapture.ARKitFaceCapture;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.EventSystems;
using UnityEngine.Events;
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: MelonInfo(typeof(Core), "BabyStepsMultiplayerClient", "1.2.4", "Caleb Orchard", "https://github.com/caleborchard/Baby-Steps-Multiplayer-Mod-Client")]
[assembly: MelonGame("DefaultCompany", "BabySteps")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Caleb Orchard")]
[assembly: AssemblyConfiguration("Retail - Debug")]
[assembly: AssemblyDescription("A MelonLoader mod for Baby Steps that adds multiplayer to the game.")]
[assembly: AssemblyFileVersion("1.2.4")]
[assembly: AssemblyInformationalVersion("1.2.4+931a551492ce62dccc6d631d92b57b4fb3a16b8c")]
[assembly: AssemblyProduct("BabyStepsMultiplayerClient")]
[assembly: AssemblyTitle("BabyStepsMultiplayerClient")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/caleborchard/Baby-Steps-Multiplayer-Mod-Client")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.4.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 BabyStepsMultiplayerClient
{
	public class Core : MelonMod
	{
		public const string SERVER_VERSION = "106";

		public static string CLIENT_VERSION;

		public const string cloneText = "(Clone)";

		public static Instance logger;

		public static UIManager uiManager;

		public static NetworkManager networkManager;

		public static Action OnConnectionStateChanged;

		public override void OnLateInitializeMelon()
		{
			CLIENT_VERSION = ((MelonBase)this).Info.Version;
			logger = ((MelonBase)this).LoggerInstance;
			try
			{
				((MelonBase)this).HarmonyInstance.PatchAll(Assembly.GetExecutingAssembly());
			}
			catch (Exception value)
			{
				logger.Error($"Harmony patching failed: {value}");
			}
			ManagedEnumerator.Register();
			uiManager = new UIManager();
			networkManager = new NetworkManager();
			MultiplayerMenu.Initialize();
			logger.Msg("Initialized!");
			VersionCheck.CheckForUpdate();
		}

		public override void OnGUI()
		{
			if (uiManager != null)
			{
				uiManager.Draw();
			}
		}

		public override void OnUpdate()
		{
			if (uiManager == null || networkManager == null)
			{
				return;
			}
			uiManager.Update();
			networkManager.Update();
			MultiplayerMenu.Update();
			if (LocalPlayer.Instance != null)
			{
				LocalPlayer.Instance.Update();
			}
			if (MelonDebug.IsEnabled())
			{
				if (Input.GetKeyDown((KeyCode)284))
				{
					networkManager.Connect(ModSettings.connection.Address.Value, ModSettings.connection.Port.Value, ModSettings.connection.Password.Value);
				}
				if (Input.GetKeyDown((KeyCode)285))
				{
					networkManager.Disconnect();
				}
			}
		}

		public override void OnLateUpdate()
		{
			if (networkManager != null)
			{
				if (LocalPlayer.Instance != null)
				{
					LocalPlayer.Instance.LateUpdate();
				}
				networkManager.LateUpdate();
			}
		}

		public override void OnApplicationQuit()
		{
			if (networkManager != null)
			{
				networkManager.Disconnect();
			}
		}

		public static bool HasLoadedGame()
		{
			if ((Object)(object)Menu.me == (Object)null)
			{
				return false;
			}
			return Menu.me.gameInProgress;
		}

		public static void DebugMsg(string msg)
		{
			logger.Msg(msg);
		}

		public static bool RegisterComponent<T>(params Type[] interfaces) where T : class
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			try
			{
				RegisterTypeOptions val = new RegisterTypeOptions();
				val.set_LogSuccess(true);
				val.set_Interfaces(Il2CppInterfaceCollection.op_Implicit(interfaces));
				ClassInjector.RegisterTypeInIl2Cpp<T>(val);
			}
			catch (Exception value)
			{
				logger.Error($"Exception while attempting to Register {typeof(T).Name}: {value}");
				return false;
			}
			return true;
		}
	}
	public static class ModSettings
	{
		public static readonly ConnectionConfig connection = new ConnectionConfig();

		public static readonly PlayerConfig player = new PlayerConfig();

		public static readonly AudioConfig audio = new AudioConfig();

		public static void Load()
		{
			connection.Load();
			player.Load();
			audio.Load();
		}

		public static void Save()
		{
			connection.Save();
			player.Save();
			audio.Save();
		}
	}
}
namespace BabyStepsMultiplayerClient.UI
{
	internal static class InputBindingHelper
	{
		private static readonly bool CaptureLoggingEnabled = true;

		private const string RewiredActionPrefix = "RWACTION:";

		private const string RewiredButtonPrefix = "RWBTN:";

		private const string RewiredAxisPrefix = "RWAXIS:";

		private const float AxisThreshold = 0.5f;

		private const int MaxRawRewiredButtonId = 1023;

		private static readonly Dictionary<string, bool> _previousState = new Dictionary<string, bool>();

		private static readonly int[] _inputActionIds = BuildInputActionIdList();

		private static readonly KeyCode[] _joystickButtonKeyCodes = BuildJoystickButtonKeyCodes();

		public static bool IsInputHeldForCapture()
		{
			if (Input.anyKey)
			{
				return true;
			}
			for (int i = 0; i < _joystickButtonKeyCodes.Length; i++)
			{
				if (Input.GetKey(_joystickButtonKeyCodes[i]))
				{
					return true;
				}
			}
			return false;
		}

		public static bool TryCapturePressedBinding(out string binding, out string displayName)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Invalid comparison between Unknown and I4
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < _joystickButtonKeyCodes.Length; i++)
			{
				KeyCode val = _joystickButtonKeyCodes[i];
				if (Input.GetKeyDown(val))
				{
					binding = ((object)(KeyCode)(ref val)).ToString();
					displayName = ((object)(KeyCode)(ref val)).ToString();
					LogCapture("joystick-keycode", binding, displayName);
					return true;
				}
			}
			foreach (KeyCode value in Enum.GetValues(typeof(KeyCode)))
			{
				KeyCode val2 = value;
				if ((int)val2 == 0 || IsJoystickKeyCode(val2) || !Input.GetKeyDown(val2))
				{
					continue;
				}
				binding = ((object)(KeyCode)(ref val2)).ToString();
				displayName = ((object)(KeyCode)(ref val2)).ToString();
				LogCapture("keyboard", binding, displayName);
				return true;
			}
			Menu me = Menu.me;
			Player val3 = ((me != null) ? me.rwPlayer : null);
			if (val3 != null)
			{
				for (int j = 0; j < _inputActionIds.Length; j++)
				{
					int num = _inputActionIds[j];
					if (val3.GetButtonDown(num))
					{
						if (!TryGetInputActionName(num, out var actionName))
						{
							actionName = "Action_" + num;
						}
						if (!IsAxisLikeAction(num, actionName))
						{
							binding = "RWACTION:" + actionName;
							displayName = FormatActionDisplayName(actionName);
							LogCapture("rewired-action", binding, displayName);
							return true;
						}
					}
				}
				if (TryCaptureAxisDown(out binding, out displayName))
				{
					return true;
				}
				for (int k = 0; k <= 1023; k++)
				{
					if (val3.GetButtonDown(k))
					{
						binding = "RWBTN:" + k;
						displayName = GetRewiredButtonDisplayName(k);
						LogCapture("rewired-raw-button", binding, displayName);
						return true;
					}
				}
			}
			binding = null;
			displayName = null;
			return false;
		}

		public static bool IsPressed(string binding)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (TryParseKeyCode(binding, out var keyCode))
			{
				return Input.GetKey(keyCode);
			}
			if (TryParseRewiredButton(binding, out var actionId))
			{
				Menu me = Menu.me;
				int result;
				if (me == null)
				{
					result = 0;
				}
				else
				{
					Player rwPlayer = me.rwPlayer;
					result = (((rwPlayer != null) ? new bool?(rwPlayer.GetButton(actionId)) : null).GetValueOrDefault() ? 1 : 0);
				}
				return (byte)result != 0;
			}
			if (TryParseRewiredAction(binding, out var actionName) && TryGetInputActionId(actionName, out var actionId2))
			{
				Menu me2 = Menu.me;
				int result2;
				if (me2 == null)
				{
					result2 = 0;
				}
				else
				{
					Player rwPlayer2 = me2.rwPlayer;
					result2 = (((rwPlayer2 != null) ? new bool?(rwPlayer2.GetButton(actionId2)) : null).GetValueOrDefault() ? 1 : 0);
				}
				return (byte)result2 != 0;
			}
			if (TryParseRewiredAxis(binding, out var axisId, out var direction))
			{
				Menu me3 = Menu.me;
				Player val = ((me3 != null) ? me3.rwPlayer : null);
				if (val == null)
				{
					return false;
				}
				float axis = val.GetAxis(axisId);
				return (direction > 0) ? (axis > 0.5f) : (axis < -0.5f);
			}
			return false;
		}

		public static bool IsDown(string binding)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (TryParseKeyCode(binding, out var keyCode))
			{
				return Input.GetKeyDown(keyCode);
			}
			if (TryParseRewiredButton(binding, out var actionId))
			{
				Menu me = Menu.me;
				int result;
				if (me == null)
				{
					result = 0;
				}
				else
				{
					Player rwPlayer = me.rwPlayer;
					result = (((rwPlayer != null) ? new bool?(rwPlayer.GetButtonDown(actionId)) : null).GetValueOrDefault() ? 1 : 0);
				}
				return (byte)result != 0;
			}
			if (TryParseRewiredAction(binding, out var actionName) && TryGetInputActionId(actionName, out var actionId2))
			{
				Menu me2 = Menu.me;
				int result2;
				if (me2 == null)
				{
					result2 = 0;
				}
				else
				{
					Player rwPlayer2 = me2.rwPlayer;
					result2 = (((rwPlayer2 != null) ? new bool?(rwPlayer2.GetButtonDown(actionId2)) : null).GetValueOrDefault() ? 1 : 0);
				}
				return (byte)result2 != 0;
			}
			if (TryParseRewiredAxis(binding, out var _, out var _))
			{
				bool flag = IsPressed(binding);
				_previousState.TryGetValue(binding, out var value);
				_previousState[binding] = flag;
				return flag && !value;
			}
			return false;
		}

		public static bool IsControllerBinding(string binding)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(binding))
			{
				return false;
			}
			if (TryParseKeyCode(binding, out var keyCode))
			{
				return IsJoystickKeyCode(keyCode);
			}
			if (binding.StartsWith("RWACTION:", StringComparison.Ordinal) || binding.StartsWith("RWBTN:", StringComparison.Ordinal) || binding.StartsWith("RWAXIS:", StringComparison.Ordinal))
			{
				return true;
			}
			return false;
		}

		public static string GetDisplayName(string binding)
		{
			if (TryParseRewiredAction(binding, out var actionName))
			{
				return FormatActionDisplayName(actionName);
			}
			if (TryParseRewiredButton(binding, out var actionId))
			{
				return GetRewiredButtonDisplayName(actionId);
			}
			if (TryParseRewiredAxis(binding, out var axisId, out var direction))
			{
				switch (axisId)
				{
				case 13:
					return (direction > 0) ? "AXIS Up" : "AXIS Down";
				case 12:
					return (direction > 0) ? "AXIS Right" : "AXIS Left";
				}
			}
			return binding;
		}

		private static bool TryParseKeyCode(string binding, out KeyCode keyCode)
		{
			if (string.IsNullOrWhiteSpace(binding))
			{
				keyCode = (KeyCode)0;
				return false;
			}
			if (!Enum.TryParse<KeyCode>(binding, ignoreCase: true, out keyCode))
			{
				return false;
			}
			return (int)keyCode != 0;
		}

		private static bool TryParseRewiredButton(string binding, out int actionId)
		{
			actionId = -1;
			if (string.IsNullOrWhiteSpace(binding) || !binding.StartsWith("RWBTN:", StringComparison.Ordinal))
			{
				return false;
			}
			return int.TryParse(binding.Substring("RWBTN:".Length), out actionId);
		}

		private static bool TryParseRewiredAction(string binding, out string actionName)
		{
			actionName = null;
			if (string.IsNullOrWhiteSpace(binding) || !binding.StartsWith("RWACTION:", StringComparison.Ordinal))
			{
				return false;
			}
			actionName = binding.Substring("RWACTION:".Length);
			return !string.IsNullOrWhiteSpace(actionName);
		}

		private static bool TryParseRewiredAxis(string binding, out int axisId, out int direction)
		{
			axisId = -1;
			direction = 0;
			if (string.IsNullOrWhiteSpace(binding) || !binding.StartsWith("RWAXIS:", StringComparison.Ordinal))
			{
				return false;
			}
			string text = binding.Substring("RWAXIS:".Length);
			string[] array = text.Split(':');
			if (array.Length != 2)
			{
				return false;
			}
			if (!int.TryParse(array[0], out axisId))
			{
				return false;
			}
			if (array[1] == "+")
			{
				direction = 1;
				return true;
			}
			if (array[1] == "-")
			{
				direction = -1;
				return true;
			}
			return false;
		}

		private static bool TryCaptureAxisDown(out string binding, out string displayName)
		{
			Menu me = Menu.me;
			Player val = ((me != null) ? me.rwPlayer : null);
			if (val == null)
			{
				binding = null;
				displayName = null;
				return false;
			}
			float axis = val.GetAxis(12);
			float axisPrev = val.GetAxisPrev(12);
			float axis2 = val.GetAxis(13);
			float axisPrev2 = val.GetAxisPrev(13);
			if (axis2 > 0.5f && axisPrev2 <= 0.5f)
			{
				binding = "RWAXIS:" + 13 + ":+";
				displayName = "AXIS Up";
				LogCapture("rewired-axis", binding, displayName);
				return true;
			}
			if (axis2 < -0.5f && axisPrev2 >= -0.5f)
			{
				binding = "RWAXIS:" + 13 + ":-";
				displayName = "AXIS Down";
				LogCapture("rewired-axis", binding, displayName);
				return true;
			}
			if (axis > 0.5f && axisPrev <= 0.5f)
			{
				binding = "RWAXIS:" + 12 + ":+";
				displayName = "AXIS Right";
				LogCapture("rewired-axis", binding, displayName);
				return true;
			}
			if (axis < -0.5f && axisPrev >= -0.5f)
			{
				binding = "RWAXIS:" + 12 + ":-";
				displayName = "AXIS Left";
				LogCapture("rewired-axis", binding, displayName);
				return true;
			}
			binding = null;
			displayName = null;
			return false;
		}

		private static string GetRewiredButtonDisplayName(int actionId)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (Enum.IsDefined(typeof(InputActions), actionId))
			{
				InputActions val = (InputActions)actionId;
				return ((object)(InputActions)(ref val)).ToString();
			}
			return "Controller Button " + actionId;
		}

		private static int[] BuildInputActionIdList()
		{
			Array values = Enum.GetValues(typeof(InputActions));
			List<int> list = new List<int>(values.Length);
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < values.Length; i++)
			{
				int item = (int)values.GetValue(i);
				if (hashSet.Add(item))
				{
					list.Add(item);
				}
			}
			list.Sort();
			return list.ToArray();
		}

		private static bool TryGetInputActionName(int actionId, out string actionName)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			actionName = null;
			if (!Enum.IsDefined(typeof(InputActions), actionId))
			{
				return false;
			}
			InputActions val = (InputActions)actionId;
			actionName = ((object)(InputActions)(ref val)).ToString();
			return !string.IsNullOrWhiteSpace(actionName);
		}

		private static bool TryGetInputActionId(string actionName, out int actionId)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected I4, but got Unknown
			actionId = -1;
			if (string.IsNullOrWhiteSpace(actionName))
			{
				return false;
			}
			if (!Enum.TryParse<InputActions>(actionName, ignoreCase: true, out InputActions result))
			{
				return false;
			}
			actionId = (int)result;
			return true;
		}

		private static bool IsAxisLikeAction(int actionId, string actionName)
		{
			if (actionId == 12 || actionId == 13)
			{
				return true;
			}
			if (string.IsNullOrWhiteSpace(actionName))
			{
				return false;
			}
			return actionName.IndexOf("Horizontal", StringComparison.OrdinalIgnoreCase) >= 0 || actionName.IndexOf("Vertical", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static KeyCode[] BuildJoystickButtonKeyCodes()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			List<KeyCode> list = new List<KeyCode>();
			for (int i = 0; i <= 19; i++)
			{
				if (Enum.TryParse<KeyCode>("JoystickButton" + i, out KeyCode result))
				{
					list.Add(result);
				}
			}
			for (int j = 1; j <= 8; j++)
			{
				for (int k = 0; k <= 19; k++)
				{
					if (Enum.TryParse<KeyCode>("Joystick" + j + "Button" + k, out KeyCode result2))
					{
						list.Add(result2);
					}
				}
			}
			return list.ToArray();
		}

		private static bool IsJoystickKeyCode(KeyCode keyCode)
		{
			string text = ((object)(KeyCode)(ref keyCode)).ToString();
			return text.IndexOf("Joystick", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static string FormatActionDisplayName(string actionName)
		{
			if (string.IsNullOrWhiteSpace(actionName))
			{
				return actionName;
			}
			if (actionName.StartsWith("UI", StringComparison.OrdinalIgnoreCase))
			{
				return "DPAD " + actionName.Substring(2);
			}
			return actionName;
		}

		private static void LogCapture(string source, string binding, string displayName)
		{
			if (CaptureLoggingEnabled)
			{
				Core.DebugMsg($"[BindCapture] source={source} binding={binding} display={displayName}");
			}
		}
	}
	public static class MenuInjectionLibrary
	{
		public sealed class RuntimeTabMenu
		{
			private readonly string[] _tabNames;

			private readonly List<Selectable>[] _pageSelectables;

			private int _setTabFrame = -1;

			private Selectable[] _fixedItems;

			public RectTransform Root { get; internal set; }

			public RectTransform Header { get; internal set; }

			public Button[] Tabs { get; internal set; }

			public RectTransform[] Pages { get; internal set; }

			public int ActiveTab { get; private set; }

			public int TabCount
			{
				get
				{
					Button[] tabs = Tabs;
					return (tabs != null) ? tabs.Length : 0;
				}
			}

			internal MenuItemList ManagedItemList { get; set; }

			internal Color NativeHighlightedColor { get; set; } = TabHighlighted;


			internal void SetFixedMenuItems(params Selectable[] items)
			{
				_fixedItems = items;
			}

			internal RuntimeTabMenu(string[] tabNames)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				_tabNames = tabNames;
				_pageSelectables = new List<Selectable>[tabNames.Length];
				for (int i = 0; i < tabNames.Length; i++)
				{
					_pageSelectables[i] = new List<Selectable>();
				}
			}

			public RectTransform GetPage(int index)
			{
				return (Pages != null && index >= 0 && index < Pages.Length) ? Pages[index] : null;
			}

			public void OnShown()
			{
				SetActiveTab(ActiveTab);
			}

			internal void RegisterPageSelectable(int page, Selectable s)
			{
				if (!((Object)(object)s == (Object)null) && _pageSelectables != null && page >= 0 && page < _pageSelectables.Length)
				{
					_pageSelectables[page].Add(s);
				}
			}

			public void SetActiveTab(int index)
			{
				//IL_015f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0158: Unknown result type (might be due to invalid IL or missing references)
				//IL_0164: Unknown result type (might be due to invalid IL or missing references)
				//IL_0187: Unknown result type (might be due to invalid IL or missing references)
				//IL_0194: Unknown result type (might be due to invalid IL or missing references)
				//IL_0213: Unknown result type (might be due to invalid IL or missing references)
				//IL_0218: Unknown result type (might be due to invalid IL or missing references)
				//IL_021c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0227: Unknown result type (might be due to invalid IL or missing references)
				//IL_0235: Unknown result type (might be due to invalid IL or missing references)
				//IL_0243: Unknown result type (might be due to invalid IL or missing references)
				//IL_0250: Unknown result type (might be due to invalid IL or missing references)
				//IL_026e: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
				if (Tabs == null || Pages == null || index < 0 || index >= TabCount)
				{
					return;
				}
				int frameCount = Time.frameCount;
				if (frameCount == _setTabFrame)
				{
					return;
				}
				_setTabFrame = frameCount;
				ActiveTab = index;
				List<Selectable>[] pageSelectables = _pageSelectables;
				List<Selectable> list = ((pageSelectables != null) ? pageSelectables[index] : null);
				for (int i = 0; i < TabCount; i++)
				{
					bool flag = i == index;
					if ((Object)(object)Pages[i] != (Object)null)
					{
						CanvasGroup component = ((Component)Pages[i]).GetComponent<CanvasGroup>();
						if ((Object)(object)component != (Object)null)
						{
							component.alpha = (flag ? 1f : 0f);
							component.interactable = flag;
							component.blocksRaycasts = flag;
						}
						if (flag)
						{
							Il2CppArrayBase<TMP_Text> componentsInChildren = ((Component)Pages[i]).GetComponentsInChildren<TMP_Text>(true);
							for (int j = 0; j < componentsInChildren.Length; j++)
							{
								if ((Object)(object)componentsInChildren[j] != (Object)null)
								{
									componentsInChildren[j].ForceMeshUpdate(false, false);
								}
							}
						}
					}
					if (!((Object)(object)Tabs[i] != (Object)null))
					{
						continue;
					}
					Color val = (flag ? TabSelected : TabUnselected);
					Image component2 = ((Component)Tabs[i]).GetComponent<Image>();
					if ((Object)(object)component2 != (Object)null)
					{
						((Graphic)component2).color = Color.white;
						((Graphic)component2).CrossFadeColor(val, 0f, true, true);
					}
					TMP_Text componentInChildren = ((Component)Tabs[i]).GetComponentInChildren<TMP_Text>(true);
					if ((Object)(object)componentInChildren != (Object)null)
					{
						if (_tabNames != null && i < _tabNames.Length)
						{
							componentInChildren.text = _tabNames[i];
						}
						((Graphic)componentInChildren).color = TabText;
						componentInChildren.ForceMeshUpdate(false, false);
					}
					ColorBlock colors = ((Selectable)Tabs[i]).colors;
					((ColorBlock)(ref colors)).normalColor = val;
					((ColorBlock)(ref colors)).selectedColor = NativeHighlightedColor;
					((ColorBlock)(ref colors)).highlightedColor = NativeHighlightedColor;
					((ColorBlock)(ref colors)).pressedColor = NativeHighlightedColor;
					((ColorBlock)(ref colors)).disabledColor = val;
					((ColorBlock)(ref colors)).colorMultiplier = 1f;
					((Selectable)Tabs[i]).colors = colors;
				}
				int num = list?.Count ?? 0;
				Selectable[] fixedItems = _fixedItems;
				int num2 = ((fixedItems != null) ? fixedItems.Length : 0);
				List<Selectable> list2 = new List<Selectable>(num + num2);
				for (int k = 0; k < num; k++)
				{
					if ((Object)(object)list[k] != (Object)null)
					{
						list2.Add(list[k]);
					}
				}
				for (int l = 0; l < num2; l++)
				{
					if ((Object)(object)_fixedItems[l] != (Object)null)
					{
						list2.Add(_fixedItems[l]);
					}
				}
				if ((Object)(object)ManagedItemList != (Object)null)
				{
					GameObject[] array = (GameObject[])(object)new GameObject[list2.Count];
					for (int m = 0; m < list2.Count; m++)
					{
						array[m] = ((Component)list2[m]).gameObject;
					}
					ManagedItemList.items = Il2CppReferenceArray<GameObject>.op_Implicit(array);
				}
				for (int n = 0; n < list2.Count; n++)
				{
					Selectable val2 = list2[n];
					Navigation navigation = val2.navigation;
					navigation.mode = (Mode)4;
					navigation.selectOnUp = ((n > 0) ? list2[n - 1] : null);
					navigation.selectOnDown = ((n < list2.Count - 1) ? list2[n + 1] : null);
					navigation.selectOnLeft = null;
					navigation.selectOnRight = null;
					val2.navigation = navigation;
				}
			}
		}

		public sealed class TabBuilder
		{
			internal TabBuilder()
			{
			}

			public TMP_Text AddLabel(string text)
			{
				return MenuInjectionLibrary.AddLabel(text, (RectTransform)null);
			}

			public Button AddButton(string text, UnityAction onClick)
			{
				return MenuInjectionLibrary.AddButton(text, onClick, (RectTransform)null);
			}

			public Toggle AddToggle(string text, bool initial, UnityAction<bool> onChange)
			{
				return MenuInjectionLibrary.AddToggle(text, initial, onChange, (RectTransform)null);
			}

			public Slider AddSlider(string text, float min, float max, float value, UnityAction<float> onChange, bool wholeNumbers = false)
			{
				return MenuInjectionLibrary.AddSlider(text, min, max, value, onChange, wholeNumbers, (RectTransform)null);
			}

			public Button AddInputField(string placeholder = "", UnityAction<string> onChange = null, string initialValue = null)
			{
				return MenuInjectionLibrary.AddInputField(placeholder, onChange, initialValue, (RectTransform)null);
			}

			public Image AddImage(Color color, float height = 52f)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				return MenuInjectionLibrary.AddImage(color, height, (RectTransform)null);
			}
		}

		public sealed class MenuBuilder
		{
			internal readonly string _mainButtonLabel;

			internal readonly List<(string name, Action<TabBuilder> configure)> _tabs = new List<(string, Action<TabBuilder>)>();

			internal readonly List<(string label, UnityAction action)> _fixed = new List<(string, UnityAction)>();

			internal float _sideMargin;

			internal float _topMargin;

			internal MenuBuilder(string mainButtonLabel)
			{
				_mainButtonLabel = mainButtonLabel;
			}

			public MenuBuilder AddTab(string name, Action<TabBuilder> configure)
			{
				_tabs.Add((name, configure));
				return this;
			}

			public MenuBuilder AddFixedButton(string label, UnityAction action = null)
			{
				_fixed.Add((label, action));
				return this;
			}

			public MenuBuilder WithMargin(float sideMargin, float topMargin)
			{
				_sideMargin = sideMargin;
				_topMargin = topMargin;
				return this;
			}

			public InjectedMenu Build()
			{
				InjectedMenu injectedMenu = new InjectedMenu(this);
				if (!_registeredMenus.Contains(injectedMenu))
				{
					_registeredMenus.Add(injectedMenu);
				}
				return injectedMenu;
			}
		}

		internal sealed class InputFieldInfo
		{
			public string Value = "";

			public TMP_Text DisplayText;

			public UnityAction<string> OnChanged;

			public string Placeholder = "";

			public RectTransform Viewport;

			public Color ActiveColor = Color.white;

			public Color PlaceholderColor = new Color(1f, 1f, 1f, 0.45f);

			public Button OwnerButton;

			public Image CursorImage;
		}

		public sealed class InjectedMenu
		{
			[CompilerGenerated]
			private sealed class <EnforceKeyboardFontSizesCoroutine>d__73 : IEnumerator<object>, IEnumerator, IDisposable
			{
				private int <>1__state;

				private object <>2__current;

				public InjectedMenu <>4__this;

				private int <i>5__1;

				object IEnumerator<object>.Current
				{
					[DebuggerHidden]
					get
					{
						return <>2__current;
					}
				}

				object IEnumerator.Current
				{
					[DebuggerHidden]
					get
					{
						return <>2__current;
					}
				}

				[DebuggerHidden]
				public <EnforceKeyboardFontSizesCoroutine>d__73(int <>1__state)
				{
					this.<>1__state = <>1__state;
				}

				[DebuggerHidden]
				void IDisposable.Dispose()
				{
					<>1__state = -2;
				}

				private bool MoveNext()
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<i>5__1 = 0;
						break;
					case 1:
						<>1__state = -1;
						<i>5__1++;
						break;
					}
					if (<i>5__1 < 3)
					{
						<>4__this.ForceKeyboardFontSizes();
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					return false;
				}

				bool IEnumerator.MoveNext()
				{
					//ILSpy generated this explicit interface implementation from .override directive in MoveNext
					return this.MoveNext();
				}

				[DebuggerHidden]
				void IEnumerator.Reset()
				{
					throw new NotSupportedException();
				}
			}

			private readonly string _mainButtonLabel;

			private readonly List<(string name, Action<TabBuilder> configure)> _tabDescs;

			private readonly List<(string label, UnityAction action)> _fixedDescs;

			private Menu _activeMenu;

			private RuntimeTabMenu _tabMenu;

			private bool _contentBuilt;

			private bool _hasAttemptedInitialDump;

			private GameObject _mainButtonObj;

			private Button _mainButton;

			private Transform _mainMenuRoot;

			private CanvasGroup _mainMenuCG;

			private MenuItemList _mainMenuItemList;

			private GameObject _submenuObj;

			private CanvasGroup _submenuCG;

			private MenuItemList _submenuItemList;

			private readonly List<Button> _fixedButtonInstances = new List<Button>();

			private GameObject _kbRoot;

			private CanvasGroup _kbCG;

			private Button[][] _kbCharRows;

			private string[][] _kbLowerRows;

			private string[][] _kbUpperRows;

			private Button[] _kbSpecialRow;

			private readonly List<GameObject> _kbAllButtons = new List<GameObject>();

			private bool _kbShift;

			private bool _kbUseLowercaseFontForKeyLabels;

			private Button _kbShiftBtn;

			private InputFieldInfo _kbActiveField;

			private Button _kbReturnButton;

			private Action _kbOnDone;

			private bool _kbBuilt;

			private bool _kbHasEdited;

			private int _kbNavFrame = -1;

			private float _kbCursorTimer = 0f;

			private bool _kbCursorVisible = true;

			private InputFieldInfo _mouseTypingField;

			private bool _mouseTypingActive;

			private int _mouseTypingNavFrame = -1;

			private int _mouseCursorBlinkFrame = -1;

			private bool _mouseHasEdited;

			private float _mouseCursorTimer = 0f;

			private bool _mouseCursorVisible = true;

			private bool _kbUsingOverlay;

			private const float CanvasW = 860f;

			private const float CanvasH = 650f;

			private const float ContentH = 460f;

			private const float ContentPad = 10f;

			private readonly float _sideMargin;

			private readonly float _topMargin;

			private RectTransform _submenuRect;

			internal bool IsMouseTyping => _mouseTypingActive;

			private bool KbVisible => (Object)(object)_kbCG != (Object)null && _kbCG.alpha > 0.5f;

			internal bool IsKeyboardOpen => _kbActiveField != null;

			private bool IsKbButton(GameObject go)
			{
				return (Object)(object)go != (Object)null && _kbAllButtons.Contains(go);
			}

			internal InjectedMenu(MenuBuilder b)
			{
				_mainButtonLabel = b._mainButtonLabel;
				_tabDescs = b._tabs;
				_fixedDescs = b._fixed;
				_sideMargin = b._sideMargin;
				_topMargin = b._topMargin;
			}

			public Button GetFixedButton(int index)
			{
				return (index >= 0 && index < _fixedButtonInstances.Count) ? _fixedButtonInstances[index] : null;
			}

			public void OnMenuAwake(Menu menu)
			{
				_activeMenu = menu;
				TryInject(menu, "Awake");
			}

			public void OnMenuPreUpdate(Menu menu)
			{
				//IL_0485: Unknown result type (might be due to invalid IL or missing references)
				//IL_048b: Invalid comparison between Unknown and I4
				_activeMenu = menu;
				if ((Object)(object)menu == (Object)null)
				{
					return;
				}
				if ((Object)(object)_mainButtonObj == (Object)null || (Object)(object)_submenuObj == (Object)null)
				{
					TryInject(menu, "PreUpdateRetry");
				}
				bool submenuVisible = GetSubmenuVisible();
				bool kbVisible = KbVisible;
				bool isCapturingKeybind = MultiplayerMenu.IsCapturingKeybind;
				if (kbVisible)
				{
					HandleKeyboardInput(menu, isCapturingKeybind, submenuVisible);
				}
				if (submenuVisible && !kbVisible && !isCapturingKeybind)
				{
					TryHandleCancel(menu);
					if (_mouseTypingActive && _mouseTypingField != null && Time.frameCount != _mouseTypingNavFrame)
					{
						_mouseTypingNavFrame = Time.frameCount;
						bool flag = false;
						string inputString = Input.inputString;
						for (int i = 0; i < inputString.Length; i++)
						{
							char c = inputString[i];
							switch (c)
							{
							case '\b':
								if (_mouseTypingField.Value.Length > 0)
								{
									_mouseTypingField.Value = _mouseTypingField.Value.Substring(0, _mouseTypingField.Value.Length - 1);
									flag = true;
								}
								continue;
							default:
								if (c != '\n')
								{
									_mouseTypingField.Value += c;
									flag = true;
									continue;
								}
								break;
							case '\r':
								break;
							}
							_mouseTypingField.OnChanged?.Invoke(_mouseTypingField.Value);
							CloseMouseTyping(restoreSelection: false);
							flag = false;
							break;
						}
						if (Input.GetKeyDown((KeyCode)27) && _mouseTypingActive)
						{
							CloseMouseTyping();
							flag = false;
						}
						if (Input.GetMouseButtonDown(0) && _mouseTypingActive && (Object)(object)_mouseTypingField?.OwnerButton != (Object)null && !IsPointerOverGameObject(((Component)_mouseTypingField.OwnerButton).gameObject))
						{
							CloseMouseTyping(restoreSelection: false);
							flag = false;
						}
						if (flag && _mouseTypingField != null)
						{
							_mouseHasEdited = true;
							RefreshMouseTypingDisplay();
							_mouseTypingField.OnChanged?.Invoke(_mouseTypingField.Value);
						}
					}
					if (_mouseTypingActive && (Object)(object)_mouseTypingField?.DisplayText != (Object)null && Time.frameCount != _mouseCursorBlinkFrame)
					{
						_mouseCursorBlinkFrame = Time.frameCount;
						_mouseCursorTimer += Time.unscaledDeltaTime;
						if (_mouseCursorTimer >= 0.53f)
						{
							_mouseCursorTimer = 0f;
							_mouseCursorVisible = !_mouseCursorVisible;
							RefreshMouseTypingDisplay();
						}
					}
					if (_tabMenu != null && menu.rwPlayer != null)
					{
						if (menu.rwPlayer.GetButtonDown(18))
						{
							_tabMenu.SetActiveTab((_tabMenu.ActiveTab - 1 + _tabMenu.TabCount) % _tabMenu.TabCount);
						}
						else if (menu.rwPlayer.GetButtonDown(17))
						{
							_tabMenu.SetActiveTab((_tabMenu.ActiveTab + 1) % _tabMenu.TabCount);
						}
					}
				}
				if (kbVisible)
				{
					if (submenuVisible && (Object)(object)_submenuItemList != (Object)null)
					{
						MenuItemList.active = _submenuItemList;
					}
				}
				else if (submenuVisible)
				{
					if ((Object)(object)_submenuItemList != (Object)null)
					{
						MenuItemList.active = _submenuItemList;
					}
					if (_mouseTypingActive && (Object)(object)_mouseTypingField?.OwnerButton != (Object)null && (Object)(object)_submenuItemList != (Object)null)
					{
						_submenuItemList.items = Il2CppReferenceArray<GameObject>.op_Implicit((GameObject[])(object)new GameObject[1] { ((Component)_mouseTypingField.OwnerButton).gameObject });
					}
				}
				else if ((int)menu.currentMenuScreen == 0 && (Object)(object)_mainMenuItemList != (Object)null)
				{
					MenuItemList.active = _mainMenuItemList;
				}
			}

			private void HandleKeyboardInput(Menu menu, bool isCapturingKeybind, bool submenuVisible)
			{
				EventSystem current = EventSystem.current;
				GameObject val = ((current != null) ? current.currentSelectedGameObject : null);
				if (((Object)(object)val == (Object)null || !IsKbButton(val)) && _kbCharRows != null && _kbCharRows.Length != 0)
				{
					val = ((Component)_kbCharRows[0][0]).gameObject;
					if (current != null)
					{
						current.SetSelectedGameObject(val);
					}
				}
				if (!isCapturingKeybind && Time.frameCount != _kbNavFrame)
				{
					_kbNavFrame = Time.frameCount;
					if ((Object)(object)val != (Object)null && IsKbButton(val) && menu.rwPlayer != null)
					{
						float axis = menu.rwPlayer.GetAxis(12);
						float axisPrev = menu.rwPlayer.GetAxisPrev(12);
						float axis2 = menu.rwPlayer.GetAxis(13);
						float axisPrev2 = menu.rwPlayer.GetAxisPrev(13);
						Selectable component = val.GetComponent<Selectable>();
						Selectable val2 = null;
						if (axis2 < -0.5f && axisPrev2 >= -0.5f)
						{
							val2 = ((component != null) ? component.navigation.selectOnDown : null);
						}
						else if (axis2 > 0.5f && axisPrev2 <= 0.5f)
						{
							val2 = ((component != null) ? component.navigation.selectOnUp : null);
						}
						else if (axis > 0.5f && axisPrev <= 0.5f)
						{
							val2 = ((component != null) ? component.navigation.selectOnRight : null);
						}
						else if (axis < -0.5f && axisPrev >= -0.5f)
						{
							val2 = ((component != null) ? component.navigation.selectOnLeft : null);
						}
						if ((Object)(object)val2 != (Object)null)
						{
							if (current != null)
							{
								current.SetSelectedGameObject(((Component)val2).gameObject);
							}
							val = ((Component)val2).gameObject;
						}
						if (_kbUsingOverlay && (Input.GetKeyDown((KeyCode)330) || Input.GetKeyDown((KeyCode)331)))
						{
							Button val3 = ((val != null) ? val.GetComponent<Button>() : null);
							if (val3 != null)
							{
								((UnityEvent)val3.onClick).Invoke();
							}
						}
					}
					if (menu.rwPlayer != null && (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown((KeyCode)332) || menu.rwPlayer.GetButtonDown(15)))
					{
						CloseKeyboardOverlay((Object)(object)_kbReturnButton == (Object)null && _kbOnDone != null);
					}
					if ((Object)(object)_kbActiveField?.DisplayText != (Object)null)
					{
						_kbCursorTimer += Time.unscaledDeltaTime;
						if (_kbCursorTimer >= 0.53f)
						{
							_kbCursorTimer = 0f;
							_kbCursorVisible = !_kbCursorVisible;
							KbBlinkCursor();
						}
					}
				}
				if (submenuVisible && (Object)(object)_submenuItemList != (Object)null && (Object)(object)val != (Object)null)
				{
					_submenuItemList.items = Il2CppReferenceArray<GameObject>.op_Implicit((GameObject[])(object)new GameObject[1] { val });
				}
			}

			public void OnMenuUpdate(Menu menu)
			{
				_activeMenu = menu;
				if (!((Object)(object)menu == (Object)null))
				{
					if (Input.GetKeyDown((KeyCode)289))
					{
					}
					if (Input.GetKeyDown((KeyCode)290))
					{
						Reset();
						TryInject(menu, "Reinject_F9");
					}
					if ((Object)(object)_mainButtonObj == (Object)null || (Object)(object)_submenuObj == (Object)null)
					{
						TryInject(menu, "UpdateRetry");
					}
					UpdateVisibility(menu);
					EnsureSelection();
				}
			}

			private void TryInject(Menu menu, string reason)
			{
				if ((Object)(object)menu == (Object)null)
				{
					return;
				}
				ResolveExisting(menu);
				if (!_hasAttemptedInitialDump)
				{
					_hasAttemptedInitialDump = true;
				}
				if ((Object)(object)menu.mainMenuCanvas == (Object)null)
				{
					return;
				}
				Button val = FindTemplateButton(menu);
				if (!((Object)(object)val == (Object)null))
				{
					_mainMenuRoot = ((Component)val).transform.parent;
					if ((Object)(object)_mainMenuRoot != (Object)null)
					{
						_mainMenuCG = ((Component)_mainMenuRoot).GetComponent<CanvasGroup>() ?? ((Component)_mainMenuRoot).gameObject.AddComponent<CanvasGroup>();
						_mainMenuItemList = ((Component)_mainMenuRoot).GetComponent<MenuItemList>();
					}
					if ((Object)(object)_mainButtonObj == (Object)null)
					{
						_mainButtonObj = BuildMainButton(menu, val);
					}
					if ((Object)(object)_submenuObj == (Object)null)
					{
						_submenuObj = BuildSubmenu(menu, val);
					}
					IntegrateIntoMainItemList();
					EnsureTabContent(menu, val);
					UpdateVisibility(menu);
				}
			}

			private void EnsureTabContent(Menu menu, Button tmpl)
			{
				if (_contentBuilt || (Object)(object)_submenuObj == (Object)null)
				{
					return;
				}
				Transform obj = _submenuObj.transform.Find("CustomSettingsContentRoot");
				RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				Button nativeTabSource = null;
				Menu me = Menu.me;
				if ((Object)(object)((me != null) ? me.tabs : null) != (Object)null)
				{
					Transform transform = ((Component)Menu.me.tabs).transform;
					for (int i = 0; i < transform.childCount; i++)
					{
						Transform child = transform.GetChild(i);
						Button val2 = ((child != null) ? ((Component)child).GetComponent<Button>() : null);
						if ((Object)(object)val2 != (Object)null)
						{
							nativeTabSource = val2;
							break;
						}
					}
				}
				string[] array = new string[_tabDescs.Count];
				for (int j = 0; j < _tabDescs.Count; j++)
				{
					array[j] = _tabDescs[j].name;
				}
				_tabMenu = BuildNativeTabMenu(array, val, nativeTabSource, tmpl);
				_tabMenu.ManagedItemList = _submenuItemList;
				Selectable[] array2 = (Selectable[])(object)new Selectable[_fixedButtonInstances.Count];
				for (int k = 0; k < _fixedButtonInstances.Count; k++)
				{
					array2[k] = (Selectable)(object)_fixedButtonInstances[k];
				}
				_tabMenu.SetFixedMenuItems(array2);
				Toggle toggle = FindToggleTemplate(menu);
				Slider slider = FindSliderTemplate(menu);
				TabBuilder obj2 = new TabBuilder();
				_currentInjectedMenu = this;
				for (int l = 0; l < _tabDescs.Count; l++)
				{
					ConfigureTabPage(_tabMenu, l, tmpl, toggle, slider);
					_tabDescs[l].configure?.Invoke(obj2);
				}
				_currentInjectedMenu = null;
				BuildKeyboard(menu, tmpl);
				_tabMenu.SetActiveTab(0);
				_contentBuilt = true;
			}

			private GameObject BuildMainButton(Menu menu, Button tmpl)
			{
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				//IL_007c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0089: Unknown result type (might be due to invalid IL or missing references)
				//IL_0096: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
				GameObject val = Object.Instantiate<GameObject>(((Component)tmpl).gameObject, ((Component)tmpl).transform.parent);
				((Object)val).name = "BBSMP_MainMenuButton";
				StripLocalizationComponents(val);
				val.transform.SetSiblingIndex(((Component)tmpl).transform.GetSiblingIndex() + 1);
				RectTransform component = val.GetComponent<RectTransform>();
				RectTransform component2 = ((Component)tmpl).GetComponent<RectTransform>();
				if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null)
				{
					component.anchorMin = component2.anchorMin;
					component.anchorMax = component2.anchorMax;
					component.pivot = component2.pivot;
					component.sizeDelta = component2.sizeDelta;
					((Transform)component).localScale = Vector3.one;
					float num = Mathf.Max(45f, Mathf.Abs(component2.sizeDelta.y) + 10f);
					component.anchoredPosition = component2.anchoredPosition + new Vector2(0f, num);
				}
				_mainButton = val.GetComponent<Button>();
				ClearButtonEvents(_mainButton);
				Button mainButton = _mainButton;
				if (mainButton != null)
				{
					((UnityEvent)mainButton.onClick).AddListener(UnityAction.op_Implicit((Action)delegate
					{
						SetSubmenuVisible(isVisible: true);
					}));
				}
				SetButtonLabel(val, _mainButtonLabel);
				return val;
			}

			private GameObject BuildSubmenu(Menu menu, Button tmpl)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Expected O, but got Unknown
				//IL_0042: 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)
				//IL_006e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: Unknown result type (might be due to invalid IL or missing references)
				//IL_009a: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e7: Expected O, but got Unknown
				//IL_010c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0122: Unknown result type (might be due to invalid IL or missing references)
				//IL_0138: Unknown result type (might be due to invalid IL or missing references)
				//IL_014e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0164: Unknown result type (might be due to invalid IL or missing references)
				//IL_018f: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_028c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0299: Unknown result type (might be due to invalid IL or missing references)
				//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
				//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
				GameObject val = new GameObject("BBSMP_SubmenuCanvas");
				val.transform.SetParent(menu.mainMenuCanvas.transform, false);
				val.transform.SetAsLastSibling();
				RectTransform val2 = val.AddComponent<RectTransform>();
				val2.anchorMin = new Vector2(0f, 1f);
				val2.anchorMax = new Vector2(0f, 1f);
				val2.pivot = new Vector2(0f, 1f);
				val2.sizeDelta = new Vector2(860f, 650f);
				val2.anchoredPosition = new Vector2(10f, -10f);
				_submenuCG = val.AddComponent<CanvasGroup>();
				_submenuCG.alpha = 0f;
				_submenuCG.blocksRaycasts = false;
				_submenuCG.interactable = false;
				GameObject val3 = new GameObject("CustomSettingsContentRoot");
				val3.transform.SetParent(val.transform, false);
				RectTransform val4 = val3.AddComponent<RectTransform>();
				val4.anchorMin = new Vector2(0f, 1f);
				val4.anchorMax = new Vector2(0f, 1f);
				val4.pivot = new Vector2(0f, 1f);
				val4.sizeDelta = new Vector2(800f, 460f);
				val4.anchoredPosition = new Vector2(10f, -10f);
				RectTransform component = ((Component)tmpl).GetComponent<RectTransform>();
				float num = (((Object)(object)component != (Object)null) ? Mathf.Max(200f, component.sizeDelta.x) : 520f);
				float num2 = (((Object)(object)component != (Object)null) ? Mathf.Clamp(component.sizeDelta.y, 40f, 72f) : 52f);
				_fixedButtonInstances.Clear();
				for (int num3 = _fixedDescs.Count - 1; num3 >= 0; num3--)
				{
					(string label, UnityAction action) tuple = _fixedDescs[num3];
					string item = tuple.label;
					UnityAction item2 = tuple.action;
					GameObject val5 = Object.Instantiate<GameObject>(((Component)tmpl).gameObject, val.transform);
					((Object)val5).name = $"BBSMP_Fixed_{num3}";
					StripLocalizationComponents(val5);
					Button component2 = val5.GetComponent<Button>();
					ClearButtonEvents(component2);
					SetButtonLabel(val5, item);
					RectTransform component3 = val5.GetComponent<RectTransform>();
					component3.anchorMin = Vector2.zero;
					component3.anchorMax = Vector2.zero;
					component3.pivot = Vector2.zero;
					component3.sizeDelta = new Vector2(num, num2);
					int num4 = _fixedDescs.Count - 1 - num3;
					component3.anchoredPosition = new Vector2(10f, 10f + (float)num4 * (num2 + 10f));
					UnityAction capturedAction = item2;
					if (component2 != null)
					{
						((UnityEvent)component2.onClick).AddListener(UnityAction.op_Implicit((Action)delegate
						{
							UnityAction obj = capturedAction;
							if (obj != null)
							{
								obj.Invoke();
							}
							SetSubmenuVisible(isVisible: false);
						}));
					}
					_fixedButtonInstances.Insert(0, component2);
				}
				_submenuItemList = val.AddComponent<MenuItemList>();
				_submenuItemList.items = Il2CppReferenceArray<GameObject>.op_Implicit((GameObject[])(object)new GameObject[0]);
				_submenuRect = val2;
				ApplyMargin();
				return val;
			}

			private void BuildKeyboard(Menu menu, Button tmpl, bool useLowercaseFontForLetterKeys = true)
			{
				//IL_037c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0386: Expected O, but got Unknown
				//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
				//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_040e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0425: Unknown result type (might be due to invalid IL or missing references)
				//IL_0453: Unknown result type (might be due to invalid IL or missing references)
				//IL_05f9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0610: Unknown result type (might be due to invalid IL or missing references)
				//IL_0627: Unknown result type (might be due to invalid IL or missing references)
				//IL_063b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0659: Unknown result type (might be due to invalid IL or missing references)
				//IL_0879: Unknown result type (might be due to invalid IL or missing references)
				//IL_0890: Unknown result type (might be due to invalid IL or missing references)
				//IL_08a7: Unknown result type (might be due to invalid IL or missing references)
				//IL_08be: Unknown result type (might be due to invalid IL or missing references)
				//IL_08cf: Unknown result type (might be due to invalid IL or missing references)
				if (_kbBuilt || (Object)(object)((menu != null) ? menu.mainMenuCanvas : null) == (Object)null || (Object)(object)tmpl == (Object)null)
				{
					return;
				}
				_kbUseLowercaseFontForKeyLabels = useLowercaseFontForLetterKeys;
				_kbLowerRows = new string[4][]
				{
					new string[11]
					{
						"1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
						"."
					},
					new string[10] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p" },
					new string[9] { "a", "s", "d", "f", "g", "h", "j", "k", "l" },
					new string[7] { "z", "x", "c", "v", "b", "n", "m" }
				};
				_kbUpperRows = new string[4][]
				{
					new string[11]
					{
						"1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
						"."
					},
					new string[10] { "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P" },
					new string[9] { "A", "S", "D", "F", "G", "H", "J", "K", "L" },
					new string[7] { "Z", "X", "C", "V", "B", "N", "M" }
				};
				int num = _kbLowerRows.Length + 1;
				float num2 = 20f + (float)num * 44f + (float)(num - 1) * 4f - 15f;
				float num3 = 780f;
				int num4 = 0;
				for (int i = 0; i < _kbLowerRows.Length; i++)
				{
					if (_kbLowerRows[i].Length > num4)
					{
						num4 = _kbLowerRows[i].Length;
					}
				}
				float num5 = (num3 - (float)(num4 - 1) * 6f) / (float)num4;
				_kbRoot = new GameObject("BBSMP_Keyboard");
				_kbRoot.transform.SetParent(menu.mainMenuCanvas.transform, false);
				_kbRoot.transform.SetAsLastSibling();
				RectTransform val = _kbRoot.AddComponent<RectTransform>();
				val.anchorMin = new Vector2(0.5f, 0f);
				val.anchorMax = new Vector2(0.5f, 0f);
				val.pivot = new Vector2(0.5f, 0f);
				val.sizeDelta = new Vector2(800f, num2);
				val.anchoredPosition = new Vector2(0f, 100f);
				Image val2 = _kbRoot.AddComponent<Image>();
				((Graphic)val2).color = new Color(0.05f, 0.11f, 0.18f, 0.97f);
				_kbCG = _kbRoot.AddComponent<CanvasGroup>();
				_kbCG.alpha = 0f;
				_kbCG.interactable = false;
				_kbCG.blocksRaycasts = false;
				_kbCharRows = new Button[_kbLowerRows.Length][];
				float num6 = -10f;
				for (int j = 0; j < _kbLowerRows.Length; j++)
				{
					int num7 = _kbLowerRows[j].Length;
					float num8 = num5;
					float num9 = (float)num7 * num8 + (float)(num7 - 1) * 6f;
					float num10 = (0f - num9) * 0.5f;
					_kbCharRows[j] = (Button[])(object)new Button[num7];
					for (int k = 0; k < num7; k++)
					{
						int capturedRow = j;
						int capturedCol = k;
						GameObject val3 = Object.Instantiate<GameObject>(((Component)tmpl).gameObject, _kbRoot.transform);
						((Object)val3).name = "BBSMP_Key_" + _kbUpperRows[j][k];
						StripLocalizationComponents(val3);
						Button component = val3.GetComponent<Button>();
						ClearButtonEvents(component);
						if ((Object)(object)component != (Object)null)
						{
							((UnityEvent)component.onClick).AddListener(UnityAction.op_Implicit((Action)delegate
							{
								KbType(capturedRow, capturedCol);
							}));
						}
						SetButtonLabel(val3, _kbLowerRows[j][k], _kbUseLowercaseFontForKeyLabels);
						TMP_Text componentInChildren = val3.GetComponentInChildren<TMP_Text>(true);
						if ((Object)(object)componentInChildren != (Object)null)
						{
							componentInChildren.fontSize = 20f;
						}
						RectTransform component2 = val3.GetComponent<RectTransform>();
						component2.anchorMin = new Vector2(0.5f, 1f);
						component2.anchorMax = new Vector2(0.5f, 1f);
						component2.pivot = new Vector2(0f, 1f);
						component2.sizeDelta = new Vector2(num8, 44f);
						component2.anchoredPosition = new Vector2(num10 + (float)k * (num8 + 6f), num6);
						_kbCharRows[j][k] = component;
						_kbAllButtons.Add(val3);
					}
					num6 -= 48f;
				}
				float num11 = num3 - 216f - 18f;
				float[] obj = new float[4] { 72f, 0f, 72f, 72f };
				obj[1] = num11;
				float[] array = obj;
				string[] array2 = new string[4] { "Shift", "Space", "Back", "Done" };
				_kbSpecialRow = (Button[])(object)new Button[4];
				float num12 = (0f - num3) * 0.5f;
				for (int l = 0; l < 4; l++)
				{
					GameObject val4 = Object.Instantiate<GameObject>(((Component)tmpl).gameObject, _kbRoot.transform);
					((Object)val4).name = "BBSMP_Key_" + array2[l];
					StripLocalizationComponentsImmediate(val4);
					Button component3 = val4.GetComponent<Button>();
					ClearButtonEvents(component3);
					if ((Object)(object)component3 != (Object)null)
					{
						switch (l)
						{
						case 0:
							((UnityEvent)component3.onClick).AddListener(UnityAction.op_Implicit((Action)KbToggleShift));
							_kbShiftBtn = component3;
							break;
						case 1:
							((UnityEvent)component3.onClick).AddListener(UnityAction.op_Implicit((Action)KbSpace));
							break;
						case 2:
							((UnityEvent)component3.onClick).AddListener(UnityAction.op_Implicit((Action)KbBackspace));
							break;
						case 3:
							((UnityEvent)component3.onClick).AddListener(UnityAction.op_Implicit((Action)KbDone));
							break;
						}
					}
					SetButtonLabel(val4, array2[l]);
					TMP_Text componentInChildren2 = val4.GetComponentInChildren<TMP_Text>(true);
					if ((Object)(object)componentInChildren2 != (Object)null)
					{
						componentInChildren2.fontSize = 20f;
					}
					RectTransform component4 = val4.GetComponent<RectTransform>();
					component4.anchorMin = new Vector2(0.5f, 1f);
					component4.anchorMax = new Vector2(0.5f, 1f);
					component4.pivot = new Vector2(0f, 1f);
					component4.sizeDelta = new Vector2(array[l], 44f);
					component4.anchoredPosition = new Vector2(num12, num6);
					num12 += array[l] + 6f;
					_kbSpecialRow[l] = component3;
					_kbAllButtons.Add(val4);
				}
				WireKeyboardNavigation();
				ForceKeyboardFontSizes();
				MelonCoroutines.Start(EnforceKeyboardFontSizesCoroutine());
				_kbBuilt = true;
			}

			private void WireKeyboardNavigation()
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Expected O, but got Unknown
				//IL_0171: Unknown result type (might be due to invalid IL or missing references)
				//IL_0176: Unknown result type (might be due to invalid IL or missing references)
				//IL_0180: Expected O, but got Unknown
				for (int i = 0; i < _kbCharRows.Length; i++)
				{
					int num = _kbCharRows[i].Length;
					for (int j = 0; j < num; j++)
					{
						Navigation val = new Navigation
						{
							mode = (Mode)0
						};
						val.selectOnLeft = (Selectable)(object)((j > 0) ? _kbCharRows[i][j - 1] : null);
						val.selectOnRight = (Selectable)(object)((j < num - 1) ? _kbCharRows[i][j + 1] : null);
						val.selectOnUp = (Selectable)(object)((i > 0) ? _kbCharRows[i - 1][Mathf.Min(j, _kbCharRows[i - 1].Length - 1)] : null);
						if (i < _kbCharRows.Length - 1)
						{
							val.selectOnDown = (Selectable)(object)_kbCharRows[i + 1][Mathf.Min(j, _kbCharRows[i + 1].Length - 1)];
						}
						else
						{
							int num2 = Mathf.Clamp(j * _kbSpecialRow.Length / _kbCharRows[i].Length, 0, _kbSpecialRow.Length - 1);
							val.selectOnDown = (Selectable)(object)_kbSpecialRow[num2];
						}
						((Selectable)_kbCharRows[i][j]).navigation = val;
					}
				}
				int num3 = _kbCharRows.Length - 1;
				int num4 = _kbCharRows[num3].Length;
				for (int k = 0; k < _kbSpecialRow.Length; k++)
				{
					Navigation val2 = new Navigation
					{
						mode = (Mode)0
					};
					val2.selectOnLeft = (Selectable)(object)((k > 0) ? _kbSpecialRow[k - 1] : null);
					val2.selectOnRight = (Selectable)(object)((k < _kbSpecialRow.Length - 1) ? _kbSpecialRow[k + 1] : null);
					val2.selectOnDown = null;
					int num5 = Mathf.Clamp(k * num4 / _kbSpecialRow.Length, 0, num4 - 1);
					val2.selectOnUp = (Selectable)(object)_kbCharRows[num3][num5];
					((Selectable)_kbSpecialRow[k]).navigation = val2;
				}
			}

			private void KbType(int row, int col)
			{
				if (_kbActiveField != null)
				{
					string[][] array = (_kbShift ? _kbUpperRows : _kbLowerRows);
					_kbActiveField.Value += array[row][col];
					_kbHasEdited = true;
					KbRefreshDisplay();
					_kbActiveField.OnChanged?.Invoke(_kbActiveField.Value);
					if (_kbShift)
					{
						_kbShift = false;
						KbRefreshKeyLabels();
					}
				}
			}

			private void KbSpace()
			{
				if (_kbActiveField != null)
				{
					_kbActiveField.Value += " ";
					_kbHasEdited = true;
					KbRefreshDisplay();
					_kbActiveField.OnChanged?.Invoke(_kbActiveField.Value);
				}
			}

			private void KbBackspace()
			{
				if (_kbActiveField != null && _kbActiveField.Value.Length != 0)
				{
					_kbActiveField.Value = _kbActiveField.Value.Substring(0, _kbActiveField.Value.Length - 1);
					_kbHasEdited = true;
					KbRefreshDisplay();
					_kbActiveField.OnChanged?.Invoke(_kbActiveField.Value);
				}
			}

			private void KbToggleShift()
			{
				_kbShift = !_kbShift;
				KbRefreshKeyLabels();
			}

			private void KbRefreshKeyLabels()
			{
				if (_kbCharRows == null)
				{
					return;
				}
				string[][] array = (_kbShift ? _kbUpperRows : _kbLowerRows);
				for (int i = 0; i < _kbCharRows.Length; i++)
				{
					for (int j = 0; j < _kbCharRows[i].Length; j++)
					{
						SetButtonLabel(((Component)_kbCharRows[i][j]).gameObject, array[i][j], _kbUseLowercaseFontForKeyLabels);
					}
				}
				if ((Object)(object)_kbShiftBtn != (Object)null)
				{
					SetButtonLabel(((Component)_kbShiftBtn).gameObject, _kbShift ? "SHIFT" : "Shift");
					ForceKeyboardFontSize(((Component)_kbShiftBtn).gameObject);
				}
			}

			private void ForceKeyboardFontSizes()
			{
				if (_kbAllButtons.Count != 0)
				{
					for (int i = 0; i < _kbAllButtons.Count; i++)
					{
						ForceKeyboardFontSize(_kbAllButtons[i]);
					}
				}
			}

			private static void ForceKeyboardFontSize(GameObject keyObj)
			{
				if (!((Object)(object)keyObj == (Object)null))
				{
					TMP_Text componentInChildren = keyObj.GetComponentInChildren<TMP_Text>(true);
					if (!((Object)(object)componentInChildren == (Object)null))
					{
						componentInChildren.fontSize = 20f;
						componentInChildren.ForceMeshUpdate(false, false);
					}
				}
			}

			[IteratorStateMachine(typeof(<EnforceKeyboardFontSizesCoroutine>d__73))]
			private IEnumerator EnforceKeyboardFontSizesCoroutine()
			{
				//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
				return new <EnforceKeyboardFontSizesCoroutine>d__73(0)
				{
					<>4__this = this
				};
			}

			private void KbRefreshDisplay()
			{
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_0116: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
				InputFieldInfo kbActiveField = _kbActiveField;
				if (kbActiveField != null && !((Object)(object)kbActiveField.DisplayText == (Object)null))
				{
					TMP_Text displayText = kbActiveField.DisplayText;
					_kbCursorVisible = true;
					_kbCursorTimer = 0f;
					ApplyCaretText(kbActiveField, _kbCursorVisible, _kbHasEdited);
					displayText.ForceMeshUpdate(false, false);
					float num = 0f;
					RectTransform rectTransform = displayText.rectTransform;
					if ((Object)(object)rectTransform != (Object)null && (Object)(object)kbActiveField.Viewport != (Object)null)
					{
						Rect rect = kbActiveField.Viewport.rect;
						float width = ((Rect)(ref rect)).width;
						float displayWidthForRenderedText = GetDisplayWidthForRenderedText(displayText, displayText.text);
						num = ((width > 1f) ? Mathf.Max(0f, displayWidthForRenderedText - width) : 0f);
						rectTransform.offsetMin = new Vector2(0f - num, rectTransform.offsetMin.y);
						rectTransform.offsetMax = new Vector2(0f - num, rectTransform.offsetMax.y);
					}
					if ((Object)(object)kbActiveField.CursorImage != (Object)null)
					{
						((Graphic)kbActiveField.CursorImage).color = Color.clear;
					}
				}
			}

			private void KbBlinkCursor()
			{
				//IL_0072: Unknown result type (might be due to invalid IL or missing references)
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00da: Unknown result type (might be due to invalid IL or missing references)
				InputFieldInfo kbActiveField = _kbActiveField;
				if (!((Object)(object)kbActiveField?.DisplayText == (Object)null))
				{
					TMP_Text displayText = kbActiveField.DisplayText;
					ApplyCaretText(kbActiveField, _kbCursorVisible, _kbHasEdited);
					displayText.ForceMeshUpdate(false, false);
					RectTransform rectTransform = displayText.rectTransform;
					if ((Object)(object)rectTransform != (Object)null && (Object)(object)kbActiveField.Viewport != (Object)null)
					{
						Rect rect = kbActiveField.Viewport.rect;
						float width = ((Rect)(ref rect)).width;
						float displayWidthForRenderedText = GetDisplayWidthForRenderedText(displayText, displayText.text);
						float num = ((width > 1f) ? Mathf.Max(0f, displayWidthForRenderedText - width) : 0f);
						rectTransform.offsetMin = new Vector2(0f - num, rectTransform.offsetMin.y);
						rectTransform.offsetMax = new Vector2(0f - num, rectTransform.offsetMax.y);
					}
				}
			}

			internal void OpenKeyboard(InputFieldInfo info, Button returnButton)
			{
				OpenKeyboard(info, returnButton, null, useOverlay: false);
			}

			internal void OpenKeyboard(InputFieldInfo info, Button returnButton, Action onDone)
			{
				OpenKeyboard(info, returnButton, onDone, useOverlay: false);
			}

			internal void OpenKeyboardForChat(InputFieldInfo info, Action onDone)
			{
				OpenKeyboard(info, null, onDone, useOverlay: true);
			}

			private void OpenKeyboard(InputFieldInfo info, Button returnButton, Action onDone, bool useOverlay)
			{
				if (_kbBuilt)
				{
					_kbActiveField = info;
					_kbReturnButton = returnButton;
					_kbOnDone = onDone;
					_kbUsingOverlay = useOverlay;
					_kbShift = false;
					_kbHasEdited = false;
					_kbCursorVisible = true;
					_kbCursorTimer = 0f;
					if (useOverlay && (Object)(object)_kbRoot != (Object)null)
					{
						RectTransform val = EnsureKeyboardOverlayRoot();
						_kbRoot.transform.SetParent((Transform)(object)val, false);
						_kbRoot.transform.SetAsLastSibling();
						ShowKeyboardOverlay();
					}
					KbRefreshKeyLabels();
					KbRefreshDisplay();
					SetKeyboardVisible(visible: true);
				}
			}

			internal void CloseKeyboardForChat()
			{
				CloseKeyboard();
			}

			internal void CloseKeyboardOverlay()
			{
				CloseKeyboardOverlay(dismissChatTab: false);
			}

			internal void CloseKeyboardOverlay(bool dismissChatTab)
			{
				CloseKeyboard();
				HideKeyboardOverlay();
				if (dismissChatTab)
				{
					Core.uiManager.showChatTab = false;
				}
			}

			private void CloseKeyboard()
			{
				//IL_0088: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00df: Unknown result type (might be due to invalid IL or missing references)
				//IL_010d: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_kbActiveField?.DisplayText != (Object)null)
				{
					TMP_Text displayText = _kbActiveField.DisplayText;
					if (_kbActiveField.Value.Length > 0)
					{
						displayText.text = _kbActiveField.Value;
						((Graphic)displayText).color = _kbActiveField.ActiveColor;
					}
					else
					{
						displayText.text = _kbActiveField.Placeholder;
						((Graphic)displayText).color = _kbActiveField.PlaceholderColor;
					}
					displayText.ForceMeshUpdate(false, false);
					RectTransform rectTransform = displayText.rectTransform;
					if ((Object)(object)rectTransform != (Object)null)
					{
						rectTransform.offsetMin = new Vector2(0f, rectTransform.offsetMin.y);
						rectTransform.offsetMax = new Vector2(0f, rectTransform.offsetMax.y);
					}
					if ((Object)(object)_kbActiveField.CursorImage != (Object)null)
					{
						((Graphic)_kbActiveField.CursorImage).color = Color.clear;
					}
				}
				SetKeyboardVisible(visible: false);
				_tabMenu?.SetActiveTab(_tabMenu.ActiveTab);
				Button kbReturnButton = _kbReturnButton;
				_kbActiveField = null;
				_kbReturnButton = null;
				_kbOnDone = null;
				if ((Object)(object)kbReturnButton != (Object)null)
				{
					EventSystem current = EventSystem.current;
					if (current != null)
					{
						current.SetSelectedGameObject(((Component)kbReturnButton).gameObject);
					}
				}
			}

			private void KbDone()
			{
				_kbOnDone?.Invoke();
				CloseKeyboard();
			}

			internal void OnInputFieldButtonClick(InputFieldInfo info, Button btn)
			{
				if (!IsMouseTyping)
				{
					if (Input.GetMouseButtonUp(0))
					{
						OpenMouseTyping(info);
					}
					else
					{
						OpenKeyboard(info, btn);
					}
				}
			}

			internal void OpenMouseTyping(InputFieldInfo info)
			{
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				_mouseTypingField = info;
				_mouseTypingActive = true;
				_mouseTypingNavFrame = -1;
				_mouseCursorBlinkFrame = -1;
				_mouseHasEdited = false;
				_mouseCursorTimer = 0f;
				_mouseCursorVisible = true;
				if ((Object)(object)info?.CursorImage != (Object)null)
				{
					((Graphic)info.CursorImage).color = Color.clear;
				}
				EventSystem current = EventSystem.current;
				if (current != null)
				{
					current.SetSelectedGameObject((GameObject)null);
				}
				RefreshMouseTypingDisplay();
			}

			private void CloseMouseTyping(bool restoreSelection = true)
			{
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0108: Unknown result type (might be due to invalid IL or missing references)
				//IL_0112: Unknown result type (might be due to invalid IL or missing references)
				//IL_0124: Unknown result type (might be due to invalid IL or missing references)
				//IL_012e: Unknown result type (might be due to invalid IL or missing references)
				_mouseTypingActive = false;
				InputFieldInfo mouseTypingField = _mouseTypingField;
				_mouseTypingField = null;
				if (restoreSelection && (Object)(object)mouseTypingField?.OwnerButton != (Object)null)
				{
					EventSystem current = EventSystem.current;
					if (current != null)
					{
						current.SetSelectedGameObject(((Component)mouseTypingField.OwnerButton).gameObject);
					}
				}
				if ((Object)(object)mouseTypingField.CursorImage != (Object)null)
				{
					((Graphic)mouseTypingField.CursorImage).color = Color.clear;
				}
				if (!((Object)(object)mouseTypingField?.DisplayText == (Object)null))
				{
					TMP_Text displayText = mouseTypingField.DisplayText;
					if (mouseTypingField.Value.Length > 0)
					{
						displayText.text = mouseTypingField.Value;
						((Graphic)displayText).color = mouseTypingField.ActiveColor;
					}
					else
					{
						displayText.text = mouseTypingField.Placeholder;
						((Graphic)displayText).color = mouseTypingField.PlaceholderColor;
					}
					displayText.ForceMeshUpdate(false, false);
					RectTransform rectTransform = displayText.rectTransform;
					if ((Object)(object)rectTransform != (Object)null)
					{
						rectTransform.offsetMin = new Vector2(0f, rectTransform.offsetMin.y);
						rectTransform.offsetMax = new Vector2(0f, rectTransform.offsetMax.y);
					}
				}
			}

			private void RefreshMouseTypingDisplay()
			{
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0104: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00df: Unknown result type (might be due to invalid IL or missing references)
				InputFieldInfo mouseTypingField = _mouseTypingField;
				if (!((Object)(object)mouseTypingField?.DisplayText == (Object)null))
				{
					TMP_Text displayText = mouseTypingField.DisplayText;
					ApplyCaretText(mouseTypingField, _mouseCursorVisible, _mouseHasEdited);
					displayText.ForceMeshUpdate(false, false);
					float num = 0f;
					RectTransform rectTransform = displayText.rectTransform;
					if ((Object)(object)rectTransform != (Object)null && (Object)(object)mouseTypingField.Viewport != (Object)null)
					{
						Rect rect = mouseTypingField.Viewport.rect;
						float width = ((Rect)(ref rect)).width;
						float displayWidthForRenderedText = GetDisplayWidthForRenderedText(displayText, displayText.text);
						num = ((width > 1f) ? Mathf.Max(0f, displayWidthForRenderedText - width) : 0f);
						rectTransform.offsetMin = new Vector2(0f - num, rectTransform.offsetMin.y);
						rectTransform.offsetMax = new Vector2(0f - num, rectTransform.offsetMax.y);
					}
					if ((Object)(object)mouseTypingField.CursorImage != (Object)null)
					{
						((Graphic)mouseTypingField.CursorImage).color = Color.clear;
					}
				}
			}

			private static void ApplyCaretText(InputFieldInfo field, bool showCaret, bool hasEdited)
			{
				//IL_003d: 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)
				if (!((Object)(object)field?.DisplayText == (Object)null))
				{
					bool flag = !string.IsNullOrEmpty(field.Value);
					((Graphic)field.DisplayText).color = (flag ? field.ActiveColor : field.PlaceholderColor);
					if (!flag)
					{
						field.DisplayText.text = (showCaret ? "|" : "<color=#FFFFFF00>|</color>");
					}
					else if (!hasEdited)
					{
						field.DisplayText.text = (showCaret ? (field.Value + "|") : (field.Value + "<color=#FFFFFF00>|</color>"));
					}
					else
					{
						field.DisplayText.text = (showCaret ? (field.Value + "|") : (field.Value + "<color=#FFFFFF00>|</color>"));
					}
				}
			}

			private static float GetDisplayWidthForRenderedText(TMP_Text text, string renderedText)
			{
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)text == (Object)null)
				{
					return 0f;
				}
				if (string.IsNullOrEmpty(renderedText))
				{
					return 0f;
				}
				float x = text.GetPreferredValues(renderedText + "M").x;
				float x2 = text.GetPreferredValues("M").x;
				return Mathf.Max(0f, x - x2);
			}

			private static bool IsPointerOverGameObject(GameObject target)
			{
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Expected O, but got Unknown
				EventSystem current = EventSystem.current;
				if ((Object)(object)current == (Object)null || (Object)(object)target == (Object)null)
				{
					return false;
				}
				PointerEventData val = new PointerEventData(current)
				{
					position = Vector2.op_Implicit(Input.mousePosition)
				};
				List<RaycastResult> val2 = new List<RaycastResult>();
				current.RaycastAll(val, val2);
				for (int i = 0; i < val2.Count; i++)
				{
					GameObject gameObject = val2[i].gameObject;
					if (!((Object)(object)gameObject == (Object)null) && ((Object)(object)gameObject == (Object)(object)target || gameObject.transform.IsChildOf(target.transform)))
					{
						return true;
					}
				}
				return false;
			}

			private void SetKeyboardVisible(bool visible)
			{
				if ((Object)(object)_kbCG == (Object)null)
				{
					return;
				}
				_kbCG.alpha = (visible ? 1f : 0f);
				_kbCG.interactable = visible;
				_kbCG.blocksRaycasts = visible;
				if (visible && _kbCharRows != null && _kbCharRows.Length != 0)
				{
					Button val = _kbCharRows[0][0];
					if ((Object)(object)_submenuItemList != (Object)null)
					{
						_submenuItemList.items = Il2CppReferenceArray<GameObject>.op_Implicit((GameObject[])(object)new GameObject[1] { ((Component)val).gameObject });
					}
					EventSystem current = EventSystem.current;
					if (current != null)
					{
						current.SetSelectedGameObject(((Component)val).gameObject);
					}
					MenuItemList.active = _submenuItemList;
				}
			}

			private void IntegrateIntoMainItemList()
			{
				if ((Object)(object)_mainMenuItemList == (Object)null || (Object)(object)_mainButtonObj == (Object)null)
				{
					return;
				}
				Il2CppReferenceArray<GameObject> items = _mainMenuItemList.items;
				if (items == null || ((Il2CppArrayBase<GameObject>)(object)items).Length == 0)
				{
					return;
				}
				for (int i = 0; i < ((Il2CppArrayBase<GameObject>)(object)items).Length; i++)
				{
					if ((Object)(object)((Il2CppArrayBase<GameObject>)(object)items)[i] == (Object)(object)_mainButtonObj)
					{
						return;
					}
				}
				Menu activeMenu = _activeMenu;
				Button val = ((activeMenu != null) ? activeMenu.ContinueGameButton : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				int num = -1;
				for (int j = 0; j < ((Il2CppArrayBase<GameObject>)(object)items).Length; j++)
				{
					if ((Object)(object)((Il2CppArrayBase<GameObject>)(object)items)[j] == (Object)(object)((Component)val).gameObject)
					{
						num = j;
						break;
					}
				}
				if (num < 0)
				{
					return;
				}
				GameObject[] array = (GameObject[])(object)new GameObject[((Il2CppArrayBase<GameObject>)(object)items).Length + 1];
				int k = 0;
				int num2 = 0;
				for (; k < array.Length; k++)
				{
					if (k == num + 1)
					{
						array[k] = _mainButtonObj;
					}
					else
					{
						array[k] = ((Il2CppArrayBase<GameObject>)(object)items)[num2++];
					}
				}
				_mainMenuItemList.items = Il2CppReferenceArray<GameObject>.op_Implicit(array);
			}

			private void UpdateVisibility(Menu menu)
			{
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Invalid comparison between Unknown and I4
				if ((Object)(object)menu == (Object)null)
				{
					return;
				}
				bool flag = (Object)(object)menu.mainMenuCanvas != (Object)null && menu.mainMenuCanvas.activeInHierarchy && (int)menu.currentMenuScreen == 0;
				if ((Object)(object)_mainButtonObj != (Object)null)
				{
					_mainButtonObj.SetActive(flag);
				}
				if (!flag)
				{
					if (GetSubmenuVisible())
					{
						SetSubmenuVisible(isVisible: false);
					}
					return;
				}
				bool submenuVisible = GetSubmenuVisible();
				if ((Object)(object)_mainMenuCG != (Object)null)
				{
					_mainMenuCG.alpha = (submenuVisible ? 0f : 1f);
					_mainMenuCG.blocksRaycasts = !submenuVisible;
					_mainMenuCG.interactable = !submenuVisible;
				}
				if ((Object)(object)menu.titleGroup != (Object)null)
				{
					menu.titleGroup.alpha = (submenuVisible ? 0f : 1f);
				}
			}

			private void SetSubmenuVisible(bool isVisible)
			{
				if ((Object)(object)_submenuCG == (Object)null)
				{
					return;
				}
				bool flag = _submenuCG.alpha > 0.5f;
				if (flag == isVisible)
				{
					return;
				}
				if (!isVisible && KbVisible)
				{
					SetKeyboardVisible(visible: false);
				}
				_submenuCG.alpha = (isVisible ? 1f : 0f);
				_submenuCG.blocksRaycasts = isVisible;
				_submenuCG.interactable = isVisible;
				for (int i = 0; i < _fixedButtonInstances.Count; i++)
				{
					if ((Object)(object)_fixedButtonInstances[i] != (Object)null)
					{
						((Selectable)_fixedButtonInstances[i]).interactable = isVisible;
					}
				}
				if ((Object)(object)_mainButton != (Object)null)
				{
					((Selectable)_mainButton).interactable = !isVisible;
				}
				if (isVisible)
				{
					ApplyMargin();
					_tabMenu?.OnShown();
					if ((Object)(object)_submenuItemList != (Object)null)
					{
						MenuItemList.active = _submenuItemList;
					}
				}
				else if ((Object)(object)_mainMenuItemList != (Object)null)
				{
					MenuItemList.active = _mainMenuItemList;
				}
				EventSystem current = EventSystem.current;
				if ((Object)(object)current != (Object)null)
				{
					if (isVisible && _fixedButtonInstances.Count > 0 && (Object)(object)_fixedButtonInstances[0] != (Object)null)
					{
						current.SetSelectedGameObject(((Component)_fixedButtonInstances[0]).gameObject);
					}
					else if (!isVisible && (Object)(object)_mainButton != (Object)null)
					{
						current.SetSelectedGameObject(((Component)_mainButton).gameObject);
					}
				}
				UpdateVisibility(_activeMenu);
			}

			private void EnsureSelection()
			{
				if (KbVisible || _mouseTypingActive)
				{
					return;
				}
				EventSystem current = EventSystem.current;
				if ((Object)(object)current == (Object)null)
				{
					return;
				}
				GameObject currentSelectedGameObject = current.currentSelectedGameObject;
				if (!((Object)(object)currentSelectedGameObject == (Object)null) && currentSelectedGameObject.activeInHierarchy)
				{
					Selectable component = currentSelectedGameObject.GetComponent<Selectable>();
					if (component == null || component.interactable)
					{
						return;
					}
				}
				if (GetSubmenuVisible())
				{
					if (_fixedButtonInstances.Count > 0 && (Object)(object)_fixedButtonInstances[0] != (Object)null)
					{
						current.SetSelectedGameObject(((Component)_fixedButtonInstances[0]).gameObject);
					}
					return;
				}
				Menu activeMenu = _activeMenu;
				if ((Object)(object)((activeMenu != null) ? activeMenu.ContinueGameButton : null) != (Object)null)
				{
					current.SetSelectedGameObject(((Component)_activeMenu.ContinueGameButton).gameObject);
				}
			}

			private void TryHandleCancel(Menu menu)
			{
				if (((menu != null) ? menu.rwPlayer : null) != null && !KbVisible && !_mouseTypingActive && (Input.GetKeyDown((KeyCode)27) || menu.rwPlayer.GetButtonDown(15)))
				{
					SetSubmenuVisible(isVisible: false);
				}
			}

			private void ApplyMargin()
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_submenuRect != (Object)null)
				{
					_submenuRect.anchoredPosition = new Vector2(10f + _sideMargin, -10f - _topMargin);
				}
			}

			private bool GetSubmenuVisible()
			{
				return (Object)(object)_submenuCG != (Object)null && _submenuCG.alpha > 0.5f;
			}

			private void ResolveExisting(Menu menu)
			{
				if ((Object)(object)((menu != null) ? menu.mainMenuCanvas : null) == (Object)null)
				{
					return;
				}
				Il2CppArrayBase<Transform> componentsInChildren = menu.mainMenuCanvas.GetComponentsInChildren<Transform>(true);
				List<GameObject> list = new List<GameObject>();
				List<GameObject> list2 = new List<GameObject>();
				List<GameObject> list3 = new List<GameObject>();
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Transform val = componentsInChildren[i];
					if (!((Object)(object)val == (Object)null))
					{
						if (((Object)val).name == "BBSMP_MainMenuButton")
						{
							list.Add(((Component)val).gameObject);
						}
						else if (((Object)val).name == "BBSMP_SubmenuCanvas")
						{
							list2.Add(((Component)val).gameObject);
						}
						else if (((Object)val).name == "BBSMP_Keyboard")
						{
							list3.Add(((Component)val).gameObject);
						}
					}
				}
				if (list3.Count > 0)
				{
					if (_kbBuilt)
					{
						_kbRoot = list3[0];
						GameObject kbRoot = _kbRoot;
						_kbCG = ((kbRoot != null) ? kbRoot.GetComponent<CanvasGroup>() : null);
					}
					else
					{
						for (int j = 0; j < list3.Count; j++)
						{
							Object.Destroy((Object)(object)list3[j]);
						}
						_kbRoot = null;
						_kbCG = null;
					}
					for (int k = 1; k < list3.Count; k++)
					{
						Object.Destroy((Object)(object)list3[k]);
					}
				}
				if (list.Count > 0)
				{
					_mainButtonObj = list[0];
					_mainButton = _mainButtonObj.GetComponent<Button>();
					for (int l = 1; l < list.Count; l++)
					{
						Object.Destroy((Object)(object)list[l]);
					}
					_mainMenuRoot = _mainButtonObj.transform.parent;
					Transform mainMenuRoot = _mainMenuRoot;
					object obj = ((mainMenuRoot != null) ? ((Component)mainMenuRoot).GetComponent<CanvasGroup>() : null);
					if (obj == null)
					{
						Transform mainMenuRoot2 = _mainMenuRoot;
						obj = ((mainMenuRoot2 != null) ? ((Component)mainMenuRoot2).gameObject.AddComponent<CanvasGroup>() : null);
					}
					_mainMenuCG = (CanvasGroup)obj;
					Transform mainMenuRoot3 = _mainMenuRoot;
					_mainMenuItemList = ((mainMenuRoot3 != null) ? ((Component)mainMenuRoot3).GetComponent<MenuItemList>() : null);
				}
				if (list2.Count > 0)
				{
					_submenuObj = list2[0];
					_submenuCG = _submenuObj.GetComponent<CanvasGroup>();
					_submenuItemList = _submenuObj.GetComponent<MenuItemList>();
					for (int m = 1; m < list2.Count; m++)
					{
						Object.Destroy((Object)(object)list2[m]);
					}
					_fixedButtonInstances.Clear();
					for (int n = 0; n < _fixedDescs.Count; n++)
					{
						Transform val2 = _submenuObj.transform.Find($"BBSMP_Fixed_{n}");
						_fixedButtonInstances.Add((val2 != null) ? ((Component)val2).GetComponent<Button>() : null);
					}
				}
			}

			private void Reset()
			{
				if ((Object)(object)_mainButtonObj != (Object)null)
				{
					Object.Destroy((Object)(object)_mainButtonObj);
				}
				if ((Object)(object)_submenuObj != (Object)null)
				{
					Object.Destroy((Object)(object)_submenuObj);
				}
				if ((Object)(object)_kbRoot != (Object)null)
				{
					Object.Destroy((Object)(object)_kbRoot);
				}
				_mainButtonObj = null;
				_mainButton = null;
				_submenuObj = null;
				_submenuCG = null;
				_submenuItemList = null;
				_submenuRect = null;
				_mainMenuRoot = null;
				_mainMenuCG = null;
				_mainMenuItemList = null;
				_tabMenu = null;
				_contentBuilt = false;
				_fixedButtonInstances.Clear();
				_kbRoot = null;
				_kbCG = null;
				_kbCharRows = null;
				_kbSpecialRow = null;
				_kbLowerRows = null;
				_kbUpperRows = null;
				_kbAllButtons.Clear();
				_kbActiveField = null;
				_kbReturnButton = null;
				_kbBuilt = false;
				_kbShift = false;
				_kbShiftBtn = null;
				_kbNavFrame = -1;
				_kbCursorTimer = 0f;
				_kbCursorVisible = true;
			}

			private static Button FindTemplateButton(Menu menu)
			{
				if ((Object)(object)menu.ContinueGameButton != (Object)null)
				{
					return menu.ContinueGameButton;
				}
				if ((Object)(object)menu.mainMenuCanvas == (Object)null)
				{
					return null;
				}
				Il2CppArrayBase<Button> componentsInChildren = menu.mainMenuCanvas.GetComponentsInChildren<Button>(true);
				if (componentsInChildren == null || componentsInChildren.Length == 0)
				{
					return null;
				}
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if (!((Object)(object)componentsInChildren[i] == (Object)null))
					{
						TMP_Text componentInChildren = ((Component)componentsInChildren[i]).GetComponentInChildren<TMP_Text>(true);
						string text = ((componentInChildren != null) ? componentInChildren.text : null) ?? "";
						if (text.Contains("continue", StringComparison.OrdinalIgnoreCase))
						{
							return componentsInChildren[i];
						}
					}
				}
				return componentsInChildren[0];
			}

			private static Toggle FindToggleTemplate(Menu menu)
			{
				if ((Object)(object)((menu != null) ? menu.mainMenuCanvas : null) == (Object)null)
				{
					return null;
				}
				Il2CppArrayBase<Toggle> componentsInChildren = menu.mainMenuCanvas.GetComponentsInChildren<Toggle>(true);
				if (componentsInChildren == null || componentsInChildren.Length == 0)
				{
					return null;
				}
				Toggle val = componentsInChildren[0];
				if ((Object)(object)val != (Object)null)
				{
					StripLocalizationComponentsImmediate(((Component)val).gameObject);
				}
				return val;
			}

			private static Slider FindSliderTemplate(Menu menu)
			{
				if ((Object)(object)((menu != null) ? menu.mainMenuCanvas : null) == (Object)null)
				{
					return null;
				}
				Il2CppArrayBase<Slider> componentsInChildren = menu.mainMenuCanvas.GetComponentsInChildren<Slider>(true);
				return (componentsInChildren != null && componentsInChildren.Length > 0) ? componentsInChildren[0] : null;
			}

			private static void DumpHierarchy(Menu menu, string reason)
			{
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (!((Object)(object)menu == (Object)null))
					{
						StringBuilder stringBuilder = new StringBuilder();
						stringBuilder.AppendLine("=== BBSMP Menu Hierarchy Dump ===");
						StringBuilder stringBuilder2 = stringBuilder;
						StringBuilder stringBuilder3 = stringBuilder2;
						StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2);
						handler.AppendLiteral("Reason: ");
						handler.AppendFormatted(reason);
						stringBuilder3.AppendLine(ref handler);
						stringBuilder2 = stringBuilder;
						StringBuilder stringBuilder4 = stringBuilder2;
						handler = new StringBuilder.AppendInterpolatedStringHandler(6, 1, stringBuilder2);
						handler.AppendLiteral("Time: ");
						handler.AppendFormatted(DateTime.Now, "O");
						stringBuilder4.AppendLine(ref handler);
						stringBuilder2 = stringBuilder;
						StringBuilder stringBuilder5 = stringBuilder2;
						handler = new StringBuilder.AppendInterpolatedStringHandler(18, 2, stringBuilder2);
						handler.AppendLiteral("Screen: ");
						handler.AppendFormatted<MenuScreen>(menu.currentMenuScreen);
						handler.AppendLiteral("  Paused: ");
						handler.AppendFormatted(menu.paused);
						stringBuilder5.AppendLine(ref handler);
						if ((Object)(object)menu.mainMenuCanvas != (Object)null)
						{
							stringBuilder2 = stringBuilder;
							StringBuilder stringBuilder6 = stringBuilder2;
							handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
							handler.AppendLiteral("CanvasW: ");
							handler.AppendFormatted(GetPath(menu.mainMenuCanvas.transform));
							stringBuilder6.AppendLine(ref handler);
							AppendTransform(stringBuilder, menu.mainMenuCanvas.transform, 0, 6);
						}
						string text = Path.Combine(Environment.CurrentDirectory, "UserData");
						Directory.CreateDirectory(text);
						File.WriteAllText(Path.Combine(text, "BBSMP_MenuHierarchyDump.txt"), stringBuilder.ToString());
					}
				}
				catch (Exception value)
				{
					Instance logger = Core.logger;
					if (logger != null)
					{
						logger.Error($"[MenuInject] Dump failed: {value}");
					}
				}
			}

			private static void AppendTransform(StringBuilder sb, Transform root, int depth, int maxDepth)
			{
				if ((Object)(object)root == (Object)null || depth > maxDepth)
				{
					return;
				}
				string value = new string(' ', depth * 2);
				Il2CppArrayBase<Component> components = ((Component)root).GetComponents<Component>();
				List<string> list = new List<string>();
				for (int i = 0; i < components.Length; i++)
				{
					if ((Object)(object)components[i] != (Object)null)
					{
						list.Add(((object)components[i]).GetType().Name);
					}
				}
				StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(5, 3, sb);
				handler.AppendFormatted(value);
				handler.AppendLiteral("- ");
				handler.AppendFormatted(((Object)root).name);
				handler.AppendLiteral(" [");
				handler.AppendFormatted(string.Join(", ", list));
				handler.AppendLiteral("]");
				sb.AppendLine(ref handler);
				for (int j = 0; j < root.childCount; j++)
				{
					AppendTransform(sb, root.GetChild(j), depth + 1, maxDepth);
				}
			}

			private static string GetPath(Transform t)
			{
				if ((Object)(object)t == (Object)null)
				{
					return "<null>";
				}
				Stack<string> stack = new Stack<string>();
				while ((Object)(object)t != (Object)null)
				{
					stack.Push(((Object)t).name);
					t = t.parent;
				}
				return string.Join("/", stack);
			}
		}

		internal static readonly List<InjectedMenu> _registeredMenus = new List<InjectedMenu>();

		private static RectTransform customRoot;

		private static Button buttonTemplate;

		private static Toggle toggleTemplate;

		private static Slider sliderTemplate;

		private const float SliderHandleSize = 112f;

		private static Sprite _inputFieldSprite;

		private static Sprite _sliderHandleSprite;

		private static Sprite _solidSprite;

		private static TMP_FontAsset _lowercaseTextFont;

		private const float KeyboardKeyFontSize = 20f;

		private static RectTransform _keyboardOverlayRoot;

		private static CanvasGroup _keyboardOverlayCanvasGroup;

		private static RuntimeTabMenu _currentTabMenu;

		private static int _currentTabPage = -1;

		private static InjectedMenu _currentInjectedMenu;

		private static float nextY;

		private const float Spacing = 10f;

		private const float DefaultHeight = 52f;

		internal static readonly Color TabUnselected = new Color(0.435f, 0.676f, 0.726f, 1f);

		internal static readonly Color TabSelected = new Color(1f, 1f, 1f, 0.78f);

		internal static readonly Color TabText = new Color(0f, 0.169f, 0.283f, 1f);

		internal static readonly Color TabHighlighted = new Color(0.519f, 0.804f, 0.865f, 1f);

		internal static InjectedMenu CurrentMenu => _currentInjectedMenu;

		internal static InjectedMenu GetKeyboardMenu()
		{
			if (_registeredMenus.Count > 0)
			{
				return _registeredMenus[0];
			}
			return null;
		}

		private static RectTransform EnsureKeyboardOverlayRoot()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_keyboardOverlayRoot != (Object)null)
			{
				return _keyboardOverlayRoot;
			}
			Il2CppReferenceArray<Type> val = new Il2CppReferenceArray<Type>(5L);
			((Il2CppArrayBase<Type>)(object)val)[0] = Il2CppType.Of<RectTransform>();
			((Il2CppArrayBase<Type>)(object)val)[1] = Il2CppType.Of<Canvas>();
			((Il2CppArrayBase<Type>)(object)val)[2] = Il2CppType.Of<CanvasScaler>();
			((Il2CppArrayBase<Type>)(object)val)[3] = Il2CppType.Of<GraphicRaycaster>();
			((Il2CppArrayBase<Type>)(object)val)[4] = Il2CppType.Of<CanvasGroup>();
			GameObject val2 = new GameObject("BBSMP_ChatKeyboardOverlay", val);
			Object.DontDestroyOnLoad((Object)(object)val2);
			Canvas component = val2.GetComponent<Canvas>();
			component.renderMode = (RenderMode)0;
			component.overrideSorting = true;
			component.sortingOrder = 5000;
			_keyboardOverlayCanvasGroup = val2.GetComponent<CanvasGroup>();
			_keyboardOverlayCanvasGroup.alpha = 1f;
			_keyboardOverlayCanvasGroup.blocksRaycasts = true;
			_keyboardOverlayCanvasGroup.interactable = true;
			_keyboardOverlayRoot = val2.GetComponent<RectTransform>();
			_keyboardOverlayRoot.anchorMin = Vector2.zero;
			_keyboardOverlayRoot.anchorMax = Vector2.one;
			_keyboardOverlayRoot.offsetMin = Vector2.zero;
			_keyboardOverlayRoot.offsetMax = Vector2.zero;
			return _keyboardOverlayRoot;
		}

		internal static void HideKeyboardOverlay()
		{
			if ((Object)(object)_keyboardOverlayCanvasGroup != (Object)null)
			{
				_keyboardOverlayCanvasGroup.alpha = 0f;
				_keyboardOverlayCanvasGroup.blocksRaycasts = false;
				_keyboardOverlayCanvasGroup.interactable = false;
			}
		}

		internal static void ShowKeyboardOverlay()
		{
			RectTransform val = EnsureKeyboardOverlayRoot();
			if ((Object)(object)val != (Object)null)
			{
				CanvasGroup keyboardOverlayCanvasGroup = _keyboardOverlayCanvasGroup;
				if ((Object)(object)keyboardOverlayCanvasGroup != (Object)null)
				{
					keyboardOverlayCanvasGroup.alpha = 1f;
					keyboardOverlayCanvasGroup.blocksRaycasts = true;
					keyboardOverlayCanvasGroup.interactable = true;
				}
			}
		}

		private static TMP_FontAsset GetOrCreateLowercaseTextFont()
		{
			if ((Object)(object)_lowercaseTextFont != (Object)null)
			{
				return _lowercaseTextFont;
			}
			StyleManager.Fonts.Prepare();
			if ((Object)(object)StyleManager.Fonts.Arial == (Object)null)
			{
				return null;
			}
			_lowercaseTextFont = TMP_FontAsset.CreateFontAsset(StyleManager.Fonts.Arial);
			return _lowercaseTextFont;
		}

		private static void ApplyLowercaseTextFont(TMP_Text text)
		{
			if (!((Object)(object)text == (Object)null))
			{
				TMP_FontAsset orCreateLowercaseTextFont = GetOrCreateLowercaseTextFont();
				if ((Object)(object)orCreateLowercaseTextFont != (Object)null)
				{
					text.font = orCreateLowercaseTextFont;
				}
			}
		}

		private static Sprite GetOrCreateSliderHandleSprite()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_sliderHandleSprite != (Object)null)
			{
				return _sliderHandleSprite;
			}
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false);
			((Texture)val).filterMode = (FilterMode)1;
			Color32[] array = (Color32[])(object)new Color32[4096];
			float num = 31.5f;
			float num2 = 31.5f;
			float num3 = 26f;
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					float num4 = (float)j - num;
					float num5 = (float)i - num2;
					float num6 = Mathf.Sqrt(num4 * num4 + num5 * num5);
					byte b = (byte)(Mathf.Clamp01(num3 + 0.5f - num6) * 255f);
					array[i * 64 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b);
				}
			}
			val.SetPixels32(Il2CppStructArray<Color32>.op_Implicit(array));
			val.Apply(false, false);
			_sliderHandleSprite = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0);
			return _sliderHandleSprite;
		}

		private static Sprite GetOrCreateInputFieldSprite()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_inputFieldSprite != (Object)null)
			{
				return _inputFieldSprite;
			}
			Texture2D val = new Texture2D(128, 64, (TextureFormat)4, false);
			((Texture)val).filterMode = (FilterMode)1;
			float num = 18f;
			float num2 = 109f;
			float num3 = 18f;
			float num4 = 45f;
			Color32[] array = (Color32[])(object)new Color32[8192];
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					float num5 = (((float)j < num) ? (num - (float)j) : (((float)j > num2) ? ((float)j - num2) : 0f));
					float num6 = (((float)i < num3) ? (num3 - (float)i) : (((float)i > num4) ? ((float)i - num4) : 0f));
					float num7 = Mathf.Sqrt(num5 * num5 + num6 * num6);
					byte b = (byte)(Mathf.Clamp01(18.5f - num7) * 255f);
					array[i * 128 + j] = new Color32(byt

Mods/Concentus.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Concentus.Celt;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using Concentus.Native;
using Concentus.Silk;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using Concentus.Structs;
using Microsoft.CodeAnalysis;
using Microsoft.Win32.SafeHandles;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("ParityTest, PublicKey=002400000480000094000000060200000024000052534131000400000100010071a2f675c04c87e64b9be6d37f5833c5285fb4ed883780cf6d61e80aee5d77950b2f06dd45bc634f53405f2a2b7b2332f4dfdcb0554ffc97b935e7343e76e733eea44346e56ac1098c12a66de71e324f2f503f9f2e32560910e2082d6943df50db42679a330e52979bd1eefbb59485d2c7420d158f6ab6d41bdf42d2172675e1")]
[assembly: InternalsVisibleTo("TestOpusEncode, PublicKey=002400000480000094000000060200000024000052534131000400000100010071a2f675c04c87e64b9be6d37f5833c5285fb4ed883780cf6d61e80aee5d77950b2f06dd45bc634f53405f2a2b7b2332f4dfdcb0554ffc97b935e7343e76e733eea44346e56ac1098c12a66de71e324f2f503f9f2e32560910e2082d6943df50db42679a330e52979bd1eefbb59485d2c7420d158f6ab6d41bdf42d2172675e1")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Logan Stromberg")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("© Xiph.Org Foundation, Skype Limited, CSIRO, Microsoft Corp.")]
[assembly: AssemblyDescription("This package is a portable C# implementation of the Opus audio compression codec (see https://opus-codec.org/ for more details). This package contains the Opus encoder, decoder, multistream codecs, repacketizer, as well as a port of the libspeexdsp resampler. It does NOT contain code to parse .ogg or .opus container files or to manage RTP packet streams. For better performance depending on your platform, see also the Concentus.Native package.")]
[assembly: AssemblyFileVersion("2.2.2.0")]
[assembly: AssemblyInformationalVersion("2.2.2+6c2328dc19044601e33a9c11628b8d60e1f3011c")]
[assembly: AssemblyProduct("Concentus")]
[assembly: AssemblyTitle("Concentus")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/lostromb/concentus")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.2.2.0")]
[module: UnverifiableCode]
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
	{
	}
}
namespace Concentus
{
	public interface IOpusDecoder : IDisposable
	{
		OpusBandwidth Bandwidth { get; }

		uint FinalRange { get; }

		int Gain { get; set; }

		int LastPacketDuration { get; }

		int NumChannels { get; }

		int Pitch { get; }

		int SampleRate { get; }

		int Decode(ReadOnlySpan<byte> in_data, Span<float> out_pcm, int frame_size, bool decode_fec = false);

		int Decode(ReadOnlySpan<byte> in_data, Span<short> out_pcm, int frame_size, bool decode_fec = false);

		void ResetState();

		string GetVersionString();
	}
	public interface IOpusEncoder : IDisposable
	{
		OpusApplication Application { get; set; }

		int Bitrate { get; set; }

		int ForceChannels { get; set; }

		OpusBandwidth MaxBandwidth { get; set; }

		OpusBandwidth Bandwidth { get; set; }

		bool UseDTX { get; set; }

		int Complexity { get; set; }

		bool UseInbandFEC { get; set; }

		int PacketLossPercent { get; set; }

		bool UseVBR { get; set; }

		bool UseConstrainedVBR { get; set; }

		OpusSignal SignalType { get; set; }

		int Lookahead { get; }

		int SampleRate { get; }

		int NumChannels { get; }

		uint FinalRange { get; }

		int LSBDepth { get; set; }

		OpusFramesize ExpertFrameDuration { get; set; }

		OpusMode ForceMode { set; }

		bool PredictionDisabled { get; set; }

		int Encode(ReadOnlySpan<short> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes);

		int Encode(ReadOnlySpan<float> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes);

		void ResetState();

		string GetVersionString();
	}
	public interface IOpusMultiStreamDecoder : IDisposable
	{
		OpusBandwidth Bandwidth { get; }

		uint FinalRange { get; }

		int Gain { get; set; }

		int LastPacketDuration { get; }

		int SampleRate { get; }

		int NumChannels { get; }

		int DecodeMultistream(ReadOnlySpan<byte> data, Span<float> out_pcm, int frame_size, bool decode_fec);

		int DecodeMultistream(ReadOnlySpan<byte> data, Span<short> out_pcm, int frame_size, bool decode_fec);

		void ResetState();

		string GetVersionString();
	}
	public interface IOpusMultiStreamEncoder : IDisposable
	{
		OpusApplication Application { get; set; }

		OpusBandwidth Bandwidth { get; set; }

		int Bitrate { get; set; }

		int Complexity { get; set; }

		int NumChannels { get; }

		OpusFramesize ExpertFrameDuration { get; set; }

		uint FinalRange { get; }

		OpusMode ForceMode { set; }

		int Lookahead { get; }

		int LSBDepth { get; set; }

		OpusBandwidth MaxBandwidth { get; set; }

		int PacketLossPercent { get; set; }

		bool PredictionDisabled { get; set; }

		int SampleRate { get; }

		OpusSignal SignalType { get; set; }

		bool UseConstrainedVBR { get; set; }

		bool UseDTX { get; set; }

		bool UseInbandFEC { get; set; }

		bool UseVBR { get; set; }

		int EncodeMultistream(ReadOnlySpan<float> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes);

		int EncodeMultistream(ReadOnlySpan<short> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes);

		void ResetState();

		string GetVersionString();
	}
	public interface IResampler : IDisposable
	{
		int InputLatency { get; }

		int InputStride { get; set; }

		int OutputLatencySamples { get; }

		TimeSpan OutputLatency { get; }

		int OutputStride { get; set; }

		int Quality { get; set; }

		void GetRateFraction(out int ratio_num, out int ratio_den);

		void GetRates(out int in_rate, out int out_rate);

		void ResetMem();

		void SkipZeroes();

		void Process(int channel_index, Span<float> input, ref int in_len, Span<float> output, ref int out_len);

		void Process(int channel_index, Span<short> input, ref int in_len, Span<short> output, ref int out_len);

		void ProcessInterleaved(Span<float> input, ref int in_len, Span<float> output, ref int out_len);

		void ProcessInterleaved(Span<short> input, ref int in_len, Span<short> output, ref int out_len);

		void SetRateFraction(int ratio_num, int ratio_den, int in_rate, int out_rate);

		void SetRates(int in_rate, int out_rate);
	}
	public static class OpusCodecFactory
	{
		private static readonly object _mutex = new object();

		private static bool _nativeLibInitialized = false;

		private static bool _isNativeLibAvailable = false;

		private static bool _userAllowNativeLib = true;

		public static bool AttemptToUseNativeLibrary
		{
			get
			{
				return _userAllowNativeLib;
			}
			set
			{
				_userAllowNativeLib = value;
			}
		}

		public static IOpusEncoder CreateEncoder(int sampleRate, int numChannels, OpusApplication application = OpusApplication.OPUS_APPLICATION_AUDIO, TextWriter messageLogger = null)
		{
			if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger))
			{
				return NativeOpusEncoder.Create(sampleRate, numChannels, application);
			}
			return new OpusEncoder(sampleRate, numChannels, application);
		}

		public static IOpusDecoder CreateDecoder(int sampleRate, int numChannels, TextWriter messageLogger = null)
		{
			if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger))
			{
				return NativeOpusDecoder.Create(sampleRate, numChannels);
			}
			return new OpusDecoder(sampleRate, numChannels);
		}

		public static IOpusMultiStreamEncoder CreateMultiStreamEncoder(int sampleRate, int numChannels, int mappingFamily, out int streams, out int coupledStreams, byte[] mapping, OpusApplication application, TextWriter messageLogger = null)
		{
			if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger))
			{
				return NativeOpusMultistreamEncoder.Create(sampleRate, numChannels, mappingFamily, out streams, out coupledStreams, mapping, application);
			}
			return OpusMSEncoder.CreateSurround(sampleRate, numChannels, mappingFamily, out streams, out coupledStreams, mapping, application);
		}

		public static IOpusMultiStreamDecoder CreateMultiStreamDecoder(int sampleRate, int numChannels, int streams, int coupledStreams, byte[] mapping, TextWriter messageLogger = null)
		{
			if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger))
			{
				return NativeOpusMultistreamDecoder.Create(sampleRate, numChannels, streams, coupledStreams, mapping);
			}
			return new OpusMSDecoder(sampleRate, numChannels, streams, coupledStreams, mapping);
		}

		private static bool NativeLibraryAvailable(TextWriter messageLogger)
		{
			lock (_mutex)
			{
				if (!_nativeLibInitialized)
				{
					try
					{
						_isNativeLibAvailable = NativeOpus.Initialize(messageLogger);
						messageLogger?.WriteLine($"Is native opus available? {_isNativeLibAvailable}");
					}
					catch (Exception ex)
					{
						messageLogger?.WriteLine(ex.ToString());
					}
					_nativeLibInitialized = true;
				}
				return _isNativeLibAvailable;
			}
		}
	}
	internal static class Analysis
	{
		private const double M_PI = 3.141592653;

		private const float cA = 0.43157974f;

		private const float cB = 0.678484f;

		private const float cC = 0.08595542f;

		private const float cE = MathF.PI / 2f;

		private const int NB_TONAL_SKIP_BANDS = 9;

		internal static float fast_atan2f(float y, float x)
		{
			if (Inlines.ABS16(x) + Inlines.ABS16(y) < 1E-09f)
			{
				x *= 1E+12f;
				y *= 1E+12f;
			}
			float num = x * x;
			float num2 = y * y;
			if (num < num2)
			{
				float num3 = (num2 + 0.678484f * num) * (num2 + 0.08595542f * num);
				if (num3 != 0f)
				{
					return (0f - x) * y * (num2 + 0.43157974f * num) / num3 + ((y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f));
				}
				if (!(y < 0f))
				{
					return MathF.PI / 2f;
				}
				return -MathF.PI / 2f;
			}
			float num4 = (num + 0.678484f * num2) * (num + 0.08595542f * num2);
			if (num4 != 0f)
			{
				return x * y * (num + 0.43157974f * num2) / num4 + ((y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f)) - ((x * y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f));
			}
			return ((y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f)) - ((x * y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f));
		}

		internal static void tonality_analysis_init(TonalityAnalysisState tonal)
		{
			tonal.Reset();
		}

		internal static void tonality_get_info(TonalityAnalysisState tonal, AnalysisInfo info_out, int len)
		{
			int num = tonal.read_pos;
			int num2 = tonal.write_pos - tonal.read_pos;
			if (num2 < 0)
			{
				num2 += 200;
			}
			if (len > 480 && num != tonal.write_pos)
			{
				num++;
				if (num == 200)
				{
					num = 0;
				}
			}
			if (num == tonal.write_pos)
			{
				num--;
			}
			if (num < 0)
			{
				num = 199;
			}
			info_out.Assign(tonal.info[num]);
			tonal.read_subframe += len / 120;
			while (tonal.read_subframe >= 4)
			{
				tonal.read_subframe -= 4;
				tonal.read_pos++;
			}
			if (tonal.read_pos >= 200)
			{
				tonal.read_pos -= 200;
			}
			num2 = Inlines.IMAX(num2 - 10, 0);
			float num3 = 0f;
			int i;
			for (i = 0; i < 200 - num2; i++)
			{
				num3 += tonal.pmusic[i];
			}
			for (; i < 200; i++)
			{
				num3 += tonal.pspeech[i];
			}
			num3 = num3 * tonal.music_confidence + (1f - num3) * tonal.speech_confidence;
			info_out.music_prob = num3;
		}

		internal static void tonality_analysis<T>(TonalityAnalysisState tonal, CeltMode celt_mode, ReadOnlySpan<T> x, int len, int offset, int c1, int c2, int C, int lsb_depth, Downmix.downmix_func<T> downmix)
		{
			int num = 480;
			int num2 = 240;
			float[] angle = tonal.angle;
			float[] d_angle = tonal.d_angle;
			float[] d2_angle = tonal.d2_angle;
			float[] array = new float[18];
			float[] array2 = new float[18];
			float[] array3 = new float[8];
			float[] array4 = new float[25];
			float num3 = 97.40909f;
			float num4 = 0f;
			float[] array5 = new float[2];
			int num5 = 0;
			float num6 = 0f;
			tonal.last_transition++;
			float num7 = 1f / (float)Inlines.IMIN(20, 1 + tonal.count);
			float num8 = 1f / (float)Inlines.IMIN(50, 1 + tonal.count);
			float num9 = 1f / (float)Inlines.IMIN(1000, 1 + tonal.count);
			if (tonal.count < 4)
			{
				tonal.music_prob = 0.5f;
			}
			FFTState st = celt_mode.mdct.kfft[0];
			if (tonal.count == 0)
			{
				tonal.mem_fill = 240;
			}
			downmix(x, tonal.inmem, tonal.mem_fill, Inlines.IMIN(len, 720 - tonal.mem_fill), offset, c1, c2, C);
			if (tonal.mem_fill + len < 720)
			{
				tonal.mem_fill += len;
				return;
			}
			AnalysisInfo analysisInfo = tonal.info[tonal.write_pos++];
			if (tonal.write_pos >= 200)
			{
				tonal.write_pos -= 200;
			}
			int[] array6 = new int[960];
			int[] array7 = new int[960];
			float[] array8 = new float[240];
			float[] array9 = new float[240];
			for (int i = 0; i < num2; i++)
			{
				float num10 = Tables.analysis_window[i];
				array6[2 * i] = (int)(num10 * (float)tonal.inmem[i]);
				array6[2 * i + 1] = (int)(num10 * (float)tonal.inmem[num2 + i]);
				array6[2 * (num - i - 1)] = (int)(num10 * (float)tonal.inmem[num - i - 1]);
				array6[2 * (num - i - 1) + 1] = (int)(num10 * (float)tonal.inmem[num + num2 - i - 1]);
			}
			Arrays.MemMoveInt(tonal.inmem, 480, 0, 240);
			int num11 = len - (720 - tonal.mem_fill);
			downmix(x, tonal.inmem, 240, num11, offset + 720 - tonal.mem_fill, c1, c2, C);
			tonal.mem_fill = 240 + num11;
			KissFFT.opus_fft(st, array6, array7);
			for (int i = 1; i < num2; i++)
			{
				float x2 = (float)array7[2 * i] + (float)array7[2 * (num - i)];
				float y = (float)array7[2 * i + 1] - (float)array7[2 * (num - i) + 1];
				float x3 = (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1];
				float y2 = (float)array7[2 * (num - i)] - (float)array7[2 * i];
				float num12 = 1f / (2f * MathF.PI) * fast_atan2f(y, x2);
				float num13 = num12 - angle[i];
				float num14 = num13 - d_angle[i];
				float num15 = 1f / (2f * MathF.PI) * fast_atan2f(y2, x3);
				float num16 = num15 - num12;
				float num17 = num16 - num13;
				float num18 = num14 - (float)Math.Floor(0.5f + num14);
				array9[i] = Inlines.ABS16(num18);
				num18 *= num18;
				num18 *= num18;
				float num19 = num17 - (float)Math.Floor(0.5f + num17);
				array9[i] += Inlines.ABS16(num19);
				num19 *= num19;
				num19 *= num19;
				float num20 = 0.25f * (d2_angle[i] + 2f * num18 + num19);
				array8[i] = 1f / (1f + 640f * num3 * num20) - 0.015f;
				angle[i] = num15;
				d_angle[i] = num16;
				d2_angle[i] = num19;
			}
			float num21 = 0f;
			float num22 = 0f;
			analysisInfo.activity = 0f;
			float num23 = 0f;
			float num24 = 0f;
			if (tonal.count == 0)
			{
				for (int j = 0; j < 18; j++)
				{
					tonal.lowE[j] = 1E+10f;
					tonal.highE[j] = -1E+10f;
				}
			}
			float num25 = 0f;
			float num26 = 0f;
			for (int j = 0; j < 18; j++)
			{
				float num27 = 0f;
				float num28 = 0f;
				float num29 = 0f;
				for (int i = Tables.tbands[j]; i < Tables.tbands[j + 1]; i++)
				{
					float num30 = (float)array7[2 * i] * (float)array7[2 * i] + (float)array7[2 * (num - i)] * (float)array7[2 * (num - i)] + (float)array7[2 * i + 1] * (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1] * (float)array7[2 * (num - i) + 1];
					num30 *= 5.55E-17f;
					num27 += num30;
					num28 += num30 * array8[i];
					num29 += num30 * 2f * (0.5f - array9[i]);
				}
				tonal.E[tonal.E_count][j] = num27;
				num23 += num29 / (1E-15f + num27);
				num26 += (float)Math.Sqrt(num27 + 1E-10f);
				array2[j] = (float)Math.Log(num27 + 1E-10f);
				tonal.lowE[j] = Inlines.MIN32(array2[j], tonal.lowE[j] + 0.01f);
				tonal.highE[j] = Inlines.MAX32(array2[j], tonal.highE[j] - 0.1f);
				if (tonal.highE[j] < tonal.lowE[j] + 1f)
				{
					tonal.highE[j] += 0.5f;
					tonal.lowE[j] -= 0.5f;
				}
				num25 += (array2[j] - tonal.lowE[j]) / (1E-15f + tonal.highE[j] - tonal.lowE[j]);
				float num31;
				float num32 = (num31 = 0f);
				for (int i = 0; i < 8; i++)
				{
					num32 += (float)Math.Sqrt(tonal.E[i][j]);
					num31 += tonal.E[i][j];
				}
				float num33 = Inlines.MIN16(0.99f, num32 / (float)Math.Sqrt(1E-15 + (double)(8f * num31)));
				num33 *= num33;
				num33 *= num33;
				num24 += num33;
				array[j] = Inlines.MAX16(num28 / (1E-15f + num27), num33 * tonal.prev_band_tonality[j]);
				num21 += array[j];
				if (j >= 9)
				{
					num21 -= array[j - 18 + 9];
				}
				num22 = Inlines.MAX16(num22, (1f + 0.03f * (float)(j - 18)) * num21);
				num4 += array[j] * (float)(j - 8);
				tonal.prev_band_tonality[j] = array[j];
			}
			float num34 = 0f;
			num5 = 0;
			num6 = 0f;
			float num35 = 0.00057f / (float)(1 << Inlines.IMAX(0, lsb_depth - 8));
			num35 *= 134217730f;
			num35 *= num35;
			for (int j = 0; j < 21; j++)
			{
				float num36 = 0f;
				int num37 = Tables.extra_bands[j];
				int num38 = Tables.extra_bands[j + 1];
				for (int i = num37; i < num38; i++)
				{
					float num39 = (float)array7[2 * i] * (float)array7[2 * i] + (float)array7[2 * (num - i)] * (float)array7[2 * (num - i)] + (float)array7[2 * i + 1] * (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1] * (float)array7[2 * (num - i) + 1];
					num36 += num39;
				}
				num6 = Inlines.MAX32(num6, num36);
				tonal.meanE[j] = Inlines.MAX32((1f - num9) * tonal.meanE[j], num36);
				num36 = Inlines.MAX32(num36, tonal.meanE[j]);
				num34 = Inlines.MAX32(0.05f * num34, num36);
				if ((double)num36 > 0.1 * (double)num34 && num36 * 1E+09f > num6 && num36 > num35 * (float)(num38 - num37))
				{
					num5 = j;
				}
			}
			if (tonal.count <= 2)
			{
				num5 = 20;
			}
			num26 = 20f * (float)Math.Log10(num26);
			tonal.Etracker = Inlines.MAX32(tonal.Etracker - 0.03f, num26);
			tonal.lowECount *= 1f - num8;
			if (num26 < tonal.Etracker - 30f)
			{
				tonal.lowECount += num8;
			}
			for (int i = 0; i < 8; i++)
			{
				float num40 = 0f;
				for (int j = 0; j < 16; j++)
				{
					num40 += Tables.dct_table[i * 16 + j] * array2[j];
				}
				array3[i] = num40;
			}
			num24 /= 18f;
			num25 /= 18f;
			if (tonal.count < 10)
			{
				num25 = 0.5f;
			}
			num23 /= 18f;
			analysisInfo.activity = num23 + (1f - num23) * num25;
			num21 = num22 / 9f;
			num21 = (tonal.prev_tonality = Inlines.MAX16(num21, tonal.prev_tonality * 0.8f));
			num4 /= 64f;
			analysisInfo.tonality_slope = num4;
			tonal.E_count = (tonal.E_count + 1) % 8;
			tonal.count++;
			analysisInfo.tonality = num21;
			for (int i = 0; i < 4; i++)
			{
				array4[i] = -0.12299f * (array3[i] + tonal.mem[i + 24]) + 0.49195f * (tonal.mem[i] + tonal.mem[i + 16]) + 0.69693f * tonal.mem[i + 8] - 1.4349f * tonal.cmean[i];
			}
			for (int i = 0; i < 4; i++)
			{
				tonal.cmean[i] = (1f - num7) * tonal.cmean[i] + num7 * array3[i];
			}
			for (int i = 0; i < 4; i++)
			{
				array4[4 + i] = 0.63246f * (array3[i] - tonal.mem[i + 24]) + 0.31623f * (tonal.mem[i] - tonal.mem[i + 16]);
			}
			for (int i = 0; i < 3; i++)
			{
				array4[8 + i] = 0.53452f * (array3[i] + tonal.mem[i + 24]) - 0.26726f * (tonal.mem[i] + tonal.mem[i + 16]) - 0.53452f * tonal.mem[i + 8];
			}
			if (tonal.count > 5)
			{
				for (int i = 0; i < 9; i++)
				{
					tonal.std[i] = (1f - num7) * tonal.std[i] + num7 * array4[i] * array4[i];
				}
			}
			for (int i = 0; i < 8; i++)
			{
				tonal.mem[i + 24] = tonal.mem[i + 16];
				tonal.mem[i + 16] = tonal.mem[i + 8];
				tonal.mem[i + 8] = tonal.mem[i];
				tonal.mem[i] = array3[i];
			}
			for (int i = 0; i < 9; i++)
			{
				array4[11 + i] = (float)Math.Sqrt(tonal.std[i]);
			}
			array4[20] = analysisInfo.tonality;
			array4[21] = analysisInfo.activity;
			array4[22] = num24;
			array4[23] = analysisInfo.tonality_slope;
			array4[24] = tonal.lowECount;
			MultiLayerPerceptron.mlp_process(Tables.net, array4, array5);
			array5[0] = 0.5f * (array5[0] + 1f);
			array5[0] = 0.01f + 1.21f * array5[0] * array5[0] - 0.23f * (float)Math.Pow(array5[0], 10.0);
			array5[1] = 0.5f * array5[1] + 0.5f;
			array5[0] = array5[1] * array5[0] + (1f - array5[1]) * 0.5f;
			float num41 = 5E-05f * array5[1];
			float num42 = 0.05f;
			float num43 = Inlines.MAX16(0.05f, Inlines.MIN16(0.95f, array5[0]));
			float num44 = Inlines.MAX16(0.05f, Inlines.MIN16(0.95f, tonal.music_prob));
			num42 = 0.01f + 0.05f * Inlines.ABS16(num43 - num44) / (num43 * (1f - num44) + num44 * (1f - num43));
			float num45 = (1f - tonal.music_prob) * (1f - num41) + tonal.music_prob * num41;
			float num46 = tonal.music_prob * (1f - num41) + (1f - tonal.music_prob) * num41;
			num45 *= (float)Math.Pow(1f - array5[0], num42);
			num46 *= (float)Math.Pow(array5[0], num42);
			tonal.music_prob = num46 / (num45 + num46);
			analysisInfo.music_prob = tonal.music_prob;
			float num47 = 1E-20f;
			float num48 = (float)Math.Pow(1f - array5[0], num42);
			float num49 = (float)Math.Pow(array5[0], num42);
			if (tonal.count == 1)
			{
				tonal.pspeech[0] = 0.5f;
				tonal.pmusic[0] = 0.5f;
			}
			float num50 = tonal.pspeech[0] + tonal.pspeech[1];
			float num51 = tonal.pmusic[0] + tonal.pmusic[1];
			tonal.pspeech[0] = num50 * (1f - num41) * num48;
			tonal.pmusic[0] = num51 * (1f - num41) * num49;
			for (int i = 1; i < 199; i++)
			{
				tonal.pspeech[i] = tonal.pspeech[i + 1] * num48;
				tonal.pmusic[i] = tonal.pmusic[i + 1] * num49;
			}
			tonal.pspeech[199] = num51 * num41 * num48;
			tonal.pmusic[199] = num50 * num41 * num49;
			for (int i = 0; i < 200; i++)
			{
				num47 += tonal.pspeech[i] + tonal.pmusic[i];
			}
			num47 = 1f / num47;
			for (int i = 0; i < 200; i++)
			{
				tonal.pspeech[i] *= num47;
				tonal.pmusic[i] *= num47;
			}
			num47 = tonal.pmusic[0];
			for (int i = 1; i < 200; i++)
			{
				num47 += tonal.pspeech[i];
			}
			if ((double)array5[1] > 0.75)
			{
				if ((double)tonal.music_prob > 0.9)
				{
					float num52 = 1f / (float)(++tonal.music_confidence_count);
					tonal.music_confidence_count = Inlines.IMIN(tonal.music_confidence_count, 500);
					tonal.music_confidence += num52 * Inlines.MAX16(-0.2f, array5[0] - tonal.music_confidence);
				}
				if ((double)tonal.music_prob < 0.1)
				{
					float num53 = 1f / (float)(++tonal.speech_confidence_count);
					tonal.speech_confidence_count = Inlines.IMIN(tonal.speech_confidence_count, 500);
					tonal.speech_confidence += num53 * Inlines.MIN16(0.2f, array5[0] - tonal.speech_confidence);
				}
			}
			else
			{
				if (tonal.music_confidence_count == 0)
				{
					tonal.music_confidence = 0.9f;
				}
				if (tonal.speech_confidence_count == 0)
				{
					tonal.speech_confidence = 0.1f;
				}
			}
			if (tonal.last_music != ((tonal.music_prob > 0.5f) ? 1 : 0))
			{
				tonal.last_transition = 0;
			}
			tonal.last_music = ((tonal.music_prob > 0.5f) ? 1 : 0);
			analysisInfo.bandwidth = num5;
			analysisInfo.noisiness = num23;
			analysisInfo.valid = 1;
		}

		internal static void run_analysis<T>(TonalityAnalysisState analysis, CeltMode celt_mode, ReadOnlySpan<T> analysis_pcm, int analysis_frame_size, int frame_size, int c1, int c2, int C, int Fs, int lsb_depth, Downmix.downmix_func<T> downmix, AnalysisInfo analysis_info)
		{
			if (!analysis_pcm.IsEmpty)
			{
				analysis_frame_size = Inlines.IMIN(195 * Fs / 100, analysis_frame_size);
				int num = analysis_frame_size - analysis.analysis_offset;
				int num2 = analysis.analysis_offset;
				do
				{
					tonality_analysis(analysis, celt_mode, analysis_pcm, Inlines.IMIN(480, num), num2, c1, c2, C, lsb_depth, downmix);
					num2 += 480;
					num -= 480;
				}
				while (num > 0);
				analysis.analysis_offset = analysis_frame_size;
				analysis.analysis_offset -= frame_size;
			}
			analysis_info.valid = 0;
			tonality_get_info(analysis, analysis_info, frame_size);
		}
	}
	internal static class CodecHelpers
	{
		private const int MAX_DYNAMIC_FRAMESIZE = 24;

		internal static byte gen_toc(OpusMode mode, int framerate, OpusBandwidth bandwidth, int channels)
		{
			int num = 0;
			while (framerate < 400)
			{
				framerate <<= 1;
				num++;
			}
			byte b;
			switch (mode)
			{
			case OpusMode.MODE_SILK_ONLY:
				b = (byte)((int)(bandwidth - 1101) << 5);
				b |= (byte)(num - 2 << 3);
				break;
			case OpusMode.MODE_CELT_ONLY:
			{
				int num2 = (int)(bandwidth - 1102);
				if (num2 < 0)
				{
					num2 = 0;
				}
				b = 128;
				b |= (byte)(num2 << 5);
				b |= (byte)(num << 3);
				break;
			}
			default:
				b = 96;
				b |= (byte)((int)(bandwidth - 1104) << 4);
				b |= (byte)(num - 2 << 3);
				break;
			}
			return (byte)(b | (byte)(((channels == 2) ? 1u : 0u) << 2));
		}

		internal static void hp_cutoff(ReadOnlySpan<short> input, int input_ptr, int cutoff_Hz, Span<short> output, int output_ptr, int[] hp_mem, int len, int channels, int Fs)
		{
			int[] array = new int[3];
			int[] array2 = new int[2];
			int num = Inlines.silk_DIV32_16(Inlines.silk_SMULBB(2471, cutoff_Hz), Fs / 1000);
			int num2 = (array[0] = 268435456 - Inlines.silk_MUL(471, num));
			array[1] = Inlines.silk_LSHIFT(-num2, 1);
			array[2] = num2;
			int num3 = Inlines.silk_RSHIFT(num2, 6);
			array2[0] = Inlines.silk_SMULWW(num3, Inlines.silk_SMULWW(num, num) - 8388608);
			array2[1] = Inlines.silk_SMULWW(num3, num3);
			Filters.silk_biquad_alt(input, input_ptr, array, array2, hp_mem, 0, output, output_ptr, len, channels);
			if (channels == 2)
			{
				Filters.silk_biquad_alt(input, input_ptr + 1, array, array2, hp_mem, 2, output, output_ptr + 1, len, channels);
			}
		}

		internal static void dc_reject(ReadOnlySpan<short> input, int input_ptr, int cutoff_Hz, Span<short> output, int output_ptr, int[] hp_mem, int len, int channels, int Fs)
		{
			int shift = Inlines.celt_ilog2(Fs / (cutoff_Hz * 3));
			for (int i = 0; i < channels; i++)
			{
				for (int j = 0; j < len; j++)
				{
					int num = Inlines.SHL32(Inlines.EXTEND32(input[channels * j + i + input_ptr]), 15);
					int num2 = num - hp_mem[2 * i];
					hp_mem[2 * i] += Inlines.PSHR32(num - hp_mem[2 * i], shift);
					int a = num2 - hp_mem[2 * i + 1];
					hp_mem[2 * i + 1] += Inlines.PSHR32(num2 - hp_mem[2 * i + 1], shift);
					output[channels * j + i + output_ptr] = Inlines.EXTRACT16(Inlines.SATURATE(Inlines.PSHR32(a, 15), 32767));
				}
			}
		}

		internal static void stereo_fade(short[] pcm_buf, int g1, int g2, int overlap48, int frame_size, int channels, int[] window, int Fs)
		{
			int num = 48000 / Fs;
			int num2 = overlap48 / num;
			g1 = 32767 - g1;
			g2 = 32767 - g2;
			int i;
			for (i = 0; i < num2; i++)
			{
				int num3 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]);
				int a = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num3, g2), 32767 - num3, g1), 15);
				int b = Inlines.EXTRACT16(Inlines.HALF32(pcm_buf[i * channels] - pcm_buf[i * channels + 1]));
				b = Inlines.MULT16_16_Q15(a, b);
				pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - b);
				pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + b);
			}
			for (; i < frame_size; i++)
			{
				int b2 = Inlines.EXTRACT16(Inlines.HALF32(pcm_buf[i * channels] - pcm_buf[i * channels + 1]));
				b2 = Inlines.MULT16_16_Q15(g2, b2);
				pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - b2);
				pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + b2);
			}
		}

		internal static void gain_fade(short[] buffer, int buf_ptr, int g1, int g2, int overlap48, int frame_size, int channels, int[] window, int Fs)
		{
			int num = 48000 / Fs;
			int num2 = overlap48 / num;
			if (channels == 1)
			{
				for (int i = 0; i < num2; i++)
				{
					int num3 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]);
					int a = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num3, g2), 32767 - num3, g1), 15);
					buffer[buf_ptr + i] = (short)Inlines.MULT16_16_Q15(a, buffer[buf_ptr + i]);
				}
			}
			else
			{
				for (int i = 0; i < num2; i++)
				{
					int num4 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]);
					int a2 = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num4, g2), 32767 - num4, g1), 15);
					buffer[buf_ptr + i * 2] = (short)Inlines.MULT16_16_Q15(a2, buffer[buf_ptr + i * 2]);
					buffer[buf_ptr + i * 2 + 1] = (short)Inlines.MULT16_16_Q15(a2, buffer[buf_ptr + i * 2 + 1]);
				}
			}
			int num5 = 0;
			do
			{
				for (int i = num2; i < frame_size; i++)
				{
					buffer[buf_ptr + i * channels + num5] = (short)Inlines.MULT16_16_Q15(g2, buffer[buf_ptr + i * channels + num5]);
				}
			}
			while (++num5 < channels);
		}

		internal static float transient_boost(Span<float> E, int E_ptr, float[] E_1, int LM, int maxM)
		{
			float num = 0f;
			float num2 = 0f;
			int num3 = Inlines.IMIN(maxM, (1 << LM) + 1);
			for (int i = E_ptr; i < num3 + E_ptr; i++)
			{
				num += E[i];
				num2 += E_1[i];
			}
			float num4 = num * num2 / (float)(num3 * num3);
			return Inlines.MIN16(1f, (float)Math.Sqrt(Inlines.MAX16(0f, 0.05f * (num4 - 2f))));
		}

		internal static int transient_viterbi(float[] E, float[] E_1, int N, int frame_cost, int rate)
		{
			float[][] array = Arrays.InitTwoDimensionalArray<float>(24, 16);
			int[][] array2 = Arrays.InitTwoDimensionalArray<int>(24, 16);
			float num = ((rate < 80) ? 0f : ((rate <= 160) ? (((float)rate - 80f) / 80f) : 1f));
			for (int i = 0; i < 16; i++)
			{
				array2[0][i] = -1;
				array[0][i] = 1E+10f;
			}
			for (int i = 0; i < 4; i++)
			{
				array[0][1 << i] = (float)(frame_cost + rate * (1 << i)) * (1f + num * transient_boost(E, 0, E_1, i, N + 1));
				array2[0][1 << i] = i;
			}
			for (int i = 1; i < N; i++)
			{
				for (int j = 2; j < 16; j++)
				{
					array[i][j] = array[i - 1][j - 1];
					array2[i][j] = j - 1;
				}
				for (int j = 0; j < 4; j++)
				{
					array2[i][1 << j] = 1;
					float num2 = array[i - 1][1];
					for (int k = 1; k < 4; k++)
					{
						float num3 = array[i - 1][(1 << k + 1) - 1];
						if (num3 < num2)
						{
							array2[i][1 << j] = (1 << k + 1) - 1;
							num2 = num3;
						}
					}
					float num4 = (float)(frame_cost + rate * (1 << j)) * (1f + num * transient_boost(E, i, E_1, j, N - i + 1));
					array[i][1 << j] = num2;
					if (N - i < 1 << j)
					{
						array[i][1 << j] += num4 * (float)(N - i) / (float)(1 << j);
					}
					else
					{
						array[i][1 << j] += num4;
					}
				}
			}
			int num5 = 1;
			float num6 = array[N - 1][1];
			for (int i = 2; i < 16; i++)
			{
				if (array[N - 1][i] < num6)
				{
					num6 = array[N - 1][i];
					num5 = i;
				}
			}
			for (int i = N - 1; i >= 0; i--)
			{
				num5 = array2[i][num5];
			}
			return num5;
		}

		internal static int optimize_framesize<T>(ReadOnlySpan<T> x, int len, int C, int Fs, int bitrate, int tonality, float[] mem, int buffering, Downmix.downmix_func<T> downmix)
		{
			float[] array = new float[28];
			float[] array2 = new float[27];
			int num = 0;
			int num2 = Fs / 400;
			int[] array3 = new int[num2];
			array[0] = mem[0];
			array2[0] = 1f / (1f + mem[0]);
			int num3;
			int num4;
			if (buffering != 0)
			{
				num3 = 2 * num2 - buffering;
				len -= num3;
				array[1] = mem[1];
				array2[1] = 1f / (1f + mem[1]);
				array[2] = mem[2];
				array2[2] = 1f / (1f + mem[2]);
				num4 = 3;
			}
			else
			{
				num4 = 1;
				num3 = 0;
			}
			int num5 = Inlines.IMIN(len / num2, 24);
			int num6 = 0;
			int i;
			for (i = 0; i < num5; i++)
			{
				float num7 = 1f;
				downmix(x, array3, 0, num2, i * num2 + num3, 0, -2, C);
				if (i == 0)
				{
					num6 = array3[0];
				}
				for (int j = 0; j < num2; j++)
				{
					int num8 = array3[j];
					num7 += (float)(num8 - num6) * (float)(num8 - num6);
					num6 = num8;
				}
				array[i + num4] = num7;
				array2[i + num4] = 1f / num7;
			}
			array[i + num4] = array[i + num4 - 1];
			if (buffering != 0)
			{
				num5 = Inlines.IMIN(24, num5 + 2);
			}
			num = transient_viterbi(array, array2, num5, (int)((1f + 0.5f * (float)tonality) * (float)(60 * C + 40)), bitrate / 400);
			mem[0] = array[1 << num];
			if (buffering != 0)
			{
				mem[1] = array[(1 << num) + 1];
				mem[2] = array[(1 << num) + 2];
			}
			return num;
		}

		internal static int frame_size_select(int frame_size, OpusFramesize variable_duration, int Fs)
		{
			if (frame_size < Fs / 400)
			{
				return -1;
			}
			int num;
			switch (variable_duration)
			{
			case OpusFramesize.OPUS_FRAMESIZE_ARG:
				num = frame_size;
				break;
			case OpusFramesize.OPUS_FRAMESIZE_VARIABLE:
				num = Fs / 50;
				break;
			case OpusFramesize.OPUS_FRAMESIZE_2_5_MS:
			case OpusFramesize.OPUS_FRAMESIZE_5_MS:
			case OpusFramesize.OPUS_FRAMESIZE_10_MS:
			case OpusFramesize.OPUS_FRAMESIZE_20_MS:
			case OpusFramesize.OPUS_FRAMESIZE_40_MS:
			case OpusFramesize.OPUS_FRAMESIZE_60_MS:
				num = Inlines.IMIN(3 * Fs / 50, Fs / 400 << (int)(variable_duration - 5001));
				break;
			default:
				return -1;
			}
			if (num > frame_size)
			{
				return -1;
			}
			if (400 * num != Fs && 200 * num != Fs && 100 * num != Fs && 50 * num != Fs && 25 * num != Fs && 50 * num != 3 * Fs)
			{
				return -1;
			}
			return num;
		}

		internal static int compute_frame_size<T>(ReadOnlySpan<T> analysis_pcm, int frame_size, OpusFramesize variable_duration, int C, int Fs, int bitrate_bps, int delay_compensation, Downmix.downmix_func<T> downmix, float[] subframe_mem, bool analysis_enabled)
		{
			if (analysis_enabled && variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size >= Fs / 200)
			{
				int num = 3;
				num = optimize_framesize(analysis_pcm, frame_size, C, Fs, bitrate_bps, 0, subframe_mem, delay_compensation, downmix);
				while (Fs / 400 << num > frame_size)
				{
					num--;
				}
				frame_size = Fs / 400 << num;
			}
			else
			{
				frame_size = frame_size_select(frame_size, variable_duration, Fs);
			}
			if (frame_size < 0)
			{
				return -1;
			}
			return frame_size;
		}

		internal static int compute_stereo_width(ReadOnlySpan<short> pcm, int pcm_ptr, int frame_size, int Fs, StereoWidthState mem)
		{
			int num = Fs / frame_size;
			int a = 32767 - 819175 / Inlines.IMAX(50, num);
			int num3;
			int num2;
			int num4 = (num3 = (num2 = 0));
			for (int i = 0; i < frame_size - 3; i += 4)
			{
				int num5 = 0;
				int num6 = 0;
				int num7 = 0;
				int num8 = pcm_ptr + 2 * i;
				int num9 = pcm[num8];
				int num10 = pcm[num8 + 1];
				num5 = Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2);
				num6 = Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2);
				num7 = Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2);
				num9 = pcm[num8 + 2];
				num10 = pcm[num8 + 3];
				num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2);
				num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2);
				num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2);
				num9 = pcm[num8 + 4];
				num10 = pcm[num8 + 5];
				num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2);
				num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2);
				num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2);
				num9 = pcm[num8 + 6];
				num10 = pcm[num8 + 7];
				num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2);
				num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2);
				num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2);
				num4 += Inlines.SHR32(num5, 10);
				num3 += Inlines.SHR32(num6, 10);
				num2 += Inlines.SHR32(num7, 10);
			}
			mem.XX += Inlines.MULT16_32_Q15(a, num4 - mem.XX);
			mem.XY += Inlines.MULT16_32_Q15(a, num3 - mem.XY);
			mem.YY += Inlines.MULT16_32_Q15(a, num2 - mem.YY);
			mem.XX = Inlines.MAX32(0, mem.XX);
			mem.XY = Inlines.MAX32(0, mem.XY);
			mem.YY = Inlines.MAX32(0, mem.YY);
			if (Inlines.MAX32(mem.XX, mem.YY) > 210)
			{
				int num11 = Inlines.celt_sqrt(mem.XX);
				int num12 = Inlines.celt_sqrt(mem.YY);
				int num13 = Inlines.celt_sqrt(num11);
				int num14 = Inlines.celt_sqrt(num12);
				mem.XY = Inlines.MIN32(mem.XY, num11 * num12);
				int num15 = Inlines.SHR32(Inlines.frac_div32(mem.XY, 1 + Inlines.MULT16_16(num11, num12)), 16);
				int b = 32767 * Inlines.ABS16(num13 - num14) / (1 + num13 + num14);
				int num16 = Inlines.MULT16_16_Q15(Inlines.celt_sqrt(1073741824 - Inlines.MULT16_16(num15, num15)), b);
				mem.smoothed_width += (num16 - mem.smoothed_width) / num;
				mem.max_follower = Inlines.MAX16(mem.max_follower - 655 / num, mem.smoothed_width);
			}
			else
			{
				int num16 = 0;
				int num15 = 32767;
				int b = 0;
			}
			return Inlines.EXTRACT16(Inlines.MIN32(32767, 20 * mem.max_follower));
		}

		internal static void smooth_fade(Span<short> in1, int in1_ptr, Span<short> in2, int in2_ptr, Span<short> output, int output_ptr, int overlap, int channels, int[] window, int Fs)
		{
			int num = 48000 / Fs;
			for (int i = 0; i < channels; i++)
			{
				for (int j = 0; j < overlap; j++)
				{
					int num2 = Inlines.MULT16_16_Q15(window[j * num], window[j * num]);
					output[output_ptr + j * channels + i] = (short)Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num2, in2[in2_ptr + j * channels + i]), 32767 - num2, in1[in1_ptr + j * channels + i]), 15);
				}
			}
		}

		internal static string opus_strerror(int error)
		{
			string[] array = new string[8] { "success", "invalid argument", "buffer too small", "internal error", "corrupted stream", "request not implemented", "invalid state", "memory allocation failed" };
			if (error > 0 || error < -7)
			{
				return "unknown error";
			}
			return array[-error];
		}

		internal static string GetVersionString()
		{
			return "Concentus 2.1.2";
		}
	}
	internal static class Downmix
	{
		internal delegate void downmix_func<T>(ReadOnlySpan<T> _x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C);

		internal static void downmix_float(ReadOnlySpan<float> x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C)
		{
			for (int i = 0; i < subframe; i++)
			{
				sub[sub_ptr + i] = Inlines.FLOAT2INT16(x[(i + offset) * C + c1]);
			}
			if (c2 > -1)
			{
				for (int i = 0; i < subframe; i++)
				{
					sub[sub_ptr + i] += Inlines.FLOAT2INT16(x[(i + offset) * C + c2]);
				}
			}
			else if (c2 == -2)
			{
				for (int j = 1; j < C; j++)
				{
					int num = j;
					for (int i = 0; i < subframe; i++)
					{
						sub[sub_ptr + i] += Inlines.FLOAT2INT16(x[(i + offset) * C + num]);
					}
				}
			}
			int num2 = 4096;
			num2 = ((C != -2) ? (num2 / 2) : (num2 / C));
			for (int i = 0; i < subframe; i++)
			{
				sub[sub_ptr + i] *= num2;
			}
		}

		internal static void downmix_int(ReadOnlySpan<short> x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C)
		{
			for (int i = 0; i < subframe; i++)
			{
				sub[i + sub_ptr] = x[(i + offset) * C + c1];
			}
			if (c2 > -1)
			{
				for (int i = 0; i < subframe; i++)
				{
					sub[i + sub_ptr] += x[(i + offset) * C + c2];
				}
			}
			else if (c2 == -2)
			{
				for (int j = 1; j < C; j++)
				{
					for (int i = 0; i < subframe; i++)
					{
						sub[i + sub_ptr] += x[(i + offset) * C + j];
					}
				}
			}
			int num = 4096;
			num = ((C != -2) ? (num / 2) : (num / C));
			for (int i = 0; i < subframe; i++)
			{
				sub[i + sub_ptr] *= num;
			}
		}
	}
	internal static class MultiLayerPerceptron
	{
		private const int MAX_NEURONS = 100;

		internal static float tansig_approx(float x)
		{
			float num = 1f;
			if (!(x < 8f))
			{
				return 1f;
			}
			if (!(x > -8f))
			{
				return -1f;
			}
			if (x < 0f)
			{
				x = 0f - x;
				num = -1f;
			}
			int num2 = (int)Math.Floor(0.5f + 25f * x);
			x -= 0.04f * (float)num2;
			float num3 = Tables.tansig_table[num2];
			float num4 = 1f - num3 * num3;
			num3 += x * num4 * (1f - num3 * x);
			return num * num3;
		}

		internal static void mlp_process(MLP m, float[] input, float[] output)
		{
			float[] array = new float[100];
			float[] weights = m.weights;
			int num = 0;
			for (int i = 0; i < m.topo[1]; i++)
			{
				float num2 = weights[num];
				num++;
				for (int j = 0; j < m.topo[0]; j++)
				{
					num2 += input[j] * weights[num];
					num++;
				}
				array[i] = tansig_approx(num2);
			}
			for (int i = 0; i < m.topo[2]; i++)
			{
				float num3 = weights[num];
				num++;
				for (int k = 0; k < m.topo[1]; k++)
				{
					num3 += array[k] * weights[num];
					num++;
				}
				output[i] = tansig_approx(num3);
			}
		}
	}
	internal static class OpusCompare
	{
		private const int NBANDS = 21;

		private const int NFREQS = 240;

		private const int TEST_WIN_SIZE = 480;

		private const int TEST_WIN_STEP = 120;

		private static readonly int[] BANDS = new int[22]
		{
			0, 2, 4, 6, 8, 10, 12, 14, 16, 20,
			24, 28, 32, 40, 48, 56, 68, 80, 96, 120,
			156, 200
		};

		private static void band_energy(Pointer<float> _out, Pointer<float> _ps, Pointer<int> _bands, int _nbands, Pointer<float> _in, int _nchannels, int _nframes, int _window_sz, int _step, int _downsample)
		{
			Pointer<float> pointer = Concentus.Common.CPlusPlus.Pointer.Malloc<float>((3 + _nchannels) * _window_sz);
			Pointer<float> pointer2 = pointer.Point(_window_sz);
			Pointer<float> pointer3 = pointer2.Point(_window_sz);
			Pointer<float> pointer4 = pointer3.Point(_window_sz);
			int num = _window_sz / 2;
			for (int i = 0; i < _window_sz; i++)
			{
				pointer[i] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * 2.0 / (double)(_window_sz - 1) * (double)i));
			}
			for (int i = 0; i < _window_sz; i++)
			{
				pointer2[i] = (float)Math.Cos(Math.PI * 2.0 / (double)_window_sz * (double)i);
			}
			for (int i = 0; i < _window_sz; i++)
			{
				pointer3[i] = (float)Math.Sin(Math.PI * 2.0 / (double)_window_sz * (double)i);
			}
			for (int j = 0; j < _nframes; j++)
			{
				for (int k = 0; k < _nchannels; k++)
				{
					for (int l = 0; l < _window_sz; l++)
					{
						pointer4[k * _window_sz + l] = pointer[l] * _in[(j * _step + l) * _nchannels + k];
					}
				}
				int i;
				for (int m = (i = 0); m < _nbands; m++)
				{
					float[] array = new float[2];
					for (; i < _bands[m + 1]; i++)
					{
						for (int k = 0; k < _nchannels; k++)
						{
							int num2 = 0;
							float num3;
							float num4 = (num3 = 0f);
							for (int l = 0; l < _window_sz; l++)
							{
								num4 += pointer2[num2] * pointer4[k * _window_sz + l];
								num3 -= pointer3[num2] * pointer4[k * _window_sz + l];
								num2 += i;
								if (num2 >= _window_sz)
								{
									num2 -= _window_sz;
								}
							}
							num4 *= (float)_downsample;
							num3 *= (float)_downsample;
							_ps[(j * num + i) * _nchannels + k] = num4 * num4 + num3 * num3 + 100000f;
							array[k] += _ps[(j * num + i) * _nchannels + k];
						}
					}
					if (_out != null)
					{
						_out[(j * _nbands + m) * _nchannels] = array[0] / (float)(_bands[m + 1] - _bands[m]);
						if (_nchannels == 2)
						{
							_out[(j * _nbands + m) * _nchannels + 1] = array[1] / (float)(_bands[m + 1] - _bands[m]);
						}
					}
				}
			}
		}

		internal static float compare(float[] x, float[] y, int nchannels, int rate = 48000)
		{
			int num = x.Length;
			int num2 = y.Length;
			int num3 = 21;
			int num4 = 240;
			if (rate != 8000 && rate != 12000 && rate != 16000 && rate != 24000 && rate != 48000)
			{
				throw new ArgumentException("Sampling rate must be 8000, 12000, 16000, 24000, or 48000\n");
			}
			int num5;
			if (rate != 48000)
			{
				num5 = 48000 / rate;
				switch (rate)
				{
				case 8000:
					num3 = 13;
					break;
				case 12000:
					num3 = 15;
					break;
				case 16000:
					num3 = 17;
					break;
				case 24000:
					num3 = 19;
					break;
				}
				num4 = 240 / num5;
			}
			else
			{
				num5 = 1;
			}
			if (num != num2 * num5)
			{
				throw new ArgumentException("Sample counts do not match");
			}
			if (num < 480)
			{
				throw new ArgumentException("Insufficient sample data");
			}
			int num6 = (num - 480 + 120) / 120;
			Pointer<float> pointer = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * 21 * nchannels);
			Pointer<float> pointer2 = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * 240 * nchannels);
			Pointer<float> pointer3 = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * num4 * nchannels);
			band_energy(pointer, pointer2, BANDS.GetPointer(), 21, x.GetPointer(), nchannels, num6, 480, 120, 1);
			band_energy(null, pointer3, BANDS.GetPointer(), num3, y.GetPointer(), nchannels, num6, 480 / num5, 120 / num5, num5);
			for (int i = 0; i < num6; i++)
			{
				int j;
				for (j = 1; j < 21; j++)
				{
					for (int k = 0; k < nchannels; k++)
					{
						pointer[(i * 21 + j) * nchannels + k] += 0.1f * pointer[(i * 21 + j - 1) * nchannels + k];
					}
				}
				j = 20;
				while (j-- > 0)
				{
					for (int k = 0; k < nchannels; k++)
					{
						pointer[(i * 21 + j) * nchannels + k] += 0.03f * pointer[(i * 21 + j + 1) * nchannels + k];
					}
				}
				if (i > 0)
				{
					for (j = 0; j < 21; j++)
					{
						for (int k = 0; k < nchannels; k++)
						{
							pointer[(i * 21 + j) * nchannels + k] += 0.5f * pointer[((i - 1) * 21 + j) * nchannels + k];
						}
					}
				}
				if (nchannels == 2)
				{
					for (j = 0; j < 21; j++)
					{
						float num7 = pointer[(i * 21 + j) * nchannels];
						float num8 = pointer[(i * 21 + j) * nchannels + 1];
						pointer[(i * 21 + j) * nchannels] += 0.01f * num8;
						pointer[(i * 21 + j) * nchannels + 1] += 0.01f * num7;
					}
				}
				for (j = 0; j < num3; j++)
				{
					for (int l = BANDS[j]; l < BANDS[j + 1]; l++)
					{
						for (int k = 0; k < nchannels; k++)
						{
							pointer2[(i * 240 + l) * nchannels + k] += 0.1f * pointer[(i * 21 + j) * nchannels + k];
							pointer3[(i * num4 + l) * nchannels + k] += 0.1f * pointer[(i * 21 + j) * nchannels + k];
						}
					}
				}
			}
			for (int j = 0; j < num3; j++)
			{
				for (int l = BANDS[j]; l < BANDS[j + 1]; l++)
				{
					for (int k = 0; k < nchannels; k++)
					{
						float num9 = pointer2[l * nchannels + k];
						float num10 = pointer3[l * nchannels + k];
						for (int i = 1; i < num6; i++)
						{
							float num11 = pointer2[(i * 240 + l) * nchannels + k];
							float num12 = pointer3[(i * num4 + l) * nchannels + k];
							pointer2[(i * 240 + l) * nchannels + k] += num9;
							pointer3[(i * num4 + l) * nchannels + k] += num10;
							num9 = num11;
							num10 = num12;
						}
					}
				}
			}
			int num13 = rate switch
			{
				48000 => BANDS[21], 
				12000 => BANDS[num3], 
				_ => BANDS[num3] - 3, 
			};
			double num14 = 0.0;
			for (int i = 0; i < num6; i++)
			{
				double num15 = 0.0;
				for (int j = 0; j < num3; j++)
				{
					double num16 = 0.0;
					for (int l = BANDS[j]; l < BANDS[j + 1] && l < num13; l++)
					{
						for (int k = 0; k < nchannels; k++)
						{
							float num17 = pointer3[(i * num4 + l) * nchannels + k] / pointer2[(i * 240 + l) * nchannels + k];
							float num18 = num17 - (float)Math.Log(num17) - 1f;
							if (l >= 79 && l <= 81)
							{
								num18 *= 0.1f;
							}
							if (l == 80)
							{
								num18 *= 0.1f;
							}
							num16 += (double)num18;
						}
					}
					num16 /= (double)((BANDS[j + 1] - BANDS[j]) * nchannels);
					num15 += num16 * num16;
				}
				num15 /= 21.0;
				num15 *= num15;
				num14 += num15 * num15;
			}
			num14 = Math.Pow(num14 / (double)num6, 0.0625);
			float result = (float)(100.0 * (1.0 - 0.5 * Math.Log(1.0 + num14) / Math.Log(1.13)));
			_ = 0f;
			return result;
		}
	}
	internal static class OpusConstants
	{
		internal const int OPUS_AUTO = -1000;

		internal const int OPUS_BITRATE_MAX = -1;

		internal const int NB_FRAMES = 8;

		internal const int NB_TBANDS = 18;

		internal const int NB_TOT_BANDS = 21;

		internal const int NB_TONAL_SKIP_BANDS = 9;

		internal const int ANALYSIS_BUF_SIZE = 720;

		internal const int DETECT_SIZE = 200;

		internal const int MAX_ENCODER_BUFFER = 480;
	}
	public class OpusException : Exception
	{
		public int OpusErrorCode { get; private set; }

		internal OpusException()
			: base("Unknown error")
		{
			OpusErrorCode = -100;
		}

		internal OpusException(string message)
			: base(message)
		{
			OpusErrorCode = -100;
		}

		internal OpusException(int opusError)
			: base(CodecHelpers.opus_strerror(opusError))
		{
			OpusErrorCode = opusError;
		}

		internal OpusException(string message, int opusError)
			: base(message)
		{
			OpusErrorCode = opusError;
		}
	}
	internal static class OpusMultistream
	{
		internal static int validate_layout(ChannelLayout layout)
		{
			int num = layout.nb_streams + layout.nb_coupled_streams;
			if (num > 255)
			{
				return 0;
			}
			for (int i = 0; i < layout.nb_channels; i++)
			{
				if (layout.mapping[i] >= num && layout.mapping[i] != byte.MaxValue)
				{
					return 0;
				}
			}
			return 1;
		}

		internal static int get_left_channel(ChannelLayout layout, int stream_id, int prev)
		{
			for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++)
			{
				if (layout.mapping[i] == stream_id * 2)
				{
					return i;
				}
			}
			return -1;
		}

		internal static int get_right_channel(ChannelLayout layout, int stream_id, int prev)
		{
			for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++)
			{
				if (layout.mapping[i] == stream_id * 2 + 1)
				{
					return i;
				}
			}
			return -1;
		}

		internal static int get_mono_channel(ChannelLayout layout, int stream_id, int prev)
		{
			for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++)
			{
				if (layout.mapping[i] == stream_id + layout.nb_coupled_streams)
				{
					return i;
				}
			}
			return -1;
		}
	}
	internal static class Tables
	{
		internal static readonly float[] dct_table = new float[128]
		{
			0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f,
			0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.351851f, 0.33833f, 0.311806f, 0.2733f,
			0.224292f, 0.166664f, 0.102631f, 0.034654f, -0.034654f, -0.102631f, -0.166664f, -0.224292f, -0.2733f, -0.311806f,
			-0.33833f, -0.351851f, 0.34676f, 0.293969f, 0.196424f, 0.068975f, -0.068975f, -0.196424f, -0.293969f, -0.34676f,
			-0.34676f, -0.293969f, -0.196424f, -0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.34676f, 0.33833f, 0.224292f,
			0.034654f, -0.166664f, -0.311806f, -0.351851f, -0.2733f, -0.102631f, 0.102631f, 0.2733f, 0.351851f, 0.311806f,
			0.166664f, -0.034654f, -0.224292f, -0.33833f, 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f,
			0.135299f, 0.326641f, 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f, 0.135299f, 0.326641f,
			0.311806f, 0.034654f, -0.2733f, -0.33833f, -0.102631f, 0.224292f, 0.351851f, 0.166664f, -0.166664f, -0.351851f,
			-0.224292f, 0.102631f, 0.33833f, 0.2733f, -0.034654f, -0.311806f, 0.293969f, -0.068975f, -0.34676f, -0.196424f,
			0.196424f, 0.34676f, 0.068975f, -0.293969f, -0.293969f, 0.068975f, 0.34676f, 0.196424f, -0.196424f, -0.34676f,
			-0.068975f, 0.293969f, 0.2733f, -0.166664f, -0.33833f, 0.034654f, 0.351851f, 0.102631f, -0.311806f, -0.224292f,
			0.224292f, 0.311806f, -0.102631f, -0.351851f, -0.034654f, 0.33833f, 0.166664f, -0.2733f
		};

		internal static readonly float[] analysis_window = new float[240]
		{
			4.3E-05f, 0.000171f, 0.000385f, 0.000685f, 0.001071f, 0.001541f, 0.002098f, 0.002739f, 0.003466f, 0.004278f,
			0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f, 0.012329f, 0.013815f, 0.015385f, 0.017037f,
			0.018772f, 0.02059f, 0.02249f, 0.024472f, 0.026535f, 0.028679f, 0.030904f, 0.03321f, 0.035595f, 0.03806f,
			0.040604f, 0.043227f, 0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f,
			0.070297f, 0.07368f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f, 0.099373f, 0.103323f,
			0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.12408f, 0.128428f, 0.132839f, 0.137313f, 0.141849f, 0.146447f,
			0.151105f, 0.155823f, 0.1606f, 0.165435f, 0.170327f, 0.175276f, 0.18028f, 0.18534f, 0.190453f, 0.195619f,
			0.200838f, 0.206107f, 0.211427f, 0.216797f, 0.222215f, 0.22768f, 0.233193f, 0.238751f, 0.244353f, 0.25f,
			0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.29067f, 0.296632f, 0.302628f, 0.308658f,
			0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.33928f, 0.345492f, 0.351729f, 0.357992f, 0.36428f, 0.37059f,
			0.376923f, 0.383277f, 0.389651f, 0.396044f, 0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f,
			0.441231f, 0.447736f, 0.454249f, 0.46077f, 0.467298f, 0.473832f, 0.48037f, 0.486912f, 0.493455f, 0.5f,
			0.506545f, 0.513088f, 0.51963f, 0.526168f, 0.532702f, 0.53923f, 0.545751f, 0.552264f, 0.558769f, 0.565263f,
			0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f, 0.610349f, 0.616723f, 0.623077f, 0.62941f,
			0.63572f, 0.642008f, 0.648271f, 0.654508f, 0.66072f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f,
			0.697372f, 0.703368f, 0.70933f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.75f,
			0.755647f, 0.761249f, 0.766807f, 0.77232f, 0.777785f, 0.783203f, 0.788573f, 0.793893f, 0.799162f, 0.804381f,
			0.809547f, 0.81466f, 0.81972f, 0.824724f, 0.829673f, 0.834565f, 0.8394f, 0.844177f, 0.848895f, 0.853553f,
			0.858151f, 0.862687f, 0.867161f, 0.871572f, 0.87592f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f,
			0.900627f, 0.904508f, 0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.92632f, 0.929703f, 0.933013f,
			0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f, 0.959396f, 0.96194f,
			0.964405f, 0.96679f, 0.969096f, 0.971321f, 0.973465f, 0.975528f, 0.97751f, 0.97941f, 0.981228f, 0.982963f,
			0.984615f, 0.986185f, 0.987671f, 0.989074f, 0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f,
			0.996534f, 0.997261f, 0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1f
		};

		internal static readonly int[] tbands = new int[19]
		{
			2, 4, 6, 8, 10, 12, 14, 16, 20, 24,
			28, 32, 40, 48, 56, 68, 80, 96, 120
		};

		internal static readonly int[] extra_bands = new int[22]
		{
			1, 2, 4, 6, 8, 10, 12, 14, 16, 20,
			24, 28, 32, 40, 48, 56, 68, 80, 96, 120,
			160, 200
		};

		internal static readonly float[] weights = new float[422]
		{
			-0.0941125f,
			-0.302976f,
			-0.603555f,
			-0.19393f,
			-0.185983f,
			-0.601617f,
			-0.0465317f,
			-0.114563f,
			-0.103599f,
			-0.618938f,
			-0.317859f,
			-0.169949f,
			-0.0702885f,
			0.148065f,
			0.409524f,
			0.548432f,
			0.367649f,
			-0.494393f,
			0.764306f,
			-1.83957f,
			0.170849f,
			12.786f,
			-1.08848f,
			-1.27284f,
			-16.2606f,
			24.1773f,
			-5.57454f,
			-0.17276f,
			-0.163388f,
			-0.224421f,
			-0.0948944f,
			-0.0728695f,
			-0.26557f,
			-0.100283f,
			-0.0515459f,
			-0.146142f,
			-0.120674f,
			-0.180655f,
			0.12857f,
			0.442138f,
			-0.493735f,
			0.167767f,
			0.206699f,
			-0.197567f,
			0.417999f,
			1.50364f,
			-0.773341f,
			-10.0401f,
			0.401872f,
			2.97966f,
			15.2165f,
			-1.88905f,
			-1.19254f,
			0.0285397f,
			-0.00405139f,
			0.0707565f,
			0.00825699f,
			-0.0927269f,
			-0.010393f,
			-0.00428882f,
			-0.00489743f,
			-0.0709731f,
			-0.00255992f,
			0.0395619f,
			0.226424f,
			0.0325231f,
			0.162175f,
			-0.100118f,
			0.485789f,
			0.12697f,
			0.285937f,
			0.0155637f,
			0.10546f,
			3.05558f,
			1.15059f,
			-1.00904f,
			-1.83088f,
			3.31766f,
			-3.42516f,
			-0.119135f,
			-0.0405654f,
			0.00690068f,
			0.0179877f,
			-0.0382487f,
			0.00597941f,
			-0.0183611f,
			0.00190395f,
			-0.144322f,
			-0.0435671f,
			0.000990594f,
			0.221087f,
			0.142405f,
			0.484066f,
			0.404395f,
			0.511955f,
			-0.237255f,
			0.241742f,
			0.35045f,
			-0.699428f,
			10.3993f,
			2.6507f,
			-2.43459f,
			-4.18838f,
			1.05928f,
			1.71067f,
			0.00667811f,
			-0.0721335f,
			-0.0397346f,
			0.0362704f,
			-0.11496f,
			-0.0235776f,
			0.0082161f,
			-0.0141741f,
			-0.0329699f,
			-0.0354253f,
			0.00277404f,
			-0.290654f,
			-1.14767f,
			-0.319157f,
			-0.686544f,
			0.36897f,
			0.478899f,
			0.182579f,
			-0.411069f,
			0.881104f,
			-4.60683f,
			1.4697f,
			325f / (356f * MathF.E),
			-1.81905f,
			-30.1699f,
			5.55225f,
			0.0019508f,
			-0.123576f,
			-0.0727332f,
			-0.0641597f,
			-0.0534458f,
			-0.108166f,
			-0.0937368f,
			-0.0697883f,
			-0.0275475f,
			-0.192309f,
			-0.110074f,
			0.285375f,
			-0.405597f,
			0.0926724f,
			-0.287881f,
			-0.851193f,
			-0.099493f,
			-0.233764f,
			-1.2852f,
			1.13611f,
			3.12168f,
			-0.0699f,
			-1.86216f,
			2.65292f,
			-7.31036f,
			2.44776f,
			-0.00111802f,
			-0.0632786f,
			-0.0376296f,
			-0.149851f,
			0.142963f,
			0.184368f,
			0.123433f,
			0.0756158f,
			0.117312f,
			0.0933395f,
			0.0692163f,
			0.0842592f,
			0.0704683f,
			0.0589963f,
			0.0942205f,
			-0.448862f,
			0.0262677f,
			0.270352f,
			-0.262317f,
			0.172586f,
			2.00227f,
			-0.159216f,
			0.038422f,
			10.2073f,
			4.15536f,
			-2.3407f,
			-0.0550265f,
			0.00964792f,
			-0.141336f,
			0.0274501f,
			0.0343921f,
			-0.0487428f,
			0.0950172f,
			-0.00775017f,
			-0.0372492f,
			-0.00548121f,
			-0.0663695f,
			0.0960506f,
			-0.200008f,
			-0.0412827f,
			0.58728f,
			0.0515787f,
			0.337254f,
			0.855024f,
			0.668371f,
			-0.114904f,
			-3.62962f,
			-0.467477f,
			-0.215472f,
			2.61537f,
			0.406117f,
			-1.36373f,
			0.0425394f,
			0.12208f,
			0.0934502f,
			0.123055f,
			0.0340935f,
			-0.142466f,
			0.035037f,
			-0.0490666f,
			0.0733208f,
			0.0576672f,
			0.123984f,
			-0.0517194f,
			-0.253018f,
			0.590565f,
			0.145849f,
			0.315185f,
			0.221534f,
			-0.149081f,
			0.216161f,
			-0.349575f,
			24.5664f,
			-0.994196f,
			0.614289f,
			-18.7905f,
			-2.83277f,
			-0.716801f,
			-0.347201f,
			0.479515f,
			-0.246027f,
			0.0758683f,
			0.137293f,
			-0.17781f,
			0.118751f,
			-0.00108329f,
			-0.237334f,
			0.355732f,
			-0.12991f,
			-0.0547627f,
			-0.318576f,
			-0.325524f,
			0.180494f,
			-0.0625604f,
			0.141219f,
			0.344064f,
			0.37658f,
			-0.591772f,
			5.8427f,
			-0.38075f,
			0.221894f,
			-1.41934f,
			-1879430f,
			1.34114f,
			0.0283355f,
			-0.0447856f,
			-0.0211466f,
			-0.0256927f,
			0.0139618f,
			0.0207934f,
			-0.0107666f,
			0.0110969f,
			0.0586069f,
			-0.0253545f,
			-0.0328433f,
			0.11872f,
			-0.216943f,
			0.145748f,
			0.119808f,
			-0.0915211f,
			-0.120647f,
			-0.0787719f,
			-0.143644f,
			-0.595116f,
			-1.152f,
			-1.25335f,
			-1.17092f,
			4.34023f,
			-975268f,
			-1.37033f,
			-0.0401123f,
			0.210602f,
			-0.136656f,
			0.135962f,
			-0.0523293f,
			0.0444604f,
			0.0143928f,
			0.00412666f,
			-0.0193003f,
			0.218452f,
			-0.110204f,
			-2.02563f,
			0.918238f,
			-2.45362f,
			1.19542f,
			-0.061362f,
			-1.92243f,
			0.308111f,
			0.49764f,
			0.912356f,
			0.209272f,
			-2.34525f,
			2.19326f,
			-6.47121f,
			1.69771f,
			-0.725123f,
			0.0118929f,
			0.0377944f,
			0.0554003f,
			0.0226452f,
			-0.0704421f,
			-0.0300309f,
			0.0122978f,
			-0.0041782f,
			-0.0686612f,
			0.0313115f,
			0.039111f,
			0.364111f,
			-0.0945548f,
			0.0229876f,
			-0.17414f,
			0.329795f,
			0.114714f,
			0.30022f,
			0.106997f,
			0.132355f,
			5.79932f,
			0.908058f,
			-0.905324f,
			-3.3561f,
			0.190647f,
			0.184211f,
			-0.673648f,
			0.231807f,
			-0.0586222f,
			0.230752f,
			-0.438277f,
			0.245857f,
			-0.17215f,
			0.0876383f,
			-0.720512f,
			0.162515f,
			0.0170571f,
			0.101781f,
			0.388477f,
			1.32931f,
			1.08548f,
			-0.936301f,
			-2.36958f,
			-6.71988f,
			-3.44376f,
			2.13818f,
			14.2318f,
			4.91459f,
			-3.09052f,
			-9.69191f,
			-0.768234f,
			1.79604f,
			0.0549653f,
			0.163399f,
			0.0797025f,
			0.0343933f,
			-0.0555876f,
			-0.00505673f,
			0.0187258f,
			0.0326628f,
			0.0231486f,
			0.15573f,
			0.0476223f,
			-0.254824f,
			1.60155f,
			-0.801221f,
			2.55496f,
			0.737629f,
			-1.36249f,
			-0.695463f,
			-2.44301f,
			-1.73188f,
			3.95279f,
			1.89068f,
			0.486087f,
			-11.3343f,
			3941600f,
			-0.381439f,
			0.12115f,
			-0.906927f,
			2.93878f,
			1.6388f,
			0.882811f,
			0.874344f,
			1.21726f,
			-0.874545f,
			0.321706f,
			0.785055f,
			0.946558f,
			-0.575066f,
			-3.46553f,
			0.884905f,
			0.0924047f,
			-9.90712f,
			0.391338f,
			0.160103f,
			-2.04954f,
			4.1455f,
			0.0684029f,
			-0.144761f,
			-0.285282f,
			0.379244f,
			-1.1584f,
			-0.0277241f,
			-9.85f,
			-4.82386f,
			3.71333f,
			3.87308f,
			3.52558f
		};

		internal static readonly int[] topo = new int[3] { 25, 15, 2 };

		internal static readonly MLP net = new MLP
		{
			layers = 3,
			topo = topo,
			weights = weights
		};

		internal static readonly float[] tansig_table = new float[201]
		{
			0f, 0.039979f, 0.07983f, 0.119427f, 0.158649f, 0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f,
			0.379949f, 0.413644f, 0.446244f, 0.4777f, 0.507977f, 0.53705f, 0.5649f, 0.591519f, 0.616909f, 0.641077f,
			0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f, 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.82104f,
			0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, 0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.91542f,
			0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f, 0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.96109f,
			0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f, 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f,
			0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, 0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.99202f,
			0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f, 0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f,
			0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.99759f, 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f,
			0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, 0.999f, 0.999076f, 0.999147f, 0.999213f, 0.999273f,
			0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f, 0.99955f, 0.999585f, 0.999617f, 0.999646f, 0.999673f,
			0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f, 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f,
			0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, 0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f,
			0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f, 0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.99997f,
			0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.99998f, 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f,
			0.999988f, 0.999989f, 0.99999f, 0.99999f, 0.999991f, 0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f,
			0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f, 0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f,
			0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f,
			0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f,
			1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f,
			1f
		};

		internal static readonly int[] mono_voice_bandwidth_thresholds = new int[8] { 11000, 1000, 14000, 1000, 17000, 1000, 21000, 2000 };

		internal static readonly int[] mono_music_bandwidth_thresholds = new int[8] { 12000, 1000, 15000, 1000, 18000, 2000, 22000, 2000 };

		internal static readonly int[] stereo_voice_bandwidth_thresholds = new int[8] { 11000, 1000, 14000, 1000, 21000, 2000, 28000, 2000 };

		internal static readonly int[] stereo_music_bandwidth_thresholds = new int[8] { 12000, 1000, 18000, 2000, 21000, 2000, 30000, 2000 };

		internal const int stereo_voice_threshold = 30000;

		internal const int stereo_music_threshold = 30000;

		internal static readonly int[][] mode_thresholds = new int[2][]
		{
			new int[2] { 64000, 16000 },
			new int[2] { 36000, 16000 }
		};
	}
	public static class ResamplerFactory
	{
		public static IResampler CreateResampler(int numChannels, int inRate, int outRate, int quality, TextWriter logger = null)
		{
			return CreateResampler(numChannels, inRate, outRate, inRate, outRate, quality, logger);
		}

		public static IResampler CreateResampler(int numChannels, int ratioNum, int ratioDen, int inRate, int outRate, int quality, TextWriter logger = null)
		{
			if (numChannels <= 0)
			{
				throw new ArgumentOutOfRangeException("numChannels");
			}
			if (ratioNum <= 0)
			{
				throw new ArgumentOutOfRangeException("ratioNum");
			}
			if (ratioDen <= 0)
			{
				throw new ArgumentOutOfRangeException("ratioDen");
			}
			if (inRate <= 0)
			{
				throw new ArgumentOutOfRangeException("inRate");
			}
			if (outRate <= 0)
			{
				throw new ArgumentOutOfRangeException("outRate");
			}
			if (quality < 0 || quality > 10)
			{
				throw new ArgumentOutOfRangeException("quality", "Quality must be between 0 and 10");
			}
			return new SpeexResampler(numChannels, ratioNum, ratioDen, inRate, outRate, quality);
		}
	}
}
namespace Concentus.Silk
{
	internal static class ApplySineWindow
	{
		private static readonly short[] freq_table_Q16 = new short[27]
		{
			12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202, 3885,
			3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422, 2313, 2214,
			2123, 2038, 1961, 1889, 1822, 1760, 1702
		};

		internal static void silk_apply_sine_window(short[] px_win, int px_win_ptr, short[] px, int px_ptr, int win_type, int length)
		{
			int num = (length >> 2) - 4;
			int num2 = freq_table_Q16[num];
			int num3 = Inlines.silk_SMULWB(num2, -num2);
			int num4;
			int num5;
			if (win_type == 1)
			{
				num4 = 0;
				num5 = num2 + Inlines.silk_RSHIFT(length, 3);
			}
			else
			{
				num4 = 65536;
				num5 = 65536 + Inlines.silk_RSHIFT(num3, 1) + Inlines.silk_RSHIFT(length, 4);
			}
			for (num = 0; num < length; num += 4)
			{
				int num6 = px_win_ptr + num;
				int num7 = px_ptr + num;
				px_win[num6] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(num4 + num5, 1), px[num7]);
				px_win[num6 + 1] = (short)Inlines.silk_SMULWB(num5, px[num7 + 1]);
				num4 = Inlines.silk_SMULWB(num5, num3) + Inlines.silk_LSHIFT(num5, 1) - num4 + 1;
				num4 = Inlines.silk_min(num4, 65536);
				px_win[num6 + 2] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(num4 + num5, 1), px[num7 + 2]);
				px_win[num6 + 3] = (short)Inlines.silk_SMULWB(num4, px[num7 + 3]);
				num5 = Inlines.silk_SMULWB(num4, num3) + Inlines.silk_LSHIFT(num4, 1) - num5;
				num5 = Inlines.silk_min(num5, 65536);
			}
		}
	}
	internal static class BurgModified
	{
		private const int MAX_FRAME_SIZE = 384;

		private const int QA = 25;

		private const int N_BITS_HEAD_ROOM = 2;

		private const int MIN_RSHIFTS = -16;

		private const int MAX_RSHIFTS = 7;

		internal static void silk_burg_modified(BoxedValueInt res_nrg, BoxedValueInt res_nrg_Q, int[] A_Q16, short[] x, int x_ptr, int minInvGain_Q30, int subfr_length, int nb_subfr, int D)
		{
			int[] array = new int[16];
			int[] array2 = new int[16];
			int[] array3 = new int[16];
			int[] array4 = new int[17];
			int[] array5 = new int[17];
			int[] array6 = new int[16];
			long num = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr);
			int num2 = Inlines.silk_CLZ64(num);
			int num3 = 35 - num2;
			if (num3 > 7)
			{
				num3 = 7;
			}
			if (num3 < -16)
			{
				num3 = -16;
			}
			int num4 = (int)((num3 <= 0) ? Inlines.silk_LSHIFT32((int)num, -num3) : Inlines.silk_RSHIFT64(num, num3));
			array5[0] = (array4[0] = num4 + Inlines.silk_SMMUL(42950, num4) + 1);
			Arrays.MemSetInt(array, 0, 16);
			if (num3 > 0)
			{
				for (int i = 0; i < nb_subfr; i++)
				{
					int num5 = x_ptr + i * subfr_length;
					for (int j = 1; j < D + 1; j++)
					{
						array[j - 1] += (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, num5, x, num5 + j, subfr_length - j), num3);
					}
				}
			}
			else
			{
				for (int i = 0; i < nb_subfr; i++)
				{
					int num5 = x_ptr + i * subfr_length;
					CeltPitchXCorr.pitch_xcorr(x, num5, x, num5 + 1, array6, subfr_length - D, D);
					for (int j = 1; j < D + 1; j++)
					{
						int k = j + subfr_length - D;
						int num6 = 0;
						for (; k < subfr_length; k++)
						{
							num6 = Inlines.MAC16_16(num6, x[num5 + k], x[num5 + k - j]);
						}
						array6[j - 1] += num6;
					}
					for (int j = 1; j < D + 1; j++)
					{
						array[j - 1] += Inlines.silk_LSHIFT32(array6[j - 1], -num3);
					}
				}
			}
			Arrays.MemCopy(array, 0, array2, 0, 16);
			array5[0] = (array4[0] = num4 + Inlines.silk_SMMUL(42950, num4) + 1);
			int num7 = 1073741824;
			int num8 = 0;
			for (int j = 0; j < D; j++)
			{
				int num9;
				int num10;
				if (num3 > -2)
				{
					for (int i = 0; i < nb_subfr; i++)
					{
						int num5 = x_ptr + i * subfr_length;
						int b = -Inlines.silk_LSHIFT32(x[num5 + j], 16 - num3);
						int b2 = -Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 16 - num3);
						num9 = Inlines.silk_LSHIFT32(x[num5 + j], 9);
						num10 = Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 9);
						for (int l = 0; l < j; l++)
						{
							array[l] = Inlines.silk_SMLAWB(array[l], b, x[num5 + j - l - 1]);
							array2[l] = Inlines.silk_SMLAWB(array2[l], b2, x[num5 + subfr_length - j + l]);
							int b3 = array3[l];
							num9 = Inlines.silk_SMLAWB(num9, b3, x[num5 + j - l - 1]);
							num10 = Inlines.silk_SMLAWB(num10, b3, x[num5 + subfr_length - j + l]);
						}
						num9 = Inlines.silk_LSHIFT32(-num9, 7 - num3);
						num10 = Inlines.silk_LSHIFT32(-num10, 7 - num3);
						for (int l = 0; l <= j; l++)
						{
							array4[l] = Inlines.silk_SMLAWB(array4[l], num9, x[num5 + j - l]);
							array5[l] = Inlines.silk_SMLAWB(array5[l], num10, x[num5 + subfr_length - j + l - 1]);
						}
					}
				}
				else
				{
					for (int i = 0; i < nb_subfr; i++)
					{
						int num5 = x_ptr + i * subfr_length;
						int b = -Inlines.silk_LSHIFT32(x[num5 + j], -num3);
						int b2 = -Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], -num3);
						num9 = Inlines.silk_LSHIFT32(x[num5 + j], 17);
						num10 = Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 17);
						for (int l = 0; l < j; l++)
						{
							array[l] = Inlines.silk_MLA(array[l], b, x[num5 + j - l - 1]);
							array2[l] = Inlines.silk_MLA(array2[l], b2, x[num5 + subfr_length - j + l]);
							int c = Inlines.silk_RSHIFT_ROUND(array3[l], 8);
							num9 = Inlines.silk_MLA(num9, x[num5 + j - l - 1], c);
							num10 = Inlines.silk_MLA(num10, x[num5 + subfr_length - j + l], c);
						}
						num9 = -num9;
						num10 = -num10;
						for (int l = 0; l <= j; l++)
						{
							array4[l] = Inlines.silk_SMLAWW(array4[l], num9, Inlines.silk_LSHIFT32(x[num5 + j - l], -num3 - 1));
							array5[l] = Inlines.silk_SMLAWW(array5[l], num10, Inlines.silk_LSHIFT32(x[num5 + subfr_length - j + l - 1], -num3 - 1));
						}
					}
				}
				num9 = array[j];
				num10 = array2[j];
				int a = 0;
				int num11 = Inlines.silk_ADD32(array5[0], array4[0]);
				for (int l = 0; l < j; l++)
				{
					int b3 = array3[l];
					num2 = Inlines.silk_CLZ32(Inlines.silk_abs(b3)) - 1;
					num2 = Inlines.silk_min(7, num2);
					int c = Inlines.silk_LSHIFT32(b3, num2);
					num9 = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(array2[j - l - 1], c), 7 - num2);
					num10 = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(array[j - l - 1], c), 7 - num2);
					a = Inlines.silk_ADD_LSHIFT32(a, Inlines.silk_SMMUL(array5[j - l], c), 7 - num2);
					num11 = Inlines.silk_ADD_LSHIFT32(num11, Inlines.silk_SMMUL(Inlines.silk_ADD32(array5[l + 1], array4[l + 1]), c), 7 - num2);
				}
				array4[j + 1] = num9;
				array5[j + 1] = num10;
				a = Inlines.silk_ADD32(a, num10);
				a = Inlines.silk_LSHIFT32(-a, 1);
				int num12 = ((Inlines.silk_abs(a) >= num11) ? ((a > 0) ? int.MaxValue : int.MinValue) : Inlines.silk_DIV32_varQ(a, num11, 31));
				num9 = 1073741824 - Inlines.silk_SMMUL(num12, num12);
				num9 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(num7, num9), 2);
				if (num9 <= minInvGain_Q30)
				{
					num10 = 1073741824 - Inlines.silk_DIV32_varQ(minInvGain_Q30, num7, 30);
					num12 = Inlines.silk_SQRT_APPROX(num10);
					num12 = Inlines.silk_RSHIFT32(num12 + Inlines.silk_DIV32(num10, num12), 1);
					num12 = Inlines.silk_LSHIFT32(num12, 16);
					if (a < 0)
					{
						num12 = -num12;
					}
					num7 = minInvGain_Q30;
					num8 = 1;
				}
				else
				{
					num7 = num9;
				}
				for (int l = 0; l < j + 1 >> 1; l++)
				{
					num9 = array3[l];
					num10 = array3[j - l - 1];
					array3[l] = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(num10, num12), 1);
					array3[j - l - 1] = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(num9, num12), 1);
				}
				array3[j] = Inlines.silk_RSHIFT32(num12, 6);
				if (num8 != 0)
				{
					for (int l = j + 1; l < D; l++)
					{
						array3[l] = 0;
					}
					break;
				}
				for (int l = 0; l <= j + 1; l++)
				{
					num9 = array4[l];
					num10 = array5[j - l + 1];
					array4[l] = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(num10, num12), 1);
					array5[j - l + 1] = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(num9, num12), 1);
				}
			}
			if (num8 != 0)
			{
				for (int l = 0; l < D; l++)
				{
					A_Q16[l] = -Inlines.silk_RSHIFT_ROUND(array3[l], 9);
				}
				if (num3 > 0)
				{
					for (int i = 0; i < nb_subfr; i++)
					{
						int num5 = x_ptr + i * subfr_length;
						num4 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, num5, x, num5, D), num3);
					}
				}
				else
				{
					for (int i = 0; i < nb_subfr; i++)
					{
						int num5 = x_ptr + i * subfr_length;
						num4 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, num5, D), -num3);
					}
				}
				res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(num7, num4), 2);
				res_nrg_Q.Val = -num3;
			}
			else
			{
				int num11 = array4[0];
				int num9 = 65536;
				for (int l = 0; l < D; l++)
				{
					int c = Inlines.silk_RSHIFT_ROUND(array3[l], 9);
					num11 = Inlines.silk_SMLAWW(num11, array4[l + 1], c);
					num9 = Inlines.silk_SMLAWW(num9, c, c);
					A_Q16[l] = -c;
				}
				res_nrg.Val = Inlines.silk_SMLAWW(num11, Inlines.silk_SMMUL(42950, num4), -num9);
				res_nrg_Q.Val = -num3;
			}
		}
	}
	internal static class BWExpander
	{
		internal static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16)
		{
			int b = chirp_Q16 - 65536;
			for (int i = 0; i < d - 1; i++)
			{
				ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]);
				chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, b), 16);
			}
			ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]);
		}

		internal static void silk_bwexpander(short[] ar, int d, int chirp_Q16)
		{
			int b = chirp_Q16 - 65536;
			for (int i = 0; i < d - 1; i++)
			{
				ar[i] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[i]), 16);
				chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, b), 16);
			}
			ar[d - 1] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[d - 1]), 16);
		}
	}
	internal static class CNG
	{
		internal static void silk_CNG_exc(Span<int> exc_Q10, int exc_Q10_ptr, int[] exc_buf_Q14, int Gain_Q16, int length, ref int rand_seed)
		{
			int num;
			for (num = 255; num > length; num = Inlines.silk_RSHIFT(num, 1))
			{
			}
			int num2 = rand_seed;
			for (int i = exc_Q10_ptr; i < exc_Q10_ptr + length; i++)
			{
				num2 = Inlines.silk_RAND(num2);
				int num3 = Inlines.silk_RSHIFT(num2, 24) & num;
				exc_Q10[i] = (short)Inlines.silk_SAT16(Inlines.silk_SMULWW(exc_buf_Q14[num3], Gain_Q16 >> 4));
			}
			rand_seed = num2;
		}

		internal static void silk_CNG_Reset(SilkChannelDecoder psDec)
		{
			int num = Inlines.silk_DIV32_16(32767, (short)(psDec.LPC_order + 1));
			int num2 = 0;
			for (int i = 0; i < psDec.LPC_order; i++)
			{
				num2 += num;
				psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short)num2;
			}
			psDec.sCNG.CNG_smth_Gain_Q16 = 0;
			psDec.sCNG.rand_seed = 3176576;
		}

		internal static void silk_CNG(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, Span<short> frame, int frame_ptr, int length)
		{
			short[] array = new short[psDec.LPC_order];
			CNGState sCNG = psDec.sCNG;
			if (psDec.fs_kHz != sCNG.fs_kHz)
			{
				silk_CNG_Reset(psDec);
				sCNG.fs_kHz = psDec.fs_kHz;
			}
			if (psDec.lossCnt == 0 && psDec.prevSignalType == 0)
			{
				for (int i = 0; i < psDec.LPC_order; i++)
				{
					sCNG.CNG_smth_NLSF_Q15[i] += (short)Inlines.silk_SMULWB(psDec.prevNLSF_Q15[i] - sCNG.CNG_smth_NLSF_Q15[i], 16348);
				}
				int num = 0;
				for (int i = 0; i < psDec.nb_subfr; i++)
				{
					if (psDecCtrl.Gains_Q16[i] > num)
					{
						num = psDecCtrl.Gains_Q16[i];
					}
				}
				Arrays.MemMoveInt(sCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length);
				for (int i = 0; i < psDec.nb_subfr; i++)
				{
					sCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - sCNG.CNG_smth_Gain_Q16, 4634);
				}
			}
			if (psDec.lossCnt != 0)
			{
				int[] array2 = new int[length + 16];
				int num2 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]);
				if (num2 >= 2097152 || sCNG.CNG_smth_Gain_Q16 > 8388608)
				{
					num2 = Inlines.silk_SMULTT(num2, num2);
					num2 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(sCNG.CNG_smth_Gain_Q16, sCNG.CNG_smth_Gain_Q16), num2, 5);
					num2 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(num2), 16);
				}
				else
				{
					num2 = Inlines.silk_SMULWW(num2, num2);
					num2 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(sCNG.CNG_smth_Gain_Q16, sCNG.CNG_smth_Gain_Q16), num2, 5);
					num2 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(num2), 8);
				}
				silk_CNG_exc(array2, 16, sCNG.CNG_exc_buf_Q14, num2, length, ref sCNG.rand_seed);
				NLSF.silk_NLSF2A(array, sCNG.CNG_smth_NLSF_Q15, psDec.LPC_order);
				Arrays.MemCopy(sCNG.CNG_synth_state, 0, array2, 0, 16);
				for (int i = 0; i < length; i++)
				{
					int num3 = 16 + i;
					int a = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 1], array[0]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 2], array[1]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 3], array[2]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 4], array[3]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 5], array[4]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 6], array[5]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 7], array[6]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 8], array[7]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 9], array[8]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 10], array[9]);
					if (psDec.LPC_order == 16)
					{
						a = Inlines.silk_SMLAWB(a, array2[num3 - 11], array[10]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 12], array[11]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 13], array[12]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 14], array[13]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 15], array[14]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 16], array[15]);
					}
					array2[num3] = Inlines.silk_ADD_LSHIFT(array2[num3], a, 4);
					frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short)Inlines.silk_RSHIFT_ROUND(array2[num3], 10));
				}
				Arrays.MemCopy(array2, length, sCNG.CNG_synth_state, 0, 16);
			}
			else
			{
				Arrays.MemSetInt(sCNG.CNG_synth_state, 0, psDec.LPC_order);
			}
		}
	}
	internal static class CodeSigns
	{
		private static int silk_enc_map(int a)
		{
			return Inlines.silk_RSHIFT(a, 15) + 1;
		}

		private static int silk_dec_map(int a)
		{
			return Inlines.silk_LSHIFT(a, 1) - 1;
		}

		internal static void silk_encode_signs(EntropyCoder psRangeEnc, Span<byte> encodedData, Span<sbyte> pulses, int length, int signalType, int quantOffsetType, int[] sum_pulses)
		{
			byte[] array = new byte[2];
			byte[] silk_sign_iCDF = Tables.silk_sign_iCDF;
			array[1] = 0;
			int num = 0;
			int num2 = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1));
			int num3 = num2;
			length = Inlines.silk_RSHIFT(length + 8, 4);
			for (num2 = 0; num2 < length; num2++)
			{
				int num4 = sum_pulses[num2];
				if (num4 > 0)
				{
					array[0] = silk_sign_iCDF[num3 + Inlines.silk_min(num4 & 0x1F, 6)];
					for (int i = num; i < num + 16; i++)
					{
						if (pulses[i] != 0)
						{
							psRangeEnc.enc_icdf(encodedData, silk_enc_map(pulses[i]), array, 8u);
						}
					}
				}
				num += 16;
			}
		}

		internal static void silk_decode_signs(EntropyCoder psRangeDec, ReadOnlySpan<byte> encodedData, short[] pulses, int length, int signalType, int quantOffsetType, int[] sum_pulses)
		{
			byte[] array = new byte[2];
			byte[] silk_sign_iCDF = Tables.silk_sign_iCDF;
			array[1] = 0;
			int num = 0;
			int num2 = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1));
			int num3 = num2;
			length = Inlines.silk_RSHIFT(length + 8, 4);
			for (num2 = 0; num2 < length; num2++)
			{
				int num4 = sum_pulses[num2];
				if (num4 > 0)
				{
					array[0] = silk_sign_iCDF[num3 + Inlines.silk_min(num4 & 0x1F, 6)];
					for (int i = 0; i < 16; i++)
					{
						if (pulses[num + i] > 0)
						{
							pulses[num + i] *= (short)silk_dec_map(psRangeDec.dec_icdf(encodedData, array, 8u));
						}
					}
				}
				num += 16;
			}
		}
	}
	internal static class CorrelateMatrix
	{
		internal static void silk_corrVector(short[] x, int x_ptr, short[] t, int t_ptr, int L, int order, int[] Xt, int rshifts)
		{
			int num = x_ptr + order - 1;
			if (rshifts > 0)
			{
				for (int i = 0; i < order; i++)
				{
					int num2 = 0;
					for (int j = 0; j < L; j++)
					{
						num2 += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num + j], t[t_ptr + j]), rshifts);
					}
					Xt[i] = num2;
					num--;
				}
			}
			else
			{
				for (int i = 0; i < order; i++)
				{
					Xt[i] = Inlines.silk_inner_prod(x, num, t, t_ptr, L);
					num--;
				}
			}
		}

		internal static void silk_corrMatrix(short[] x, int x_ptr, int L, int order, int head_room, int[] XX, int XX_ptr, BoxedValueInt rshifts)
		{
			SumSqrShift.silk_sum_sqr_shift(out var energy, out var shift, x, x_ptr, L + order - 1);
			int num = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0);
			energy = Inlines.silk_RSHIFT32(energy, num);
			shift += num;
			for (int i = x_ptr; i < x_ptr + order - 1; i++)
			{
				energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), shift);
			}
			if (shift < rshifts.Val)
			{
				energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - shift);
				shift = rshifts.Val;
			}
			Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy);
			int num2 = x_ptr + order - 1;
			for (int j = 1; j < order; j++)
			{
				energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + L - j], x[num2 + L - j]), shift));
				energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 - j], x[num2 - j]), shift));
				Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy);
			}
			int num3 = x_ptr + order - 2;
			if (shift > 0)
			{
				for (int k = 1; k < order; k++)
				{
					energy = 0;
					for (int i = 0; i < L; i++)
					{
						energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + i], x[num3 + i]), shift);
					}
					Inlines.MatrixSet(XX, XX_ptr, k, 0, order, energy);
					Inlines.MatrixSet(XX, XX_ptr, 0, k, order, energy);
					for (int j = 1; j < order - k; j++)
					{
						energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + L - j], x[num3 + L - j]), shift));
						energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 - j], x[num3 - j]), shift));
						Inlines.MatrixSet(XX, XX_ptr, k + j, j, order, energy);
						Inlines.MatrixSet(XX, XX_ptr, j, k + j, order, energy);
					}
					num3--;
				}
			}
			else
			{
				for (int k = 1; k < order; k++)
				{
					energy = Inlines.silk_inner_prod(x, num2, x, num3, L);
					Inlines.MatrixSet(XX, XX_ptr, k, 0, order, energy);
					Inlines.MatrixSet(XX, XX_ptr, 0, k, order, energy);
					for (int j = 1; j < order - k; j++)
					{
						energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[num2 + L - j], x[num3 + L - j]));
						energy = Inlines.silk_SMLABB(energy, x[num2 - j], x[num3 - j]);
						Inlines.MatrixSet(XX, XX_ptr, k + j, j, order, energy);
						Inlines.MatrixSet(XX, XX_ptr, j, k + j, order, energy);
					}
					num3--;
				}
			}
			rshifts.Val = shift;
		}
	}
	internal static class DecodeAPI
	{
		internal static int silk_InitDecoder(SilkDecoder decState)
		{
			decState.Reset();
			int result = SilkError.SILK_NO_ERROR;
			SilkChannelDecoder[] channel_state = decState.channel_state;
			for (int i = 0; i < 2; i++)
			{
				result = channel_state[i].silk_init_decoder();
			}
			decState.sStereo.Reset();
			decState.prev_decode_only_middle = 0;
			return result;
		}

		internal static int silk_Decode(SilkDecoder psDec, DecControlState decControl, ReadOnlySpan<byte> frameData, int lostFlag, int newPacketFlag, EntropyCoder psRangeDec, Span<short> samplesOut, int samplesOut_ptr, out int nSamplesOut)
		{
			int num = 0;
			int num2 = SilkError.SILK_NO_ERROR;
			BoxedValueInt boxedValueInt = new BoxedValueInt();
			int[] array = new int[2];
			int[] array2 = new int[2];
			SilkChannelDecoder[] channel_state = psDec.channel_state;
			nSamplesOut = 0;
			if (newPacketFlag != 0)
			{
				for (int i = 0; i < decControl.nChannelsInternal; i++)
				{
					channel_state[i].nFramesDecoded = 0;
				}
			}
			if (decControl.nChannelsInternal > psDec.nChannelsInternal)
			{
				num2 += channel_state[1].silk_init_decoder();
			}
			int num3 = ((decControl.nChannelsInternal == 1 && psDec.nChannelsInternal == 2 && decControl.internalSampleRate == 1000 * channel_state[0].fs_kHz) ? 1 : 0);
			if (channel_state[0].nFramesDecoded == 0)
			{
				for (int i = 0; i < decControl.nChannelsInternal; i++)
				{
					if (decControl.payloadSize_ms == 0)
					{
						channel_state[i].nFramesPerPacket = 1;
						channel_state[i].nb_subfr = 2;
					}
					else if (decControl.payloadSize_ms == 10)
					{
						channel_state[i].nFramesPerPacket = 1;
						channel_state[i].nb_subfr = 2;
					}
					else if (decControl.payloadSize_ms == 20)
					{
						channel_state[i].nFramesPerPacket = 1;
						channel_state[i].nb_subfr = 4;
					}
					else if (decControl.payloadSize_ms == 40)
					{
						channel_state[i].nFramesPerPacket = 2;
						channel_state[i].nb_subfr = 4;
					}
					else
					{
						if (decControl.payloadSize_ms != 60)
						{
							return SilkError.SILK_DEC_INVALID_FRAME_SIZE;
						}
						channel_state[i].nFramesPerPacket = 3;
						channel_state[i].nb_subfr = 4;
					}
					int num4 = (decControl.internalSampleRate >> 10) + 1;
					if (num4 != 8 && num4 != 12 && num4 != 16)
					{
						return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY;
					}
					num2 += channel_state[i].silk_decoder_set_fs(num4, decControl.API_sampleRate);
				}
			}
			if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2 && (psDec.nChannelsAPI == 1 || psDec.nChannelsInternal == 1))
			{
				Arrays.MemSetShort(psDec.sStereo.pred_prev_Q13, 0, 2);
				Arrays.MemSetShort(psDec.sStereo.sSide, 0, 2);
				channel_state[1].resampler_state.Assign(channel_state[0].resampler_state);
			}
			psDec.nChannelsAPI = decControl.nChannelsAPI;
			psDec.nChannelsInternal = decControl.nChannelsInternal;
			if (decControl.API_sampleRate > 48000 || decControl.API_sampleRate < 8000)
			{
				return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY;
			}
			if (lostFlag != 1 && channel_state[0].nFramesDecoded == 0)
			{
				for (int i = 0; i < decControl.nChannelsInternal; i++)
				{
					for (int j = 0; j < channel_state[i].nFramesPerPacket; j++)
					{
						channel_state[i].VAD_flags[j] = psRangeDec.dec_bit_logp(frameData, 1u);
					}
					channel_state[i].LBRR_flag = psRangeDec.dec_bit_logp(frameData, 1u);
				}
				for (int i = 0; i < decControl.nChannelsInternal; i++)
				{
					Arrays.MemSetInt(channel_state[i].LBRR_flags, 0, 3);
					if (channel_state[i].LBRR_flag == 0)
					{
						continue;
					}
					if (channel_state[i].nFramesPerPacket == 1)
					{
						channel_state[i].LBRR_flags[0] = 1;
						continue;
					}
					int a = psRangeDec.dec_icdf(frameData, Tables.silk_LBRR_flags_iCDF_ptr[channel_state[i].nFramesPerPacket - 2], 8u) + 1;
					for (int j = 0; j < channel_state[i].nFramesPerPacket; j++)
					{
						channel_state[i].LBRR_flags[j] = Inlines.silk_RSHIFT(a, j) & 1;
					}
				}
				if (lostFlag == 0)
				{
					for (int j = 0; j < channel_state[0].nFramesPerPacket; j++)
					{
						for (int i = 0; i < decControl.nChannelsInternal; i++)
						{
							if (channel_state[i].LBRR_flags[j] == 0)
							{
								continue;
							}
							short[] pulses = new short[320];
							if (decControl.nChannelsInternal == 2 && i == 0)
							{
								Stereo.silk_stereo_decode_pred(psRangeDec, frameData, array2);
								if (channel_state[1].LBRR_flags[j] == 0)
								{
									BoxedValueInt boxedValueInt2 = new BoxedValueInt(num);
									Stereo.silk_stereo_decode_mid_only(psRangeDec, frameData, boxedValueInt2);
									num = boxedValueInt2.Val;
								}
							}
							DecodeIndices.silk_decode_indices(condCoding: (j > 0 && channel_state[i].LBRR_flags[j - 1] != 0) ? 2 : 0, psDec: channel_state[i], psRangeDec: psRangeDec, frameData: frameData, FrameIndex: j, decode_LBRR: 1);
							DecodePulses.silk_decode_pulses(psRangeDec, frameData, pulses, channel_state[i].indices.signalType, channel_state[i].indices.quantOffsetType, channel_state[i].frame_length);
						}
					}
				}
			}
			if (decControl.nChannelsInternal == 2)
			{
				if (lostFlag == 0 || (lostFlag == 2 && channel_state[0].LBRR_flags[channel_state[0].nFramesDecoded] == 1))
				{
					Stereo.silk_stereo_decode_pred(psRangeDec, frameData, array2);
					if ((lostFlag == 0 && channel_state[1].VAD_flags[channel_state[0].nFramesDecoded] == 0) || (lostFlag == 2 && channel_state[1].LBRR_flags[channel_state[0].nFramesDecoded] == 0))
					{
						BoxedValueInt boxedValueInt3 = new BoxedValueInt(num);
						Stereo.silk_stereo_decode_mid_only(psRangeDec, frameData, boxedValueInt3);
						num = boxedValueInt3.Val;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					for (int i = 0; i < 2; i++)
					{
						array2[i] = psDec.sStereo.pred_prev_Q13[i];
					}
				}
			}
			if (decControl.nChannelsInternal == 2 && num == 0 && psDec.prev_decode_only_middle == 1)
			{
				Arrays.MemSetShort(psDec.channel_state[1].outBuf, 0, 480);
				Arrays.MemSetInt(psDec.channel_state[1].sLPC_Q14_buf, 0, 16);
				psDec.channel_state[1].lagPrev = 100;
				psDec.channel_state[1].LastGainIndex = 10;
				psDec.channel_state[1].prevSignalType = 0;
				psDec.channel_state[1].first_frame_after_reset = 1;
			}
			int num5 = ((decControl.internalSampleRate * decControl.nChannelsInternal < decControl.API_sampleRate * decControl.nChannelsAPI) ? 1 : 0);
			Span<short> span;
			if (num5 != 0)
			{
				span = samplesOut;
				array[0] = samplesOut_ptr;
				array[1] = samplesOut_ptr + channel_state[0].frame_length + 2;
			}
			else
			{
				span = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)];
				array[0] = 0;
				array[1] = channel_state[0].frame_length + 2;
			}
			int num6 = ((lostFlag != 0) ? ((psDec.prev_decode_only_middle == 0 || (decControl.nChannelsInternal == 2 && lostFlag == 2 && channel_state[1].LBRR_flags[channel_state[1].nFramesDecoded] == 1)) ? 1 : 0) : ((num == 0) ? 1 : 0));
			for (int i = 0; i < decControl.nChannelsInternal; i++)
			{
				if (i == 0 || num6 != 0)
				{
					int num7 = channel_state[0].nFramesDecoded - i;
					int condCoding2 = ((num7 > 0) ? ((lostFlag == 2) ? ((channel_state[i].LBRR_flags[num7 - 1] != 0) ? 2 : 0) : ((i > 0 && psDec.prev_decode_only_middle != 0) ? 1 : 2)) : 0);
					num2 += channel_state[i].silk_decode_frame(psRangeDec, frameData, span, array[i] + 2, boxedValueInt, lostFlag, condCoding2);
				}
				else
				{
					Arrays.MemSetWithOffset<short>(span, 0, array[i] + 2, boxedValueInt.Val);
				}
				channel_state[i].nFramesDecoded++;
			}
			if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2)
			{
				Stereo.silk_stereo_MS_to_LR(psDec.sStereo, span, array[0], span, array[1], array2, channel_state[0].fs_kHz, boxedValueInt.Val);
			}
			else
			{
				psDec.sStereo.sMid.AsSpan(0, 2).CopyTo(span.Slice(array[0]));
				span.Slice(array[0] + boxedValueInt.Val, 2).CopyTo(psDec.sStereo.sMid);
			}
			nSamplesOut = Inlines.silk_DIV32(boxedValueInt.Val * decControl.API_sampleRate, Inlines.silk_SMULBB(channel_state[0].fs_kHz, 1000));
			Span<short> output;
			int num8;
			if (decControl.nChannelsAPI == 2)
			{
				output = new short[nSamplesOut];
				num8 = 0;
			}
			else
			{
				output = samplesOut;
				num8 = samplesOut_ptr;
			}
			if (num5 != 0)
			{
				short[] array3 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)];
				samplesOut.Slice(samplesOut_ptr, decControl.nChannelsInternal * (channel_state[0].frame_length + 2)).CopyTo(array3);
				span = array3;
				array[0] = 0;
				array[1] = channel_state[0].frame_length + 2;
			}
			for (int i = 0; i < Inlines.silk_min(decControl.nChannelsAPI, decControl.nChannelsInternal); i++)
			{
				num2 += Resampler.silk_resampler(channel_state[i].resampler_state, output, num8, span, array[i] + 1, boxedValueInt.Val);
				if (decControl.nChannelsAPI == 2)
				{
					int num9 = samplesOut_ptr + i;
					for (int j = 0; j < nSamplesOut; j++)
					{
						samplesOut[num9 + 2 * j] = output[num8 + j];
					}
				}
			}
			if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 1)
			{
				if (num3 != 0)
				{
					num2 += Resampler.silk_resampler(channel_state[1].resampler_state, output, num8, span, array[0] + 1, boxedValueInt.Val);
					for (int j = 0; j < nSamplesOut; j++)
					{
						samplesOut[samplesOut_ptr + 1 + 2 * j] = output[num8 + j];
					}
				}
				else
				{
					for (int j = 0; j < nSamplesOut; j++)
					{
						samplesOut[samplesOut_ptr + 1 + 2 * j] = samplesOut[samplesOut_ptr + 2 * j];
					}
				}
			}
			if (channel_state[0].prevSignalType == 2)
			{
				int[] array4 = new int[3] { 6, 4, 3 };
				decControl.prevPitchLag = channel_state[0].lagPrev * array4[channel_state[0].fs_kHz - 8 >> 2];
			}
			else
			{
				decControl.prevPitchLag = 0;
			}
			if (lostFlag == 1)
			{
				for (int j = 0; j < psDec.nChannelsInternal; j++)
				{
					psDec.channel_state[j].LastGainIndex = 10;
				}
			}
			else
			{
				psDec.prev_decode_only_middle = num;
			}
			return num2;
		}
	}
	internal static class DecodeCore
	{
		internal static void silk_decode_core(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, Span<short> xq, int xq_ptr, short[] pulses)
		{
			int num = 0;
			short[] lTPCoef_Q = psDecCtrl.LTPCoef_Q14;
			short[] array = new short[psDec.ltp_mem_length];
			int[] array2 = new int[psDec.ltp_mem_length + psDec.frame_length];
			int[] array3 = new int[psDec.subfr_length];
			int[] array4 = new int[psDec.subfr_length + 16];
			int num2 = Tables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType];
			int num3 = ((psDec.indices.NLSFInterpCoef_Q2 < 4) ? 1 : 0);
			int seed = psDec.indices.Seed;
			for (int i = 0; i < psDec.frame_length; i++)
			{
				seed = Inlines.silk_RAND(seed);
				psDec.exc_Q14[i] = Inlines.silk_LSHIFT(pulses[i], 14);
				if (psDec.exc_Q14[i] > 0)
				{
					psDec.exc_Q14[i] -= 1280;
				}
				else if (psDec.exc_Q14[i] < 0)
				{
					psDec.exc_Q14[i] += 1280;
				}
				psDec.exc_Q14[i] += num2 << 4;
				if (seed < 0)
				{
					psDec.exc_Q14[i] = -psDec.exc_Q14[i];
				}
				seed = Inlines.silk_ADD32_ovflw(seed, pulses[i]);
			}
			Arrays.MemCopy(psDec.sLPC_Q14_buf, 0, array4, 0, 16);
			int num4 = 0;
			int num5 = xq_ptr;
			int num6 = psDec.ltp_mem_length;
			for (int j = 0; j < psDec.nb_subfr; j++)
			{
				int[] array5 = array3;
				int num7 = 0;
				short[] array6 = psDecCtrl.PredCoef_Q12[j >> 1];
				int num8 = j * 5;
				int num9 = psDec.indices.signalType;
				int b = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[j], 6);
				int a = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[j], 47);
				int num10;
				if (psDecCtrl.Gains_Q16[j] != psDec.prev_gain_Q16)
				{
					num10 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[j], 16);
					for (int i = 0; i < 16; i++)
					{
						array4[i] = Inlines.silk_SMULWW(num10, array4[i]);
					}
				}
				else
				{
					num10 = 65536;
				}
				psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[j];
				if (psDec.lossCnt != 0 && psDec.prevSignalType == 2 && psDec.indices.signalType != 2 && j < 2)
				{
					Arrays.MemSetWithOffset(lTPCoef_Q, (short)0, num8, 5);
					lTPCoef_Q[num8 + 2] = 4096;
					num9 = 2;
					psDecCtrl.pitchL[j] = psDec.lagPrev;
				}
				if (num9 == 2)
				{
					num = psDecCtrl.pitchL[j];
					if (j == 0 || (j == 2 && num3 != 0))
					{
						int num11 = psDec.ltp_mem_length - num - psDec.LPC_order - 2;
						if (j == 2)
						{
							xq.Slice(xq_ptr, 2 * psDec.subfr_length).CopyTo(psDec.outBuf.AsSpan(psDec.ltp_mem_length));
						}
						Filters.silk_LPC_analysis_filter(array, num11, psDec.outBuf, num11 + j * psDec.subfr_length, array6, 0, psDec.ltp_mem_length - num11, psDec.LPC_order);
						if (j == 0)
						{
							a = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(a, psDecCtrl.LTP_scale_Q14), 2);
						}
						for (int i = 0; i < num + 2; i++)
						{
							array2[num6 - i - 1] = Inlines.silk_SMULWB(a, array[psDec.ltp_mem_length - i - 1]);
						}
					}
					else if (num10 != 65536)
					{
						for (int i = 0; i < num + 2; i++)
						{
							array2[num6 - i - 1] = Inlines.silk_SMULWW(num10, array2[num6 - i - 1]);
						}
					}
				}
				if (num9 == 2)
				{
					int num12 = num6 - num + 2;
					for (int i = 0; i < psDec.subfr_length; i++)
					{
						int a2 = 2;
						a2 = Inlines.silk_SMLAWB(a2, array2[num12], lTPCoef_Q[num8]);
						a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 1], lTPCoef_Q[num8 + 1]);
						a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 2], lTPCoef_Q[num8 + 2]);
						a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 3], lTPCoef_Q[num8 + 3]);
						a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 4], lTPCoef_Q[num8 + 4]);
						num12++;
						array5[num7 + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[num4 + i], a2, 1);
						array2[num6] = Inlines.silk_LSHIFT(array5[num7 + i], 1);
						num6++;
					}
				}
				else
				{
					array5 = psDec.exc_Q14;
					num7 = num4;
				}
				for (int i = 0; i < psDec.subfr_length; i++)
				{
					int a3 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 1], array6[0]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 2], array6[1]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 3], array6[2]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 4], array6[3]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 5], array6[4]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 6], array6[5]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 7], array6[6]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 8], array6[7]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 9], array6[8]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 10], array6[9]);
					if (psDec.LPC_order == 16)
					{
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 11], array6[10]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 12], array6[11]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 13], array6[12]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 14], array6[13]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 15], array6[14]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 16], array6[15]);
					}
					array4[16 + i] = Inlines.silk_ADD_LSHIFT32(array5[num7 + i], a3, 4);
					xq[num5 + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(array4[16 + i], b), 8));
				}
				Arrays.MemCopy(array4, psDec.subfr_length, array4, 0, 16);
				num4 += psDec.subfr_length;
				num5 += psDec.subfr_length;
			}
			Arrays.MemCopy(array4, 0, psDec.sLPC_Q14_buf, 0, 16);
		}
	}
	internal static class DecodeIndices
	{
		internal static void silk_decode_indices(SilkChannelDecoder psDec, EntropyCoder psRangeDec, ReadOnlySpan<byte> frameData, int FrameIndex, int decode_LBRR, int condCoding)
		{
			short[] array = new short[psDec.LPC_order];
			byte[] array2 = new byte[psDec.LPC_order];
			int num = ((decode_LBRR == 0 && psDec.VAD_flags[FrameIndex] == 0) ? psRangeDec.dec_icdf(frameData, Tables.silk_type_offset_no_VAD_iCDF, 8u) : (psRangeDec.dec_icdf(frameData, Tables.silk_type_offset_VAD_iCDF, 8u) + 2));
			psDec.indices.signalType = (sbyte)Inlines.silk_RSHIFT(num, 1);
			psDec.indices.quantOffsetType = (sbyte)(num & 1);
			if (condCoding == 2)
			{
				psDec.indices.GainsIndices[0] = (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_delta_gain_iCDF, 8u);
			}
			else
			{
				psDec.indices.GainsIndices[0] = (sbyte)Inlines.silk_LSHIFT(psRangeDec.dec_icdf(frameData, Tables.silk_gain_iCDF[psDec.indices.signalType], 8u), 3);
				psDec.indices.GainsIndices[0] += (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_uniform8_iCDF, 8u);
			}
			for (int i = 1; i < psDec.nb_subfr; i++)
			{
				psDec.indices.GainsIndices[i] = (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_delta_gain_iCDF, 8u);
			}
			psDec.indices.NLSFIndices[0] = (sbyte)psRangeDec.dec_icdf(frameData, psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8u);
			NLSF.silk_NLSF_unpack(array, array2, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]);
			for (int i = 0; i < psDec.psNLSF_CB.order; i++)
			{
				num = psRangeDec.dec_icdf(frameData, psDec.psNLSF_CB.ec_iCDF, array[i], 8u);
				switch (num)
				{
				case 0:
					num -= psRangeDec.dec_icdf(frameData, Tables.silk_NLSF_EXT_iCDF, 8u);
					break;
				case 8:
					num += psRangeDec.dec_icdf(frameData, Tables.silk_NLSF_EXT_iCDF, 8u);
					break;
				}
				psDec.indices.NLSFIndices[i + 1] = (sbyte)(num - 4);
			}
			if (psDec.nb_subfr == 4)
			{
				psDec.indices.NLSFInterpCoef_Q2 = (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_NLSF

Mods/LiteNetLib.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using LiteNetLib.Layers;
using LiteNetLib.Utils;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyMetadata("IsTrimmable", "True")]
[assembly: AssemblyCompany("Ruslan Pyrch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright 2024 Ruslan Pyrch")]
[assembly: AssemblyDescription("Lite reliable UDP library for .NET, Mono, and .NET Core")]
[assembly: AssemblyFileVersion("1.3.1")]
[assembly: AssemblyInformationalVersion("1.0.0+f57b9eafe72b924681a371d671af911478b6ab88")]
[assembly: AssemblyProduct("LiteNetLib")]
[assembly: AssemblyTitle("LiteNetLib")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/RevenantX/LiteNetLib")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.1.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsUnmanagedAttribute : Attribute
	{
	}
}
namespace LiteNetLib
{
	internal abstract class BaseChannel
	{
		protected readonly NetPeer Peer;

		protected readonly Queue<NetPacket> OutgoingQueue = new Queue<NetPacket>(64);

		private int _isAddedToPeerChannelSendQueue;

		public int PacketsInQueue => OutgoingQueue.Count;

		protected BaseChannel(NetPeer peer)
		{
			Peer = peer;
		}

		public void AddToQueue(NetPacket packet)
		{
			lock (OutgoingQueue)
			{
				OutgoingQueue.Enqueue(packet);
			}
			AddToPeerChannelSendQueue();
		}

		protected void AddToPeerChannelSendQueue()
		{
			if (Interlocked.CompareExchange(ref _isAddedToPeerChannelSendQueue, 1, 0) == 0)
			{
				Peer.AddToReliableChannelSendQueue(this);
			}
		}

		public bool SendAndCheckQueue()
		{
			bool num = SendNextPackets();
			if (!num)
			{
				Interlocked.Exchange(ref _isAddedToPeerChannelSendQueue, 0);
			}
			return num;
		}

		protected abstract bool SendNextPackets();

		public abstract bool ProcessPacket(NetPacket packet);
	}
	internal enum ConnectionRequestResult
	{
		None,
		Accept,
		Reject,
		RejectForce
	}
	public class ConnectionRequest
	{
		private readonly NetManager _listener;

		private int _used;

		internal NetConnectRequestPacket InternalPacket;

		public readonly IPEndPoint RemoteEndPoint;

		public NetDataReader Data => InternalPacket.Data;

		internal ConnectionRequestResult Result { get; private set; }

		internal void UpdateRequest(NetConnectRequestPacket connectRequest)
		{
			if (connectRequest.ConnectionTime >= InternalPacket.ConnectionTime && (connectRequest.ConnectionTime != InternalPacket.ConnectionTime || connectRequest.ConnectionNumber != InternalPacket.ConnectionNumber))
			{
				InternalPacket = connectRequest;
			}
		}

		private bool TryActivate()
		{
			return Interlocked.CompareExchange(ref _used, 1, 0) == 0;
		}

		internal ConnectionRequest(IPEndPoint remoteEndPoint, NetConnectRequestPacket requestPacket, NetManager listener)
		{
			InternalPacket = requestPacket;
			RemoteEndPoint = remoteEndPoint;
			_listener = listener;
		}

		public NetPeer AcceptIfKey(string key)
		{
			if (!TryActivate())
			{
				return null;
			}
			try
			{
				if (Data.GetString() == key)
				{
					Result = ConnectionRequestResult.Accept;
				}
			}
			catch
			{
				NetDebug.WriteError("[AC] Invalid incoming data");
			}
			if (Result == ConnectionRequestResult.Accept)
			{
				return _listener.OnConnectionSolved(this, null, 0, 0);
			}
			Result = ConnectionRequestResult.Reject;
			_listener.OnConnectionSolved(this, null, 0, 0);
			return null;
		}

		public NetPeer Accept()
		{
			if (!TryActivate())
			{
				return null;
			}
			Result = ConnectionRequestResult.Accept;
			return _listener.OnConnectionSolved(this, null, 0, 0);
		}

		public void Reject(byte[] rejectData, int start, int length, bool force)
		{
			if (TryActivate())
			{
				Result = (force ? ConnectionRequestResult.RejectForce : ConnectionRequestResult.Reject);
				_listener.OnConnectionSolved(this, rejectData, start, length);
			}
		}

		public void Reject(byte[] rejectData, int start, int length)
		{
			Reject(rejectData, start, length, force: false);
		}

		public void RejectForce(byte[] rejectData, int start, int length)
		{
			Reject(rejectData, start, length, force: true);
		}

		public void RejectForce()
		{
			Reject(null, 0, 0, force: true);
		}

		public void RejectForce(byte[] rejectData)
		{
			Reject(rejectData, 0, rejectData.Length, force: true);
		}

		public void RejectForce(NetDataWriter rejectData)
		{
			Reject(rejectData.Data, 0, rejectData.Length, force: true);
		}

		public void Reject()
		{
			Reject(null, 0, 0, force: false);
		}

		public void Reject(byte[] rejectData)
		{
			Reject(rejectData, 0, rejectData.Length, force: false);
		}

		public void Reject(NetDataWriter rejectData)
		{
			Reject(rejectData.Data, 0, rejectData.Length, force: false);
		}
	}
	public enum UnconnectedMessageType
	{
		BasicMessage,
		Broadcast
	}
	public enum DisconnectReason
	{
		ConnectionFailed,
		Timeout,
		HostUnreachable,
		NetworkUnreachable,
		RemoteConnectionClose,
		DisconnectPeerCalled,
		ConnectionRejected,
		InvalidProtocol,
		UnknownHost,
		Reconnect,
		PeerToPeerConnection,
		PeerNotFound
	}
	public struct DisconnectInfo
	{
		public DisconnectReason Reason;

		public SocketError SocketErrorCode;

		public NetPacketReader AdditionalData;
	}
	public interface INetEventListener
	{
		void OnPeerConnected(NetPeer peer);

		void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);

		void OnNetworkError(IPEndPoint endPoint, SocketError socketError);

		void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod);

		void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType);

		void OnNetworkLatencyUpdate(NetPeer peer, int latency);

		void OnConnectionRequest(ConnectionRequest request);
	}
	public interface IDeliveryEventListener
	{
		void OnMessageDelivered(NetPeer peer, object userData);
	}
	public interface INtpEventListener
	{
		void OnNtpResponse(NtpPacket packet);
	}
	public interface IPeerAddressChangedListener
	{
		void OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress);
	}
	public class EventBasedNetListener : INetEventListener, IDeliveryEventListener, INtpEventListener, IPeerAddressChangedListener
	{
		public delegate void OnPeerConnected(NetPeer peer);

		public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);

		public delegate void OnNetworkError(IPEndPoint endPoint, SocketError socketError);

		public delegate void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod);

		public delegate void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType);

		public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency);

		public delegate void OnConnectionRequest(ConnectionRequest request);

		public delegate void OnDeliveryEvent(NetPeer peer, object userData);

		public delegate void OnNtpResponseEvent(NtpPacket packet);

		public delegate void OnPeerAddressChangedEvent(NetPeer peer, IPEndPoint previousAddress);

		public event OnPeerConnected PeerConnectedEvent;

		public event OnPeerDisconnected PeerDisconnectedEvent;

		public event OnNetworkError NetworkErrorEvent;

		public event OnNetworkReceive NetworkReceiveEvent;

		public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent;

		public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent;

		public event OnConnectionRequest ConnectionRequestEvent;

		public event OnDeliveryEvent DeliveryEvent;

		public event OnNtpResponseEvent NtpResponseEvent;

		public event OnPeerAddressChangedEvent PeerAddressChangedEvent;

		public void ClearPeerConnectedEvent()
		{
			this.PeerConnectedEvent = null;
		}

		public void ClearPeerDisconnectedEvent()
		{
			this.PeerDisconnectedEvent = null;
		}

		public void ClearNetworkErrorEvent()
		{
			this.NetworkErrorEvent = null;
		}

		public void ClearNetworkReceiveEvent()
		{
			this.NetworkReceiveEvent = null;
		}

		public void ClearNetworkReceiveUnconnectedEvent()
		{
			this.NetworkReceiveUnconnectedEvent = null;
		}

		public void ClearNetworkLatencyUpdateEvent()
		{
			this.NetworkLatencyUpdateEvent = null;
		}

		public void ClearConnectionRequestEvent()
		{
			this.ConnectionRequestEvent = null;
		}

		public void ClearDeliveryEvent()
		{
			this.DeliveryEvent = null;
		}

		public void ClearNtpResponseEvent()
		{
			this.NtpResponseEvent = null;
		}

		public void ClearPeerAddressChangedEvent()
		{
			this.PeerAddressChangedEvent = null;
		}

		void INetEventListener.OnPeerConnected(NetPeer peer)
		{
			if (this.PeerConnectedEvent != null)
			{
				this.PeerConnectedEvent(peer);
			}
		}

		void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
		{
			if (this.PeerDisconnectedEvent != null)
			{
				this.PeerDisconnectedEvent(peer, disconnectInfo);
			}
		}

		void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode)
		{
			if (this.NetworkErrorEvent != null)
			{
				this.NetworkErrorEvent(endPoint, socketErrorCode);
			}
		}

		void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod)
		{
			if (this.NetworkReceiveEvent != null)
			{
				this.NetworkReceiveEvent(peer, reader, channelNumber, deliveryMethod);
			}
		}

		void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType)
		{
			if (this.NetworkReceiveUnconnectedEvent != null)
			{
				this.NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType);
			}
		}

		void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency)
		{
			if (this.NetworkLatencyUpdateEvent != null)
			{
				this.NetworkLatencyUpdateEvent(peer, latency);
			}
		}

		void INetEventListener.OnConnectionRequest(ConnectionRequest request)
		{
			if (this.ConnectionRequestEvent != null)
			{
				this.ConnectionRequestEvent(request);
			}
		}

		void IDeliveryEventListener.OnMessageDelivered(NetPeer peer, object userData)
		{
			if (this.DeliveryEvent != null)
			{
				this.DeliveryEvent(peer, userData);
			}
		}

		void INtpEventListener.OnNtpResponse(NtpPacket packet)
		{
			if (this.NtpResponseEvent != null)
			{
				this.NtpResponseEvent(packet);
			}
		}

		void IPeerAddressChangedListener.OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress)
		{
			if (this.PeerAddressChangedEvent != null)
			{
				this.PeerAddressChangedEvent(peer, previousAddress);
			}
		}
	}
	internal sealed class NetConnectRequestPacket
	{
		public const int HeaderSize = 18;

		public readonly long ConnectionTime;

		public byte ConnectionNumber;

		public readonly byte[] TargetAddress;

		public readonly NetDataReader Data;

		public readonly int PeerId;

		private NetConnectRequestPacket(long connectionTime, byte connectionNumber, int localId, byte[] targetAddress, NetDataReader data)
		{
			ConnectionTime = connectionTime;
			ConnectionNumber = connectionNumber;
			TargetAddress = targetAddress;
			Data = data;
			PeerId = localId;
		}

		public static int GetProtocolId(NetPacket packet)
		{
			return BitConverter.ToInt32(packet.RawData, 1);
		}

		public static NetConnectRequestPacket FromData(NetPacket packet)
		{
			if (packet.ConnectionNumber >= 4)
			{
				return null;
			}
			long connectionTime = BitConverter.ToInt64(packet.RawData, 5);
			int localId = BitConverter.ToInt32(packet.RawData, 13);
			int num = packet.RawData[17];
			if (num != 16 && num != 28)
			{
				return null;
			}
			byte[] array = new byte[num];
			Buffer.BlockCopy(packet.RawData, 18, array, 0, num);
			NetDataReader netDataReader = new NetDataReader(null, 0, 0);
			if (packet.Size > 18 + num)
			{
				netDataReader.SetSource(packet.RawData, 18 + num, packet.Size);
			}
			return new NetConnectRequestPacket(connectionTime, packet.ConnectionNumber, localId, array, netDataReader);
		}

		public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectTime, int localId)
		{
			NetPacket netPacket = new NetPacket(PacketProperty.ConnectRequest, connectData.Length + addressBytes.Size);
			FastBitConverter.GetBytes(netPacket.RawData, 1, 13);
			FastBitConverter.GetBytes(netPacket.RawData, 5, connectTime);
			FastBitConverter.GetBytes(netPacket.RawData, 13, localId);
			netPacket.RawData[17] = (byte)addressBytes.Size;
			for (int i = 0; i < addressBytes.Size; i++)
			{
				netPacket.RawData[18 + i] = addressBytes[i];
			}
			Buffer.BlockCopy(connectData.Data, 0, netPacket.RawData, 18 + addressBytes.Size, connectData.Length);
			return netPacket;
		}
	}
	internal sealed class NetConnectAcceptPacket
	{
		public const int Size = 15;

		public readonly long ConnectionTime;

		public readonly byte ConnectionNumber;

		public readonly int PeerId;

		public readonly bool PeerNetworkChanged;

		private NetConnectAcceptPacket(long connectionTime, byte connectionNumber, int peerId, bool peerNetworkChanged)
		{
			ConnectionTime = connectionTime;
			ConnectionNumber = connectionNumber;
			PeerId = peerId;
			PeerNetworkChanged = peerNetworkChanged;
		}

		public static NetConnectAcceptPacket FromData(NetPacket packet)
		{
			if (packet.Size != 15)
			{
				return null;
			}
			long connectionTime = BitConverter.ToInt64(packet.RawData, 1);
			byte b = packet.RawData[9];
			if (b >= 4)
			{
				return null;
			}
			byte b2 = packet.RawData[10];
			if (b2 > 1)
			{
				return null;
			}
			int num = BitConverter.ToInt32(packet.RawData, 11);
			if (num < 0)
			{
				return null;
			}
			return new NetConnectAcceptPacket(connectionTime, b, num, b2 == 1);
		}

		public static NetPacket Make(long connectTime, byte connectNum, int localPeerId)
		{
			NetPacket netPacket = new NetPacket(PacketProperty.ConnectAccept, 0);
			FastBitConverter.GetBytes(netPacket.RawData, 1, connectTime);
			netPacket.RawData[9] = connectNum;
			FastBitConverter.GetBytes(netPacket.RawData, 11, localPeerId);
			return netPacket;
		}

		public static NetPacket MakeNetworkChanged(NetPeer peer)
		{
			NetPacket netPacket = new NetPacket(PacketProperty.PeerNotFound, 14);
			FastBitConverter.GetBytes(netPacket.RawData, 1, peer.ConnectTime);
			netPacket.RawData[9] = peer.ConnectionNum;
			netPacket.RawData[10] = 1;
			FastBitConverter.GetBytes(netPacket.RawData, 11, peer.RemoteId);
			return netPacket;
		}
	}
	internal static class NativeSocket
	{
		private static class WinSock
		{
			private const string LibName = "ws2_32.dll";

			[DllImport("ws2_32.dll", SetLastError = true)]
			public static extern int recvfrom(IntPtr socketHandle, [In][Out] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [Out] byte[] socketAddress, [In][Out] ref int socketAddressSize);

			[DllImport("ws2_32.dll", SetLastError = true)]
			internal unsafe static extern int sendto(IntPtr socketHandle, byte* pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize);
		}

		private static class UnixSock
		{
			private const string LibName = "libc";

			[DllImport("libc", SetLastError = true)]
			public static extern int recvfrom(IntPtr socketHandle, [In][Out] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [Out] byte[] socketAddress, [In][Out] ref int socketAddressSize);

			[DllImport("libc", SetLastError = true)]
			internal unsafe static extern int sendto(IntPtr socketHandle, byte* pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize);
		}

		public static readonly bool IsSupported;

		public static readonly bool UnixMode;

		public const int IPv4AddrSize = 16;

		public const int IPv6AddrSize = 28;

		public const int AF_INET = 2;

		public const int AF_INET6 = 10;

		private static readonly Dictionary<int, SocketError> NativeErrorToSocketError;

		static NativeSocket()
		{
			IsSupported = false;
			UnixMode = false;
			NativeErrorToSocketError = new Dictionary<int, SocketError>
			{
				{
					13,
					SocketError.AccessDenied
				},
				{
					98,
					SocketError.AddressAlreadyInUse
				},
				{
					99,
					SocketError.AddressNotAvailable
				},
				{
					97,
					SocketError.AddressFamilyNotSupported
				},
				{
					11,
					SocketError.WouldBlock
				},
				{
					114,
					SocketError.AlreadyInProgress
				},
				{
					9,
					SocketError.OperationAborted
				},
				{
					125,
					SocketError.OperationAborted
				},
				{
					103,
					SocketError.ConnectionAborted
				},
				{
					111,
					SocketError.ConnectionRefused
				},
				{
					104,
					SocketError.ConnectionReset
				},
				{
					89,
					SocketError.DestinationAddressRequired
				},
				{
					14,
					SocketError.Fault
				},
				{
					112,
					SocketError.HostDown
				},
				{
					6,
					SocketError.HostNotFound
				},
				{
					113,
					SocketError.HostUnreachable
				},
				{
					115,
					SocketError.InProgress
				},
				{
					4,
					SocketError.Interrupted
				},
				{
					22,
					SocketError.InvalidArgument
				},
				{
					106,
					SocketError.IsConnected
				},
				{
					24,
					SocketError.TooManyOpenSockets
				},
				{
					90,
					SocketError.MessageSize
				},
				{
					100,
					SocketError.NetworkDown
				},
				{
					102,
					SocketError.NetworkReset
				},
				{
					101,
					SocketError.NetworkUnreachable
				},
				{
					23,
					SocketError.TooManyOpenSockets
				},
				{
					105,
					SocketError.NoBufferSpaceAvailable
				},
				{
					61,
					SocketError.NoData
				},
				{
					2,
					SocketError.AddressNotAvailable
				},
				{
					92,
					SocketError.ProtocolOption
				},
				{
					107,
					SocketError.NotConnected
				},
				{
					88,
					SocketError.NotSocket
				},
				{
					3440,
					SocketError.OperationNotSupported
				},
				{
					1,
					SocketError.AccessDenied
				},
				{
					32,
					SocketError.Shutdown
				},
				{
					96,
					SocketError.ProtocolFamilyNotSupported
				},
				{
					93,
					SocketError.ProtocolNotSupported
				},
				{
					91,
					SocketError.ProtocolType
				},
				{
					94,
					SocketError.SocketNotSupported
				},
				{
					108,
					SocketError.Disconnecting
				},
				{
					110,
					SocketError.TimedOut
				},
				{
					0,
					SocketError.Success
				}
			};
			if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
			{
				IsSupported = true;
				UnixMode = true;
			}
			else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
			{
				IsSupported = true;
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int RecvFrom(IntPtr socketHandle, byte[] pinnedBuffer, int len, byte[] socketAddress, ref int socketAddressSize)
		{
			if (!UnixMode)
			{
				return WinSock.recvfrom(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, ref socketAddressSize);
			}
			return UnixSock.recvfrom(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, ref socketAddressSize);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public unsafe static int SendTo(IntPtr socketHandle, byte* pinnedBuffer, int len, byte[] socketAddress, int socketAddressSize)
		{
			if (!UnixMode)
			{
				return WinSock.sendto(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, socketAddressSize);
			}
			return UnixSock.sendto(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, socketAddressSize);
		}

		public static SocketError GetSocketError()
		{
			int lastWin32Error = Marshal.GetLastWin32Error();
			if (UnixMode)
			{
				if (!NativeErrorToSocketError.TryGetValue(lastWin32Error, out var value))
				{
					return SocketError.SocketError;
				}
				return value;
			}
			return (SocketError)lastWin32Error;
		}

		public static SocketException GetSocketException()
		{
			int lastWin32Error = Marshal.GetLastWin32Error();
			if (UnixMode)
			{
				if (!NativeErrorToSocketError.TryGetValue(lastWin32Error, out var value))
				{
					return new SocketException(-1);
				}
				return new SocketException((int)value);
			}
			return new SocketException(lastWin32Error);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static short GetNativeAddressFamily(IPEndPoint remoteEndPoint)
		{
			if (!UnixMode)
			{
				return (short)remoteEndPoint.AddressFamily;
			}
			return (short)((remoteEndPoint.AddressFamily == AddressFamily.InterNetwork) ? 2 : 10);
		}
	}
	public enum NatAddressType
	{
		Internal,
		External
	}
	public interface INatPunchListener
	{
		void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token);

		void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token);
	}
	public class EventBasedNatPunchListener : INatPunchListener
	{
		public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token);

		public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token);

		public event OnNatIntroductionRequest NatIntroductionRequest;

		public event OnNatIntroductionSuccess NatIntroductionSuccess;

		void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token)
		{
			if (this.NatIntroductionRequest != null)
			{
				this.NatIntroductionRequest(localEndPoint, remoteEndPoint, token);
			}
		}

		void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token)
		{
			if (this.NatIntroductionSuccess != null)
			{
				this.NatIntroductionSuccess(targetEndPoint, type, token);
			}
		}
	}
	public sealed class NatPunchModule
	{
		private struct RequestEventData
		{
			public IPEndPoint LocalEndPoint;

			public IPEndPoint RemoteEndPoint;

			public string Token;
		}

		private struct SuccessEventData
		{
			public IPEndPoint TargetEndPoint;

			public NatAddressType Type;

			public string Token;
		}

		private class NatIntroduceRequestPacket
		{
			public IPEndPoint Internal
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}

			public string Token
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}
		}

		private class NatIntroduceResponsePacket
		{
			public IPEndPoint Internal
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}

			public IPEndPoint External
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}

			public string Token
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}
		}

		private class NatPunchPacket
		{
			public string Token
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}

			public bool IsExternal
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}
		}

		private readonly NetManager _socket;

		private readonly ConcurrentQueue<RequestEventData> _requestEvents = new ConcurrentQueue<RequestEventData>();

		private readonly ConcurrentQueue<SuccessEventData> _successEvents = new ConcurrentQueue<SuccessEventData>();

		private readonly NetDataReader _cacheReader = new NetDataReader();

		private readonly NetDataWriter _cacheWriter = new NetDataWriter();

		private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(256);

		private INatPunchListener _natPunchListener;

		public const int MaxTokenLength = 256;

		public bool UnsyncedEvents;

		internal NatPunchModule(NetManager socket)
		{
			_socket = socket;
			_netPacketProcessor.SubscribeReusable<NatIntroduceResponsePacket>(OnNatIntroductionResponse);
			_netPacketProcessor.SubscribeReusable<NatIntroduceRequestPacket, IPEndPoint>(OnNatIntroductionRequest);
			_netPacketProcessor.SubscribeReusable<NatPunchPacket, IPEndPoint>(OnNatPunch);
		}

		internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet)
		{
			lock (_cacheReader)
			{
				_cacheReader.SetSource(packet.RawData, 1, packet.Size);
				_netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint);
			}
		}

		public void Init(INatPunchListener listener)
		{
			_natPunchListener = listener;
		}

		private void Send<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>(T packet, IPEndPoint target) where T : class, new()
		{
			_cacheWriter.Reset();
			_cacheWriter.Put((byte)16);
			_netPacketProcessor.Write(_cacheWriter, packet);
			_socket.SendRaw(_cacheWriter.Data, 0, _cacheWriter.Length, target);
		}

		public void NatIntroduce(IPEndPoint hostInternal, IPEndPoint hostExternal, IPEndPoint clientInternal, IPEndPoint clientExternal, string additionalInfo)
		{
			NatIntroduceResponsePacket natIntroduceResponsePacket = new NatIntroduceResponsePacket
			{
				Token = additionalInfo
			};
			natIntroduceResponsePacket.Internal = hostInternal;
			natIntroduceResponsePacket.External = hostExternal;
			Send(natIntroduceResponsePacket, clientExternal);
			natIntroduceResponsePacket.Internal = clientInternal;
			natIntroduceResponsePacket.External = clientExternal;
			Send(natIntroduceResponsePacket, hostExternal);
		}

		public void PollEvents()
		{
			if (!UnsyncedEvents && _natPunchListener != null && (!_successEvents.IsEmpty || !_requestEvents.IsEmpty))
			{
				SuccessEventData result;
				while (_successEvents.TryDequeue(out result))
				{
					_natPunchListener.OnNatIntroductionSuccess(result.TargetEndPoint, result.Type, result.Token);
				}
				RequestEventData result2;
				while (_requestEvents.TryDequeue(out result2))
				{
					_natPunchListener.OnNatIntroductionRequest(result2.LocalEndPoint, result2.RemoteEndPoint, result2.Token);
				}
			}
		}

		public void SendNatIntroduceRequest(string host, int port, string additionalInfo)
		{
			SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo);
		}

		public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo)
		{
			string localIp = NetUtils.GetLocalIp(LocalAddrType.IPv4);
			if (string.IsNullOrEmpty(localIp) || masterServerEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
			{
				localIp = NetUtils.GetLocalIp(LocalAddrType.IPv6);
			}
			Send(new NatIntroduceRequestPacket
			{
				Internal = NetUtils.MakeEndPoint(localIp, _socket.LocalPort),
				Token = additionalInfo
			}, masterServerEndPoint);
		}

		private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint)
		{
			if (UnsyncedEvents)
			{
				_natPunchListener.OnNatIntroductionRequest(req.Internal, senderEndPoint, req.Token);
				return;
			}
			_requestEvents.Enqueue(new RequestEventData
			{
				LocalEndPoint = req.Internal,
				RemoteEndPoint = senderEndPoint,
				Token = req.Token
			});
		}

		private void OnNatIntroductionResponse(NatIntroduceResponsePacket req)
		{
			NatPunchPacket natPunchPacket = new NatPunchPacket
			{
				Token = req.Token
			};
			Send(natPunchPacket, req.Internal);
			_socket.Ttl = 2;
			_socket.SendRaw(new byte[1] { 17 }, 0, 1, req.External);
			_socket.Ttl = 255;
			natPunchPacket.IsExternal = true;
			Send(natPunchPacket, req.External);
		}

		private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint)
		{
			if (UnsyncedEvents)
			{
				_natPunchListener.OnNatIntroductionSuccess(senderEndPoint, req.IsExternal ? NatAddressType.External : NatAddressType.Internal, req.Token);
				return;
			}
			_successEvents.Enqueue(new SuccessEventData
			{
				TargetEndPoint = senderEndPoint,
				Type = (req.IsExternal ? NatAddressType.External : NatAddressType.Internal),
				Token = req.Token
			});
		}
	}
	public enum DeliveryMethod : byte
	{
		Unreliable = 4,
		ReliableUnordered = 0,
		Sequenced = 1,
		ReliableOrdered = 2,
		ReliableSequenced = 3
	}
	public static class NetConstants
	{
		public const int DefaultWindowSize = 64;

		public const int SocketBufferSize = 1048576;

		public const int SocketTTL = 255;

		public const int HeaderSize = 1;

		public const int ChanneledHeaderSize = 4;

		public const int FragmentHeaderSize = 6;

		public const int FragmentedHeaderTotalSize = 10;

		public const ushort MaxSequence = 32768;

		public const ushort HalfMaxSequence = 16384;

		internal const int ProtocolId = 13;

		internal const int MaxUdpHeaderSize = 68;

		internal const int ChannelTypeCount = 4;

		internal static readonly int[] PossibleMtu = new int[6] { 1024, 1164, 1392, 1404, 1424, 1432 };

		public static readonly int InitialMtu = PossibleMtu[0];

		public static readonly int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1];

		public static readonly int MaxUnreliableDataSize = MaxPacketSize - 1;

		public const byte MaxConnectionNumber = 4;
	}
	public class InvalidPacketException : ArgumentException
	{
		public InvalidPacketException(string message)
			: base(message)
		{
		}
	}
	public class TooBigPacketException : InvalidPacketException
	{
		public TooBigPacketException(string message)
			: base(message)
		{
		}
	}
	public enum NetLogLevel
	{
		Warning,
		Error,
		Trace,
		Info
	}
	public interface INetLogger
	{
		void WriteNet(NetLogLevel level, string str, params object[] args);
	}
	public static class NetDebug
	{
		public static INetLogger Logger = null;

		private static readonly object DebugLogLock = new object();

		private static void WriteLogic(NetLogLevel logLevel, string str, params object[] args)
		{
			lock (DebugLogLock)
			{
				if (Logger == null)
				{
					Console.WriteLine(str, args);
				}
				else
				{
					Logger.WriteNet(logLevel, str, args);
				}
			}
		}

		[Conditional("DEBUG_MESSAGES")]
		internal static void Write(string str)
		{
			WriteLogic(NetLogLevel.Trace, str);
		}

		[Conditional("DEBUG_MESSAGES")]
		internal static void Write(NetLogLevel level, string str)
		{
			WriteLogic(level, str);
		}

		[Conditional("DEBUG_MESSAGES")]
		[Conditional("DEBUG")]
		internal static void WriteForce(string str)
		{
			WriteLogic(NetLogLevel.Trace, str);
		}

		[Conditional("DEBUG_MESSAGES")]
		[Conditional("DEBUG")]
		internal static void WriteForce(NetLogLevel level, string str)
		{
			WriteLogic(level, str);
		}

		internal static void WriteError(string str)
		{
			WriteLogic(NetLogLevel.Error, str);
		}
	}
	public sealed class NetPacketReader : NetDataReader
	{
		private NetPacket _packet;

		private readonly NetManager _manager;

		private readonly NetEvent _evt;

		internal NetPacketReader(NetManager manager, NetEvent evt)
		{
			_manager = manager;
			_evt = evt;
		}

		internal void SetSource(NetPacket packet, int headerSize)
		{
			if (packet != null)
			{
				_packet = packet;
				SetSource(packet.RawData, headerSize, packet.Size);
			}
		}

		internal void RecycleInternal()
		{
			Clear();
			if (_packet != null)
			{
				_manager.PoolRecycle(_packet);
			}
			_packet = null;
			_manager.RecycleEvent(_evt);
		}

		public void Recycle()
		{
			if (!_manager.AutoRecycle)
			{
				RecycleInternal();
			}
		}
	}
	internal sealed class NetEvent
	{
		public enum EType
		{
			Connect,
			Disconnect,
			Receive,
			ReceiveUnconnected,
			Error,
			ConnectionLatencyUpdated,
			Broadcast,
			ConnectionRequest,
			MessageDelivered,
			PeerAddressChanged
		}

		public NetEvent Next;

		public EType Type;

		public NetPeer Peer;

		public IPEndPoint RemoteEndPoint;

		public object UserData;

		public int Latency;

		public SocketError ErrorCode;

		public DisconnectReason DisconnectReason;

		public ConnectionRequest ConnectionRequest;

		public DeliveryMethod DeliveryMethod;

		public byte ChannelNumber;

		public readonly NetPacketReader DataReader;

		public NetEvent(NetManager manager)
		{
			DataReader = new NetPacketReader(manager, this);
		}
	}
	public class NetManager : IEnumerable<NetPeer>, IEnumerable
	{
		public struct NetPeerEnumerator : IEnumerator<NetPeer>, IEnumerator, IDisposable
		{
			private readonly NetPeer _initialPeer;

			private NetPeer _p;

			public NetPeer Current => _p;

			object IEnumerator.Current => _p;

			public NetPeerEnumerator(NetPeer p)
			{
				_initialPeer = p;
				_p = null;
			}

			public void Dispose()
			{
			}

			public bool MoveNext()
			{
				_p = ((_p == null) ? _initialPeer : _p.NextPeer);
				return _p != null;
			}

			public void Reset()
			{
				throw new NotSupportedException();
			}
		}

		private struct IncomingData
		{
			public NetPacket Data;

			public IPEndPoint EndPoint;

			public DateTime TimeWhenGet;
		}

		private struct Slot
		{
			internal int HashCode;

			internal int Next;

			internal NetPeer Value;
		}

		private readonly List<IncomingData> _pingSimulationList = new List<IncomingData>();

		private readonly Random _randomGenerator = new Random();

		private const int MinLatencyThreshold = 5;

		private Thread _logicThread;

		private bool _manualMode;

		private readonly AutoResetEvent _updateTriggerEvent = new AutoResetEvent(initialState: true);

		private NetEvent _pendingEventHead;

		private NetEvent _pendingEventTail;

		private NetEvent _netEventPoolHead;

		private readonly INetEventListener _netEventListener;

		private readonly IDeliveryEventListener _deliveryEventListener;

		private readonly INtpEventListener _ntpEventListener;

		private readonly IPeerAddressChangedListener _peerAddressChangedListener;

		private readonly Dictionary<IPEndPoint, ConnectionRequest> _requestsDict = new Dictionary<IPEndPoint, ConnectionRequest>();

		private readonly ConcurrentDictionary<IPEndPoint, NtpRequest> _ntpRequests = new ConcurrentDictionary<IPEndPoint, NtpRequest>();

		private long _connectedPeersCount;

		private readonly List<NetPeer> _connectedPeerListCache = new List<NetPeer>();

		private readonly PacketLayerBase _extraPacketLayer;

		private int _lastPeerId;

		private ConcurrentQueue<int> _peerIds = new ConcurrentQueue<int>();

		private byte _channelsCount = 1;

		private readonly object _eventLock = new object();

		private bool _dropPacket;

		public bool UnconnectedMessagesEnabled;

		public bool NatPunchEnabled;

		public int UpdateTime = 15;

		public int PingInterval = 1000;

		public int DisconnectTimeout = 5000;

		public bool SimulatePacketLoss;

		public bool SimulateLatency;

		public int SimulationPacketLossChance = 10;

		public int SimulationMinLatency = 30;

		public int SimulationMaxLatency = 100;

		public bool UnsyncedEvents;

		public bool UnsyncedReceiveEvent;

		public bool UnsyncedDeliveryEvent;

		public bool BroadcastReceiveEnabled;

		public int ReconnectDelay = 500;

		public int MaxConnectAttempts = 10;

		public bool ReuseAddress;

		public bool DontRoute;

		public readonly NetStatistics Statistics = new NetStatistics();

		public bool EnableStatistics;

		public readonly NatPunchModule NatPunchModule;

		public bool AutoRecycle;

		public bool IPv6Enabled = true;

		public int MtuOverride;

		public bool MtuDiscovery;

		public bool UseNativeSockets;

		public bool DisconnectOnUnreachable;

		public bool AllowPeerAddressChange;

		private const int MaxPrimeArrayLength = 2147483587;

		private const int HashPrime = 101;

		private const int Lower31BitMask = int.MaxValue;

		private static readonly int[] Primes;

		private int[] _buckets;

		private Slot[] _slots;

		private int _count;

		private int _lastIndex;

		private int _freeList = -1;

		private NetPeer[] _peersArray = new NetPeer[32];

		private readonly ReaderWriterLockSlim _peersLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

		private volatile NetPeer _headPeer;

		private NetPacket _poolHead;

		private int _poolCount;

		private readonly object _poolLock = new object();

		public int PacketPoolSize = 1000;

		private const int ReceivePollingTime = 500000;

		private Socket _udpSocketv4;

		private Socket _udpSocketv6;

		private Thread _receiveThread;

		private IPEndPoint _bufferEndPointv4;

		private IPEndPoint _bufferEndPointv6;

		private const int SioUdpConnreset = -1744830452;

		private static readonly IPAddress MulticastAddressV6;

		public static readonly bool IPv6Support;

		internal bool NotConnected;

		public bool IsRunning { get; private set; }

		public int LocalPort { get; private set; }

		public NetPeer FirstPeer => _headPeer;

		public byte ChannelsCount
		{
			get
			{
				return _channelsCount;
			}
			set
			{
				if (value < 1 || value > 64)
				{
					throw new ArgumentException("Channels count must be between 1 and 64");
				}
				_channelsCount = value;
			}
		}

		public List<NetPeer> ConnectedPeerList
		{
			get
			{
				GetPeersNonAlloc(_connectedPeerListCache, ConnectionState.Connected);
				return _connectedPeerListCache;
			}
		}

		public int ConnectedPeersCount => (int)Interlocked.Read(ref _connectedPeersCount);

		public int ExtraPacketSizeForLayer => _extraPacketLayer?.ExtraPacketSizeForLayer ?? 0;

		public int PoolCount => _poolCount;

		public short Ttl
		{
			get
			{
				return _udpSocketv4.Ttl;
			}
			internal set
			{
				_udpSocketv4.Ttl = value;
			}
		}

		public NetManager(INetEventListener listener, PacketLayerBase extraPacketLayer = null)
		{
			_netEventListener = listener;
			_deliveryEventListener = listener as IDeliveryEventListener;
			_ntpEventListener = listener as INtpEventListener;
			_peerAddressChangedListener = listener as IPeerAddressChangedListener;
			NatPunchModule = new NatPunchModule(this);
			_extraPacketLayer = extraPacketLayer;
		}

		internal void ConnectionLatencyUpdated(NetPeer fromPeer, int latency)
		{
			CreateEvent(NetEvent.EType.ConnectionLatencyUpdated, fromPeer, null, SocketError.Success, latency, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
		}

		internal void MessageDelivered(NetPeer fromPeer, object userData)
		{
			if (_deliveryEventListener != null)
			{
				CreateEvent(NetEvent.EType.MessageDelivered, fromPeer, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, null, userData);
			}
		}

		internal void DisconnectPeerForce(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, NetPacket eventData)
		{
			DisconnectPeer(peer, reason, socketErrorCode, force: true, null, 0, 0, eventData);
		}

		private void DisconnectPeer(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, bool force, byte[] data, int start, int count, NetPacket eventData)
		{
			switch (peer.Shutdown(data, start, count, force))
			{
			case ShutdownResult.None:
				return;
			case ShutdownResult.WasConnected:
				Interlocked.Decrement(ref _connectedPeersCount);
				break;
			}
			CreateEvent(NetEvent.EType.Disconnect, peer, null, socketErrorCode, 0, reason, null, DeliveryMethod.Unreliable, 0, eventData);
		}

		private void CreateEvent(NetEvent.EType type, NetPeer peer = null, IPEndPoint remoteEndPoint = null, SocketError errorCode = SocketError.Success, int latency = 0, DisconnectReason disconnectReason = DisconnectReason.ConnectionFailed, ConnectionRequest connectionRequest = null, DeliveryMethod deliveryMethod = DeliveryMethod.Unreliable, byte channelNumber = 0, NetPacket readerSource = null, object userData = null)
		{
			bool flag = UnsyncedEvents;
			switch (type)
			{
			case NetEvent.EType.Connect:
				Interlocked.Increment(ref _connectedPeersCount);
				break;
			case NetEvent.EType.MessageDelivered:
				flag = UnsyncedDeliveryEvent;
				break;
			}
			NetEvent netEvent;
			lock (_eventLock)
			{
				netEvent = _netEventPoolHead;
				if (netEvent == null)
				{
					netEvent = new NetEvent(this);
				}
				else
				{
					_netEventPoolHead = netEvent.Next;
				}
			}
			netEvent.Next = null;
			netEvent.Type = type;
			netEvent.DataReader.SetSource(readerSource, readerSource?.GetHeaderSize() ?? 0);
			netEvent.Peer = peer;
			netEvent.RemoteEndPoint = remoteEndPoint;
			netEvent.Latency = latency;
			netEvent.ErrorCode = errorCode;
			netEvent.DisconnectReason = disconnectReason;
			netEvent.ConnectionRequest = connectionRequest;
			netEvent.DeliveryMethod = deliveryMethod;
			netEvent.ChannelNumber = channelNumber;
			netEvent.UserData = userData;
			if (flag || _manualMode)
			{
				ProcessEvent(netEvent);
				return;
			}
			lock (_eventLock)
			{
				if (_pendingEventTail == null)
				{
					_pendingEventHead = netEvent;
				}
				else
				{
					_pendingEventTail.Next = netEvent;
				}
				_pendingEventTail = netEvent;
			}
		}

		private void ProcessEvent(NetEvent evt)
		{
			bool isNull = evt.DataReader.IsNull;
			switch (evt.Type)
			{
			case NetEvent.EType.Connect:
				_netEventListener.OnPeerConnected(evt.Peer);
				break;
			case NetEvent.EType.Disconnect:
			{
				DisconnectInfo disconnectInfo = default(DisconnectInfo);
				disconnectInfo.Reason = evt.DisconnectReason;
				disconnectInfo.AdditionalData = evt.DataReader;
				disconnectInfo.SocketErrorCode = evt.ErrorCode;
				DisconnectInfo disconnectInfo2 = disconnectInfo;
				_netEventListener.OnPeerDisconnected(evt.Peer, disconnectInfo2);
				break;
			}
			case NetEvent.EType.Receive:
				_netEventListener.OnNetworkReceive(evt.Peer, evt.DataReader, evt.ChannelNumber, evt.DeliveryMethod);
				break;
			case NetEvent.EType.ReceiveUnconnected:
				_netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.BasicMessage);
				break;
			case NetEvent.EType.Broadcast:
				_netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.Broadcast);
				break;
			case NetEvent.EType.Error:
				_netEventListener.OnNetworkError(evt.RemoteEndPoint, evt.ErrorCode);
				break;
			case NetEvent.EType.ConnectionLatencyUpdated:
				_netEventListener.OnNetworkLatencyUpdate(evt.Peer, evt.Latency);
				break;
			case NetEvent.EType.ConnectionRequest:
				_netEventListener.OnConnectionRequest(evt.ConnectionRequest);
				break;
			case NetEvent.EType.MessageDelivered:
				_deliveryEventListener.OnMessageDelivered(evt.Peer, evt.UserData);
				break;
			case NetEvent.EType.PeerAddressChanged:
			{
				_peersLock.EnterUpgradeableReadLock();
				IPEndPoint iPEndPoint = null;
				if (ContainsPeer(evt.Peer))
				{
					_peersLock.EnterWriteLock();
					RemovePeerFromSet(evt.Peer);
					iPEndPoint = new IPEndPoint(evt.Peer.Address, evt.Peer.Port);
					evt.Peer.FinishEndPointChange(evt.RemoteEndPoint);
					AddPeerToSet(evt.Peer);
					_peersLock.ExitWriteLock();
				}
				_peersLock.ExitUpgradeableReadLock();
				if (iPEndPoint != null && _peerAddressChangedListener != null)
				{
					_peerAddressChangedListener.OnPeerAddressChanged(evt.Peer, iPEndPoint);
				}
				break;
			}
			}
			if (isNull)
			{
				RecycleEvent(evt);
			}
			else if (AutoRecycle)
			{
				evt.DataReader.RecycleInternal();
			}
		}

		internal void RecycleEvent(NetEvent evt)
		{
			evt.Peer = null;
			evt.ErrorCode = SocketError.Success;
			evt.RemoteEndPoint = null;
			evt.ConnectionRequest = null;
			lock (_eventLock)
			{
				evt.Next = _netEventPoolHead;
				_netEventPoolHead = evt;
			}
		}

		private void UpdateLogic()
		{
			List<NetPeer> list = new List<NetPeer>();
			Stopwatch stopwatch = new Stopwatch();
			stopwatch.Start();
			while (IsRunning)
			{
				try
				{
					float num = (float)((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0);
					num = ((num <= 0f) ? 0.001f : num);
					stopwatch.Restart();
					for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
					{
						if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > (float)DisconnectTimeout)
						{
							list.Add(netPeer);
						}
						else
						{
							netPeer.Update(num);
						}
					}
					if (list.Count > 0)
					{
						_peersLock.EnterWriteLock();
						for (int i = 0; i < list.Count; i++)
						{
							RemovePeer(list[i], enableWriteLock: false);
						}
						_peersLock.ExitWriteLock();
						list.Clear();
					}
					ProcessNtpRequests(num);
					int num2 = UpdateTime - (int)stopwatch.ElapsedMilliseconds;
					if (num2 > 0)
					{
						_updateTriggerEvent.WaitOne(num2);
					}
				}
				catch (ThreadAbortException)
				{
					return;
				}
				catch (Exception ex2)
				{
					NetDebug.WriteError("[NM] LogicThread error: " + ex2);
				}
			}
			stopwatch.Stop();
		}

		[Conditional("DEBUG")]
		private void ProcessDelayedPackets()
		{
			if (!SimulateLatency)
			{
				return;
			}
			DateTime utcNow = DateTime.UtcNow;
			lock (_pingSimulationList)
			{
				for (int i = 0; i < _pingSimulationList.Count; i++)
				{
					IncomingData incomingData = _pingSimulationList[i];
					if (incomingData.TimeWhenGet <= utcNow)
					{
						HandleMessageReceived(incomingData.Data, incomingData.EndPoint);
						_pingSimulationList.RemoveAt(i);
						i--;
					}
				}
			}
		}

		private void ProcessNtpRequests(float elapsedMilliseconds)
		{
			List<IPEndPoint> list = null;
			foreach (KeyValuePair<IPEndPoint, NtpRequest> ntpRequest in _ntpRequests)
			{
				ntpRequest.Value.Send(_udpSocketv4, elapsedMilliseconds);
				if (ntpRequest.Value.NeedToKill)
				{
					if (list == null)
					{
						list = new List<IPEndPoint>();
					}
					list.Add(ntpRequest.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (IPEndPoint item in list)
			{
				_ntpRequests.TryRemove(item, out var _);
			}
		}

		public void ManualUpdate(float elapsedMilliseconds)
		{
			if (!_manualMode)
			{
				return;
			}
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > (float)DisconnectTimeout)
				{
					RemovePeer(netPeer, enableWriteLock: false);
				}
				else
				{
					netPeer.Update(elapsedMilliseconds);
				}
			}
			ProcessNtpRequests(elapsedMilliseconds);
		}

		internal NetPeer OnConnectionSolved(ConnectionRequest request, byte[] rejectData, int start, int length)
		{
			NetPeer actualValue = null;
			if (request.Result == ConnectionRequestResult.RejectForce)
			{
				if (rejectData != null && length > 0)
				{
					NetPacket netPacket = PoolGetWithProperty(PacketProperty.Disconnect, length);
					netPacket.ConnectionNumber = request.InternalPacket.ConnectionNumber;
					FastBitConverter.GetBytes(netPacket.RawData, 1, request.InternalPacket.ConnectionTime);
					if (netPacket.Size >= NetConstants.PossibleMtu[0])
					{
						NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU!");
					}
					else
					{
						Buffer.BlockCopy(rejectData, start, netPacket.RawData, 9, length);
					}
					SendRawAndRecycle(netPacket, request.RemoteEndPoint);
				}
				lock (_requestsDict)
				{
					_requestsDict.Remove(request.RemoteEndPoint);
				}
			}
			else
			{
				lock (_requestsDict)
				{
					if (!TryGetPeer(request.RemoteEndPoint, out actualValue))
					{
						if (request.Result == ConnectionRequestResult.Reject)
						{
							actualValue = new NetPeer(this, request.RemoteEndPoint, GetNextPeerId());
							actualValue.Reject(request.InternalPacket, rejectData, start, length);
							AddPeer(actualValue);
						}
						else
						{
							actualValue = new NetPeer(this, request, GetNextPeerId());
							AddPeer(actualValue);
							CreateEvent(NetEvent.EType.Connect, actualValue, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
						}
					}
					_requestsDict.Remove(request.RemoteEndPoint);
				}
			}
			return actualValue;
		}

		private int GetNextPeerId()
		{
			if (!_peerIds.TryDequeue(out var result))
			{
				return _lastPeerId++;
			}
			return result;
		}

		private void ProcessConnectRequest(IPEndPoint remoteEndPoint, NetPeer netPeer, NetConnectRequestPacket connRequest)
		{
			if (netPeer != null)
			{
				ConnectRequestResult connectRequestResult = netPeer.ProcessConnectRequest(connRequest);
				switch (connectRequestResult)
				{
				default:
					return;
				case ConnectRequestResult.Reconnection:
					DisconnectPeerForce(netPeer, DisconnectReason.Reconnect, SocketError.Success, null);
					RemovePeer(netPeer, enableWriteLock: true);
					break;
				case ConnectRequestResult.NewConnection:
					RemovePeer(netPeer, enableWriteLock: true);
					break;
				case ConnectRequestResult.P2PLose:
					DisconnectPeerForce(netPeer, DisconnectReason.PeerToPeerConnection, SocketError.Success, null);
					RemovePeer(netPeer, enableWriteLock: true);
					break;
				}
				if (connectRequestResult != ConnectRequestResult.P2PLose)
				{
					connRequest.ConnectionNumber = (byte)((netPeer.ConnectionNum + 1) % 4);
				}
			}
			ConnectionRequest value;
			lock (_requestsDict)
			{
				if (_requestsDict.TryGetValue(remoteEndPoint, out value))
				{
					value.UpdateRequest(connRequest);
					return;
				}
				value = new ConnectionRequest(remoteEndPoint, connRequest, this);
				_requestsDict.Add(remoteEndPoint, value);
			}
			CreateEvent(NetEvent.EType.ConnectionRequest, null, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, value, DeliveryMethod.Unreliable, 0);
		}

		private void OnMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			if (packet.Size == 0)
			{
				PoolRecycle(packet);
				return;
			}
			_dropPacket = false;
			if (!_dropPacket)
			{
				HandleMessageReceived(packet, remoteEndPoint);
			}
		}

		[Conditional("DEBUG")]
		private void HandleSimulateLatency(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			if (!SimulateLatency)
			{
				return;
			}
			int num = _randomGenerator.Next(SimulationMinLatency, SimulationMaxLatency);
			if (num > 5)
			{
				lock (_pingSimulationList)
				{
					_pingSimulationList.Add(new IncomingData
					{
						Data = packet,
						EndPoint = remoteEndPoint,
						TimeWhenGet = DateTime.UtcNow.AddMilliseconds(num)
					});
				}
				_dropPacket = true;
			}
		}

		[Conditional("DEBUG")]
		private void HandleSimulatePacketLoss()
		{
			if (SimulatePacketLoss && _randomGenerator.NextDouble() * 100.0 < (double)SimulationPacketLossChance)
			{
				_dropPacket = true;
			}
		}

		private void HandleMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			int size = packet.Size;
			if (EnableStatistics)
			{
				Statistics.IncrementPacketsReceived();
				Statistics.AddBytesReceived(size);
			}
			if (_ntpRequests.Count > 0 && _ntpRequests.TryGetValue(remoteEndPoint, out var _))
			{
				if (packet.Size >= 48)
				{
					byte[] array = new byte[packet.Size];
					Buffer.BlockCopy(packet.RawData, 0, array, 0, packet.Size);
					NtpPacket ntpPacket = NtpPacket.FromServerResponse(array, DateTime.UtcNow);
					try
					{
						ntpPacket.ValidateReply();
					}
					catch (InvalidOperationException)
					{
						ntpPacket = null;
					}
					if (ntpPacket != null)
					{
						_ntpRequests.TryRemove(remoteEndPoint, out var _);
						_ntpEventListener?.OnNtpResponse(ntpPacket);
					}
				}
				return;
			}
			if (_extraPacketLayer != null)
			{
				_extraPacketLayer.ProcessInboundPacket(ref remoteEndPoint, ref packet.RawData, ref packet.Size);
				if (packet.Size == 0)
				{
					return;
				}
			}
			if (!packet.Verify())
			{
				NetDebug.WriteError("[NM] DataReceived: bad!");
				PoolRecycle(packet);
				return;
			}
			switch (packet.Property)
			{
			case PacketProperty.ConnectRequest:
				if (NetConnectRequestPacket.GetProtocolId(packet) != 13)
				{
					SendRawAndRecycle(PoolGetWithProperty(PacketProperty.InvalidProtocol), remoteEndPoint);
					return;
				}
				break;
			case PacketProperty.Broadcast:
				if (BroadcastReceiveEnabled)
				{
					CreateEvent(NetEvent.EType.Broadcast, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, packet);
				}
				return;
			case PacketProperty.UnconnectedMessage:
				if (UnconnectedMessagesEnabled)
				{
					CreateEvent(NetEvent.EType.ReceiveUnconnected, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, packet);
				}
				return;
			case PacketProperty.NatMessage:
				if (NatPunchEnabled)
				{
					NatPunchModule.ProcessMessage(remoteEndPoint, packet);
				}
				return;
			}
			NetPeer actualValue = remoteEndPoint as NetPeer;
			bool flag = actualValue != null || TryGetPeer(remoteEndPoint, out actualValue);
			if (flag && EnableStatistics)
			{
				actualValue.Statistics.IncrementPacketsReceived();
				actualValue.Statistics.AddBytesReceived(size);
			}
			switch (packet.Property)
			{
			case PacketProperty.ConnectRequest:
			{
				NetConnectRequestPacket netConnectRequestPacket = NetConnectRequestPacket.FromData(packet);
				if (netConnectRequestPacket != null)
				{
					ProcessConnectRequest(remoteEndPoint, actualValue, netConnectRequestPacket);
				}
				break;
			}
			case PacketProperty.PeerNotFound:
				if (flag)
				{
					if (actualValue.ConnectionState == ConnectionState.Connected)
					{
						if (packet.Size == 1)
						{
							actualValue.ResetMtu();
							SendRaw(NetConnectAcceptPacket.MakeNetworkChanged(actualValue), remoteEndPoint);
						}
						else if (packet.Size == 2 && packet.RawData[1] == 1)
						{
							DisconnectPeerForce(actualValue, DisconnectReason.PeerNotFound, SocketError.Success, null);
						}
					}
				}
				else
				{
					if (packet.Size <= 1)
					{
						break;
					}
					bool flag2 = false;
					if (AllowPeerAddressChange)
					{
						NetConnectAcceptPacket netConnectAcceptPacket = NetConnectAcceptPacket.FromData(packet);
						if (netConnectAcceptPacket != null && netConnectAcceptPacket.PeerNetworkChanged && netConnectAcceptPacket.PeerId < _peersArray.Length)
						{
							_peersLock.EnterUpgradeableReadLock();
							NetPeer netPeer = _peersArray[netConnectAcceptPacket.PeerId];
							_peersLock.ExitUpgradeableReadLock();
							if (netPeer != null && netPeer.ConnectTime == netConnectAcceptPacket.ConnectionTime && netPeer.ConnectionNum == netConnectAcceptPacket.ConnectionNumber)
							{
								if (netPeer.ConnectionState == ConnectionState.Connected)
								{
									netPeer.InitiateEndPointChange();
									CreateEvent(NetEvent.EType.PeerAddressChanged, netPeer, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
								}
								flag2 = true;
							}
						}
					}
					PoolRecycle(packet);
					if (!flag2)
					{
						NetPacket netPacket = PoolGetWithProperty(PacketProperty.PeerNotFound, 1);
						netPacket.RawData[1] = 1;
						SendRawAndRecycle(netPacket, remoteEndPoint);
					}
				}
				break;
			case PacketProperty.InvalidProtocol:
				if (flag && actualValue.ConnectionState == ConnectionState.Outgoing)
				{
					DisconnectPeerForce(actualValue, DisconnectReason.InvalidProtocol, SocketError.Success, null);
				}
				break;
			case PacketProperty.Disconnect:
				if (flag)
				{
					DisconnectResult disconnectResult = actualValue.ProcessDisconnect(packet);
					if (disconnectResult == DisconnectResult.None)
					{
						PoolRecycle(packet);
						break;
					}
					DisconnectPeerForce(actualValue, (disconnectResult == DisconnectResult.Disconnect) ? DisconnectReason.RemoteConnectionClose : DisconnectReason.ConnectionRejected, SocketError.Success, packet);
				}
				else
				{
					PoolRecycle(packet);
				}
				SendRawAndRecycle(PoolGetWithProperty(PacketProperty.ShutdownOk), remoteEndPoint);
				break;
			case PacketProperty.ConnectAccept:
				if (flag)
				{
					NetConnectAcceptPacket netConnectAcceptPacket2 = NetConnectAcceptPacket.FromData(packet);
					if (netConnectAcceptPacket2 != null && actualValue.ProcessConnectAccept(netConnectAcceptPacket2))
					{
						CreateEvent(NetEvent.EType.Connect, actualValue, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
					}
				}
				break;
			default:
				if (flag)
				{
					actualValue.ProcessPacket(packet);
				}
				else
				{
					SendRawAndRecycle(PoolGetWithProperty(PacketProperty.PeerNotFound), remoteEndPoint);
				}
				break;
			}
		}

		internal void CreateReceiveEvent(NetPacket packet, DeliveryMethod method, byte channelNumber, int headerSize, NetPeer fromPeer)
		{
			if (UnsyncedEvents || UnsyncedReceiveEvent || _manualMode)
			{
				NetEvent netEvent;
				lock (_eventLock)
				{
					netEvent = _netEventPoolHead;
					if (netEvent == null)
					{
						netEvent = new NetEvent(this);
					}
					else
					{
						_netEventPoolHead = netEvent.Next;
					}
				}
				netEvent.Next = null;
				netEvent.Type = NetEvent.EType.Receive;
				netEvent.DataReader.SetSource(packet, headerSize);
				netEvent.Peer = fromPeer;
				netEvent.DeliveryMethod = method;
				netEvent.ChannelNumber = channelNumber;
				ProcessEvent(netEvent);
				return;
			}
			lock (_eventLock)
			{
				NetEvent netEvent = _netEventPoolHead;
				if (netEvent == null)
				{
					netEvent = new NetEvent(this);
				}
				else
				{
					_netEventPoolHead = netEvent.Next;
				}
				netEvent.Next = null;
				netEvent.Type = NetEvent.EType.Receive;
				netEvent.DataReader.SetSource(packet, headerSize);
				netEvent.Peer = fromPeer;
				netEvent.DeliveryMethod = method;
				netEvent.ChannelNumber = channelNumber;
				if (_pendingEventTail == null)
				{
					_pendingEventHead = netEvent;
				}
				else
				{
					_pendingEventTail.Next = netEvent;
				}
				_pendingEventTail = netEvent;
			}
		}

		public void SendToAll(NetDataWriter writer, DeliveryMethod options)
		{
			SendToAll(writer.Data, 0, writer.Length, options);
		}

		public void SendToAll(byte[] data, DeliveryMethod options)
		{
			SendToAll(data, 0, data.Length, options);
		}

		public void SendToAll(byte[] data, int start, int length, DeliveryMethod options)
		{
			SendToAll(data, start, length, 0, options);
		}

		public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options)
		{
			SendToAll(writer.Data, 0, writer.Length, channelNumber, options);
		}

		public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options)
		{
			SendToAll(data, 0, data.Length, channelNumber, options);
		}

		public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options)
		{
			try
			{
				_peersLock.EnterReadLock();
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					netPeer.Send(data, start, length, channelNumber, options);
				}
			}
			finally
			{
				_peersLock.ExitReadLock();
			}
		}

		public void SendToAll(NetDataWriter writer, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(writer.Data, 0, writer.Length, 0, options, excludePeer);
		}

		public void SendToAll(byte[] data, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(data, 0, data.Length, 0, options, excludePeer);
		}

		public void SendToAll(byte[] data, int start, int length, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(data, start, length, 0, options, excludePeer);
		}

		public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(writer.Data, 0, writer.Length, channelNumber, options, excludePeer);
		}

		public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(data, 0, data.Length, channelNumber, options, excludePeer);
		}

		public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options, NetPeer excludePeer)
		{
			try
			{
				_peersLock.EnterReadLock();
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					if (netPeer != excludePeer)
					{
						netPeer.Send(data, start, length, channelNumber, options);
					}
				}
			}
			finally
			{
				_peersLock.ExitReadLock();
			}
		}

		public void SendToAll(ReadOnlySpan<byte> data, DeliveryMethod options)
		{
			SendToAll(data, 0, options, null);
		}

		public void SendToAll(ReadOnlySpan<byte> data, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(data, 0, options, excludePeer);
		}

		public void SendToAll(ReadOnlySpan<byte> data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer)
		{
			try
			{
				_peersLock.EnterReadLock();
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					if (netPeer != excludePeer)
					{
						netPeer.Send(data, channelNumber, options);
					}
				}
			}
			finally
			{
				_peersLock.ExitReadLock();
			}
		}

		public bool SendUnconnectedMessage(ReadOnlySpan<byte> message, IPEndPoint remoteEndPoint)
		{
			int headerSize = NetPacket.GetHeaderSize(PacketProperty.UnconnectedMessage);
			NetPacket netPacket = PoolGetPacket(message.Length + headerSize);
			netPacket.Property = PacketProperty.UnconnectedMessage;
			message.CopyTo(new Span<byte>(netPacket.RawData, headerSize, message.Length));
			return SendRawAndRecycle(netPacket, remoteEndPoint) > 0;
		}

		public bool Start()
		{
			return Start(0);
		}

		public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port)
		{
			return Start(addressIPv4, addressIPv6, port, manualMode: false);
		}

		public bool Start(string addressIPv4, string addressIPv6, int port)
		{
			IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4);
			IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6);
			return Start(addressIPv7, addressIPv8, port);
		}

		public bool Start(int port)
		{
			return Start(IPAddress.Any, IPAddress.IPv6Any, port);
		}

		public bool StartInManualMode(IPAddress addressIPv4, IPAddress addressIPv6, int port)
		{
			return Start(addressIPv4, addressIPv6, port, manualMode: true);
		}

		public bool StartInManualMode(string addressIPv4, string addressIPv6, int port)
		{
			IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4);
			IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6);
			return StartInManualMode(addressIPv7, addressIPv8, port);
		}

		public bool StartInManualMode(int port)
		{
			return StartInManualMode(IPAddress.Any, IPAddress.IPv6Any, port);
		}

		public bool SendUnconnectedMessage(byte[] message, IPEndPoint remoteEndPoint)
		{
			return SendUnconnectedMessage(message, 0, message.Length, remoteEndPoint);
		}

		public bool SendUnconnectedMessage(NetDataWriter writer, string address, int port)
		{
			IPEndPoint remoteEndPoint = NetUtils.MakeEndPoint(address, port);
			return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint);
		}

		public bool SendUnconnectedMessage(NetDataWriter writer, IPEndPoint remoteEndPoint)
		{
			return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint);
		}

		public bool SendUnconnectedMessage(byte[] message, int start, int length, IPEndPoint remoteEndPoint)
		{
			NetPacket packet = PoolGetWithData(PacketProperty.UnconnectedMessage, message, start, length);
			return SendRawAndRecycle(packet, remoteEndPoint) > 0;
		}

		public void TriggerUpdate()
		{
			_updateTriggerEvent.Set();
		}

		public void PollEvents(int maxProcessedEvents = 0)
		{
			if (_manualMode)
			{
				if (_udpSocketv4 != null)
				{
					ManualReceive(_udpSocketv4, _bufferEndPointv4, maxProcessedEvents);
				}
				if (_udpSocketv6 != null && _udpSocketv6 != _udpSocketv4)
				{
					ManualReceive(_udpSocketv6, _bufferEndPointv6, maxProcessedEvents);
				}
			}
			else
			{
				if (UnsyncedEvents)
				{
					return;
				}
				NetEvent netEvent;
				lock (_eventLock)
				{
					netEvent = _pendingEventHead;
					_pendingEventHead = null;
					_pendingEventTail = null;
				}
				int num = 0;
				while (netEvent != null)
				{
					NetEvent next = netEvent.Next;
					ProcessEvent(netEvent);
					netEvent = next;
					num++;
					if (num == maxProcessedEvents)
					{
						break;
					}
				}
			}
		}

		public NetPeer Connect(string address, int port, string key)
		{
			return Connect(address, port, NetDataWriter.FromString(key));
		}

		public NetPeer Connect(string address, int port, NetDataWriter connectionData)
		{
			IPEndPoint target;
			try
			{
				target = NetUtils.MakeEndPoint(address, port);
			}
			catch
			{
				CreateEvent(NetEvent.EType.Disconnect, null, null, SocketError.Success, 0, DisconnectReason.UnknownHost, null, DeliveryMethod.Unreliable, 0);
				return null;
			}
			return Connect(target, connectionData);
		}

		public NetPeer Connect(IPEndPoint target, string key)
		{
			return Connect(target, NetDataWriter.FromString(key));
		}

		public NetPeer Connect(IPEndPoint target, NetDataWriter connectionData)
		{
			if (!IsRunning)
			{
				throw new InvalidOperationException("Client is not running");
			}
			lock (_requestsDict)
			{
				if (_requestsDict.ContainsKey(target))
				{
					return null;
				}
				byte connectNum = 0;
				if (TryGetPeer(target, out var actualValue))
				{
					ConnectionState connectionState = actualValue.ConnectionState;
					if (connectionState == ConnectionState.Outgoing || connectionState == ConnectionState.Connected)
					{
						return actualValue;
					}
					connectNum = (byte)((actualValue.ConnectionNum + 1) % 4);
					RemovePeer(actualValue, enableWriteLock: true);
				}
				actualValue = new NetPeer(this, target, GetNextPeerId(), connectNum, connectionData);
				AddPeer(actualValue);
				return actualValue;
			}
		}

		public void Stop()
		{
			Stop(sendDisconnectMessages: true);
		}

		public void Stop(bool sendDisconnectMessages)
		{
			if (IsRunning)
			{
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					netPeer.Shutdown(null, 0, 0, !sendDisconnectMessages);
				}
				CloseSocket();
				_updateTriggerEvent.Set();
				if (!_manualMode)
				{
					_logicThread.Join();
					_logicThread = null;
				}
				ClearPeerSet();
				_peerIds = new ConcurrentQueue<int>();
				_lastPeerId = 0;
				_connectedPeersCount = 0L;
				_pendingEventHead = null;
				_pendingEventTail = null;
			}
		}

		[Conditional("DEBUG")]
		private void ClearPingSimulationList()
		{
			lock (_pingSimulationList)
			{
				_pingSimulationList.Clear();
			}
		}

		public int GetPeersCount(ConnectionState peerState)
		{
			int num = 0;
			_peersLock.EnterReadLock();
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if ((netPeer.ConnectionState & peerState) != 0)
				{
					num++;
				}
			}
			_peersLock.ExitReadLock();
			return num;
		}

		public void GetPeersNonAlloc(List<NetPeer> peers, ConnectionState peerState)
		{
			peers.Clear();
			_peersLock.EnterReadLock();
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if ((netPeer.ConnectionState & peerState) != 0)
				{
					peers.Add(netPeer);
				}
			}
			_peersLock.ExitReadLock();
		}

		public void DisconnectAll()
		{
			DisconnectAll(null, 0, 0);
		}

		public void DisconnectAll(byte[] data, int start, int count)
		{
			_peersLock.EnterReadLock();
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				DisconnectPeer(netPeer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, force: false, data, start, count, null);
			}
			_peersLock.ExitReadLock();
		}

		public void DisconnectPeerForce(NetPeer peer)
		{
			DisconnectPeerForce(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, null);
		}

		public void DisconnectPeer(NetPeer peer)
		{
			DisconnectPeer(peer, null, 0, 0);
		}

		public void DisconnectPeer(NetPeer peer, byte[] data)
		{
			DisconnectPeer(peer, data, 0, data.Length);
		}

		public void DisconnectPeer(NetPeer peer, NetDataWriter writer)
		{
			DisconnectPeer(peer, writer.Data, 0, writer.Length);
		}

		public void DisconnectPeer(NetPeer peer, byte[] data, int start, int count)
		{
			DisconnectPeer(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, force: false, data, start, count, null);
		}

		public void CreateNtpRequest(IPEndPoint endPoint)
		{
			_ntpRequests.TryAdd(endPoint, new NtpRequest(endPoint));
		}

		public void CreateNtpRequest(string ntpServerAddress, int port)
		{
			IPEndPoint iPEndPoint = NetUtils.MakeEndPoint(ntpServerAddress, port);
			_ntpRequests.TryAdd(iPEndPoint, new NtpRequest(iPEndPoint));
		}

		public void CreateNtpRequest(string ntpServerAddress)
		{
			IPEndPoint iPEndPoint = NetUtils.MakeEndPoint(ntpServerAddress, 123);
			_ntpRequests.TryAdd(iPEndPoint, new NtpRequest(iPEndPoint));
		}

		public NetPeerEnumerator GetEnumerator()
		{
			return new NetPeerEnumerator(_headPeer);
		}

		IEnumerator<NetPeer> IEnumerable<NetPeer>.GetEnumerator()
		{
			return new NetPeerEnumerator(_headPeer);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return new NetPeerEnumerator(_headPeer);
		}

		private static int HashSetGetPrime(int min)
		{
			int[] primes = Primes;
			foreach (int num in primes)
			{
				if (num >= min)
				{
					return num;
				}
			}
			for (int j = min | 1; j < int.MaxValue; j += 2)
			{
				if (IsPrime(j) && (j - 1) % 101 != 0)
				{
					return j;
				}
			}
			return min;
			static bool IsPrime(int candidate)
			{
				if (((uint)candidate & (true ? 1u : 0u)) != 0)
				{
					int num2 = (int)Math.Sqrt(candidate);
					for (int k = 3; k <= num2; k += 2)
					{
						if (candidate % k == 0)
						{
							return false;
						}
					}
					return true;
				}
				return candidate == 2;
			}
		}

		private void ClearPeerSet()
		{
			_peersLock.EnterWriteLock();
			_headPeer = null;
			if (_lastIndex > 0)
			{
				Array.Clear(_slots, 0, _lastIndex);
				Array.Clear(_buckets, 0, _buckets.Length);
				_lastIndex = 0;
				_count = 0;
				_freeList = -1;
			}
			_peersArray = new NetPeer[32];
			_peersLock.ExitWriteLock();
		}

		private bool ContainsPeer(NetPeer item)
		{
			if (item == null)
			{
				NetDebug.WriteError($"Contains peer null: {item}");
				return false;
			}
			if (_buckets != null)
			{
				int num = item.GetHashCode() & 0x7FFFFFFF;
				for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next)
				{
					if (_slots[num2].HashCode == num && _slots[num2].Value.Equals(item))
					{
						return true;
					}
				}
			}
			return false;
		}

		public NetPeer GetPeerById(int id)
		{
			if (id < 0 || id >= _peersArray.Length)
			{
				return null;
			}
			return _peersArray[id];
		}

		public bool TryGetPeerById(int id, out NetPeer peer)
		{
			peer = GetPeerById(id);
			return peer != null;
		}

		private void AddPeer(NetPeer peer)
		{
			if (peer == null)
			{
				NetDebug.WriteError($"Add peer null: {peer}");
				return;
			}
			_peersLock.EnterWriteLock();
			if (_headPeer != null)
			{
				peer.NextPeer = _headPeer;
				_headPeer.PrevPeer = peer;
			}
			_headPeer = peer;
			AddPeerToSet(peer);
			if (peer.Id >= _peersArray.Length)
			{
				int num = _peersArray.Length * 2;
				while (peer.Id >= num)
				{
					num *= 2;
				}
				Array.Resize(ref _peersArray, num);
			}
			_peersArray[peer.Id] = peer;
			_peersLock.ExitWriteLock();
		}

		private void RemovePeer(NetPeer peer, bool enableWriteLock)
		{
			if (enableWriteLock)
			{
				_peersLock.EnterWriteLock();
			}
			if (!RemovePeerFromSet(peer))
			{
				if (enableWriteLock)
				{
					_peersLock.ExitWriteLock();
				}
				return;
			}
			if (peer == _headPeer)
			{
				_headPeer = peer.NextPeer;
			}
			if (peer.PrevPeer != null)
			{
				peer.PrevPeer.NextPeer = peer.NextPeer;
			}
			if (peer.NextPeer != null)
			{
				peer.NextPeer.PrevPeer = peer.PrevPeer;
			}
			peer.PrevPeer = null;
			_peersArray[peer.Id] = null;
			_peerIds.Enqueue(peer.Id);
			if (enableWriteLock)
			{
				_peersLock.ExitWriteLock();
			}
		}

		private bool RemovePeerFromSet(NetPeer peer)
		{
			if (_buckets == null || peer == null)
			{
				return false;
			}
			int num = peer.GetHashCode() & 0x7FFFFFFF;
			int num2 = num % _buckets.Length;
			int num3 = -1;
			for (int num4 = _buckets[num2] - 1; num4 >= 0; num4 = _slots[num4].Next)
			{
				if (_slots[num4].HashCode == num && _slots[num4].Value.Equals(peer))
				{
					if (num3 < 0)
					{
						_buckets[num2] = _slots[num4].Next + 1;
					}
					else
					{
						_slots[num3].Next = _slots[num4].Next;
					}
					_slots[num4].HashCode = -1;
					_slots[num4].Value = null;
					_slots[num4].Next = _freeList;
					_count--;
					if (_count == 0)
					{
						_lastIndex = 0;
						_freeList = -1;
					}
					else
					{
						_freeList = num4;
					}
					return true;
				}
				num3 = num4;
			}
			return false;
		}

		private bool TryGetPeer(IPEndPoint endPoint, out NetPeer actualValue)
		{
			if (_buckets != null)
			{
				int num = endPoint.GetHashCode() & 0x7FFFFFFF;
				_peersLock.EnterReadLock();
				for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next)
				{
					if (_slots[num2].HashCode == num && _slots[num2].Value.Equals(endPoint))
					{
						actualValue = _slots[num2].Value;
						_peersLock.ExitReadLock();
						return true;
					}
				}
				_peersLock.ExitReadLock();
			}
			actualValue = null;
			return false;
		}

		private bool TryGetPeer(SocketAddress saddr, out NetPeer actualValue)
		{
			if (_buckets != null)
			{
				int num = saddr.GetHashCode() & 0x7FFFFFFF;
				_peersLock.EnterReadLock();
				for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next)
				{
					if (_slots[num2].HashCode == num && _slots[num2].Value.Serialize().Equals(saddr))
					{
						actualValue = _slots[num2].Value;
						_peersLock.ExitReadLock();
						return true;
					}
				}
				_peersLock.ExitReadLock();
			}
			actualValue = null;
			return false;
		}

		private bool AddPeerToSet(NetPeer value)
		{
			if (_buckets == null)
			{
				int num = HashSetGetPrime(0);
				_buckets = new int[num];
				_slots = new Slot[num];
			}
			int num2 = value.GetHashCode() & 0x7FFFFFFF;
			int num3 = num2 % _buckets.Length;
			for (int num4 = _buckets[num2 % _buckets.Length] - 1; num4 >= 0; num4 = _slots[num4].Next)
			{
				if (_slots[num4].HashCode == num2 && _slots[num4].Value.Equals(value))
				{
					return false;
				}
			}
			int num5;
			if (_freeList >= 0)
			{
				num5 = _freeList;
				_freeList = _slots[num5].Next;
			}
			else
			{
				if (_lastIndex == _slots.Length)
				{
					int num6 = 2 * _count;
					num6 = (((uint)num6 > 2147483587u && 2147483587 > _count) ? 2147483587 : HashSetGetPrime(num6));
					Slot[] array = new Slot[num6];
					Array.Copy(_slots, 0, array, 0, _lastIndex);
					_buckets = new int[num6];
					for (int i = 0; i < _lastIndex; i++)
					{
						int num7 = array[i].HashCode % num6;
						array[i].Next = _buckets[num7] - 1;
						_buckets[num7] = i + 1;
					}
					_slots = array;
					num3 = num2 % _buckets.Length;
				}
				num5 = _lastIndex;
				_lastIndex++;
			}
			_slots[num5].HashCode = num2;
			_slots[num5].Value = value;
			_slots[num5].Next = _buckets[num3] - 1;
			_buckets[num3] = num5 + 1;
			_count++;
			return true;
		}

		private NetPacket PoolGetWithData(PacketProperty property, byte[] data, int start, int length)
		{
			int headerSize = NetPacket.GetHeaderSize(property);
			NetPacket netPacket = PoolGetPacket(length + headerSize);
			netPacket.Property = property;
			Buffer.BlockCopy(data, start, netPacket.RawData, headerSize, length);
			return netPacket;
		}

		private NetPacket PoolGetWithProperty(PacketProperty property, int size)
		{
			NetPacket netPacket = PoolGetPacket(size + NetPacket.GetHeaderSize(property));
			netPacket.Property = property;
			return netPacket;
		}

		private NetPacket PoolGetWithProperty(PacketProperty property)
		{
			NetPacket netPacket = PoolGetPacket(NetPacket.GetHeaderSize(property));
			netPacket.Property = property;
			return netPacket;
		}

		internal NetPacket PoolGetPacket(int size)
		{
			if (size > NetConstants.MaxPacketSize)
			{
				return new NetPacket(size);
			}
			NetPacket poolHead;
			lock (_poolLock)
			{
				poolHead = _poolHead;
				if (poolHead == null)
				{
					return new NetPacket(size);
				}
				_poolHead = _poolHead.Next;
				_poolCount--;
			}
			poolHead.Size = size;
			if (poolHead.RawData.Length < size)
			{
				poolHead.RawData = new byte[size];
			}
			return poolHead;
		}

		internal void PoolRecycle(NetPacket packet)
		{
			if (packet.RawData.Length > NetConstants.MaxPacketSize || _poolCount >= PacketPoolSize)
			{
				return;
			}
			packet.RawData[0] = 0;
			lock (_poolLock)
			{
				packet.Next = _poolHead;
				_poolHead = packet;
				_poolCount++;
			}
		}

		static NetManager()
		{
			Primes = new int[72]
			{
				3, 7, 11, 17, 23, 29, 37, 47, 59, 71,
				89, 107, 131, 163, 197, 239, 293, 353, 431, 521,
				631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371,
				4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023,
				25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363,
				156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403,
				968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559,
				5999471, 7199369
			};
			MulticastAddressV6 = IPAddress.Parse("ff02::1");
			IPv6Support = Socket.OSSupportsIPv6;
		}

		private bool ProcessError(SocketException ex)
		{
			switch (ex.SocketErrorCode)
			{
			case SocketError.NotConnected:
				NotConnected = true;
				return true;
			case SocketError.OperationAborted:
			case SocketError.Interrupted:
			case SocketError.NotSocket:
				return true;
			default:
				NetDebug.WriteError($"[R]Error code: {ex.SocketErrorCode} - {ex}");
				CreateEvent(NetEvent.EType.Error, null, null, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
				break;
			case SocketError.WouldBlock:
			case SocketError.MessageSize:
			case SocketError.NetworkReset:
			case SocketError.ConnectionReset:
			case SocketError.TimedOut:
				break;
			}
			return false;
		}

		private void ManualReceive(Socket socket, EndPoint bufferEndPoint, int maxReceive)
		{
			try
			{
				int num = 0;
				while (socket.Available > 0)
				{
					ReceiveFrom(socket, ref bufferEndPoint);
					num++;
					if (num == maxReceive)
					{
						break;
					}
				}
			}
			catch (SocketException ex)
			{
				ProcessError(ex);
			}
			catch (ObjectDisposedException)
			{
			}
			catch (Exception ex3)
			{
				NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex3);
			}
		}

		private void NativeReceiveLogic()
		{
			IntPtr handle = _udpSocketv4.Handle;
			IntPtr s2 = _udpSocketv6?.Handle ?? IntPtr.Zero;
			byte[] address2 = new byte[16];
			byte[] address3 = new byte[28];
			IPEndPoint tempEndPoint = new IPEndPoint(IPAddress.Any, 0);
			List<Socket> list = new List<Socket>(2);
			Socket udpSocketv = _udpSocketv4;
			Socket udpSocketv2 = _udpSocketv6;
			NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize);
			while (IsRunning)
			{
				try
				{
					if (udpSocketv2 == null)
					{
						if (!NativeReceiveFrom(handle, address2))
						{
							break;
						}
						continue;
					}
					bool flag = false;
					if (udpSocketv.Available != 0 || list.Contains(udpSocketv))
					{
						if (!NativeReceiveFrom(handle, address2))
						{
							break;
						}
						flag = true;
					}
					if (udpSocketv2.Available != 0 || list.Contains(udpSocketv2))
					{
						if (!NativeReceiveFrom(s2, address3))
						{
							break;
						}
						flag = true;
					}
					list.Clear();
					if (!flag)
					{
						list.Add(udpSocketv);
						list.Add(udpSocketv2);
						Socket.Select(list, null, null, 500000);
					}
				}
				catch (SocketException ex)
				{
					if (ProcessError(ex))
					{
						break;
					}
				}
				catch (ObjectDisposedException)
				{
					break;
				}
				catch (ThreadAbortException)
				{
					break;
				}
				catch (Exception ex4)
				{
					NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex4);
				}
			}
			bool NativeReceiveFrom(IntPtr s, byte[] address)
			{
				int socketAddressSize = address.Length;
				packet.Size = NativeSocket.RecvFrom(s, packet.RawData, NetConstants.MaxPacketSize, address, ref socketAddressSize);
				if (packet.Size == 0)
				{
					return true;
				}
				if (packet.Size == -1)
				{
					return !ProcessError(new SocketException((int)NativeSocket.GetSocketError()));
				}
				short num = (short)((address[1] << 8) | address[0]);
				tempEndPoint.Port = (ushort)((address[2] << 8) | address[3]);
				if ((NativeSocket.UnixMode && num == 10) || (!NativeSocket.UnixMode && num == 23))
				{
					uint num2 = (uint)((address[27] << 24) + (address[26] << 16) + (address[25] << 8) + address[24]);
					tempEndPoint.Address = new IPAddress(new ReadOnlySpan<byte>(address, 8, 16), num2);
				}
				else
				{
					long newAddress = (uint)((address[4] & 0xFF) | ((address[5] << 8) & 0xFF00) | ((address[6] << 16) & 0xFF0000) | (address[7] << 24));
					tempEndPoint.Address = new IPAddress(newAddress);
				}
				if (TryGetPeer(tempEndPoint, out var actualValue))
				{
					OnMessageReceived(packet, actualValue);
				}
				else
				{
					OnMessageReceived(packet, tempEndPoint);
					tempEndPoint = new IPEndPoint(IPAddress.Any, 0);
				}
				packet = PoolGetPacket(NetConstants.MaxPacketSize);
				return true;
			}
		}

		private void ReceiveFrom(Socket s, ref EndPoint bufferEndPoint)
		{
			NetPacket netPacket = PoolGetPacket(NetConstants.MaxPacketSize);
			netPacket.Size = s.ReceiveFrom(netPacket.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, ref bufferEndPoint);
			OnMessageReceived(netPacket, (IPEndPoint)bufferEndPoint);
		}

		private void ReceiveLogic()
		{
			EndPoint bufferEndPoint = new IPEndPoint(IPAddress.Any, 0);
			EndPoint bufferEndPoint2 = new IPEndPoint(IPAddress.IPv6Any, 0);
			List<Socket> list = new List<Socket>(2);
			Socket udpSocketv = _udpSocketv4;
			Socket udpSocketv2 = _udpSocketv6;
			while (IsRunning)
			{
				try
				{
					if (udpSocketv2 == null)
					{
						if (udpSocketv.Available != 0 || udpSocketv.Poll(500000, SelectMode.SelectRead))
						{
							ReceiveFrom(udpSocketv, ref bufferEndPoint);
						}
						continue;
					}
					bool flag = false;
					if (udpSocketv.Available != 0 || list.Contains(udpSocketv))
					{
						ReceiveFrom(udpSocketv, ref bufferEndPoint);
						flag = true;
					}
					if (udpSocketv2.Available != 0 || list.Contains(udpSocketv2))
					{
						ReceiveFrom(udpSocketv2, ref bufferEndPoint2);
						flag = true;
					}
					list.Clear();
					if (!flag)
					{
						list.Add(udpSocketv);
						list.Add(udpSocketv2);
						Socket.Select(list, null, null, 500000);
					}
				}
				catch (SocketException ex)
				{
					if (ProcessError(ex))
					{
						break;
					}
				}
				catch (ObjectDisposedException)
				{
					break;
				}
				catch (ThreadAbortException)
				{
					break;
				}
				catch (Exception ex4)
				{
					NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex4);
				}
			}
		}

		public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool manualMode)
		{
			if (IsRunning && !NotConnected)
			{
				return false;
			}
			NotConnected = false;
			_manualMode = manualMode;
			UseNativeSockets = UseNativeSockets && NativeSocket.IsSupported;
			_udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
			if (!BindSocket(_udpSocketv4, new IPEndPoint(addressIPv4, port)))
			{
				return false;
			}
			LocalPort = ((IPEndPoint)_udpSocketv4.LocalEndPoint).Port;
			IsRunning = true;
			if (_manualMode)
			{
				_bufferEndPointv4 = new IPEndPoint(IPAddress.Any, 0);
			}
			if (IPv6Support && IPv6Enabled)
			{
				_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
				if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort)))
				{
					if (_manualMode)
					{
						_bufferEndPointv6 = new IPEndPoint(IPAddress.IPv6Any, 0);
					}
				}
				else
				{
					_udpSocketv6 = null;
				}
			}
			if (!manualMode)
			{
				ThreadStart start = ReceiveLogic;
				if (UseNativeSockets)
				{
					start = NativeReceiveLogic;
				}
				_receiveThread = new Thread(start)
				{
					Name = $"ReceiveThread({LocalPort})",
					IsBackground = true
				};
				_receiveThread.Start();
				if (_logicThread == null)
				{
					_logicThread = new Thread(UpdateLogic)
					{
						Name = "LogicThread",
						IsBackground = true
					};
					_logicThread.Start();
				}
			}
			return true;
		}

		private bool BindSocket(Socket socket, IPEndPoint ep)
		{
			socket.ReceiveTimeout = 500;
			socket.SendTimeout = 500;
			socket.ReceiveBufferSize = 1048576;
			socket.SendBufferSize = 1048576;
			socket.Blocking = true;
			if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
			{
				try
				{
					socket.IOControl(-1744830452, new byte[1], null);
				}
				catch
				{
				}
			}
			try
			{
				socket.ExclusiveAddressUse = !ReuseAddress;
				socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, ReuseAddress);
				socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, DontRoute);
			}
			catch
			{
			}
			if (ep.AddressFamily == AddressFamily.InterNetwork)
			{
				Ttl = 255;
				try
				{
					socket.EnableBroadcast = true;
				}
				catch (SocketException ex)
				{
					NetDebug.WriteError($"[B]Broadcast error: {ex.SocketErrorCode}");
				}
				if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
				{
					try
					{
						socket.DontFragment = true;
					}
					catch (SocketException ex2)
					{
						NetDebug.WriteError($"[B]DontFragment error: {ex2.SocketErrorCode}");
					}
				}
			}
			try
			{
				socket.Bind(ep);
				if (ep.AddressFamily == AddressFamily.InterNetworkV6)
				{
					try
					{
						socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(MulticastAddressV6));
					}
					catch (Exception)
					{
					}
				}
			}
			catch (SocketException ex4)
			{
				switch (ex4.SocketErrorCode)
				{
				case SocketError.AddressAlreadyInUse:
					if (socket.AddressFamily == AddressFamily.InterNetworkV6)
					{
						try
						{
							socket.DualMode = false;
							socket.Bind(ep);
						}
						catch (SocketException ex5)
						{
							NetDebug.WriteError($"[B]Bind exception: {ex5}, errorCode: {ex5.SocketErrorCode}");
							return false;
						}
						return true;
					}
					break;
				case SocketError.AddressFamilyNotSupported:
					return true;
				}
				NetDebug.WriteError($"[B]Bind exception: {ex4}, errorCode: {ex4.SocketErrorCode}");
				return false;
			}
			return true;
		}

		internal int SendRawAndRecycle(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			int result = SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
			PoolRecycle(packet);
			return result;
		}

		internal int SendRaw(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			return SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
		}

		internal unsafe int SendRaw(byte[] message, int start, int length, IPEndPoint remoteEndPoint)
		{
			if (!IsRunning)
			{
				return 0;
			}
			NetPacket netPacket = null;
			if (_extraPacketLayer != null)
			{
				netPacket = PoolGetPacket(length + _extraPacketLayer.ExtraPacketSizeForLayer);
				Buffer.BlockCopy(message, start, netPacket.RawData, 0, length);
				start = 0;
				_extraPacketLayer.ProcessOutBoundPacket(ref remoteEndPoint, ref netPacket.RawData, ref start, ref length);
				message = netPacket.RawData;
			}
			Socket socket = _udpSocketv4;
			if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support)
			{
				socket = _udpSocketv6;
				if (socket == null)
				{
					return 0;
				}
			}
			int num;
			try
			{
				if (UseNativeSockets && remoteEndPoint is NetPeer netPeer)
				{
					fixed (byte* pinnedBuffer = &message[start])
					{
						num = NativeSocket.SendTo(socket.Handle, pinnedBuffer, length, netPeer.NativeAddress, netPeer.NativeAddress.Length);
					}
					if (num == -1)
					{
						throw NativeSocket.GetSocketException();
					}
				}
				else
				{
					num = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint);
				}
			}
			catch (SocketException ex)
			{
				switch (ex.SocketErrorCode)
				{
				case SocketError.Interrupted:
				case SocketError.NoBufferSpaceAvailable:
					return 0;
				case SocketError.MessageSize:
					return 0;
				case SocketError.NetworkUnreachable:
				case SocketError.HostUnreachable:
					if (DisconnectOnUnreachable && remoteEndPoint is NetPeer peer)
					{
						DisconnectPeerForce(peer, (ex.SocketErrorCode == SocketError.HostUnreachable) ? DisconnectReason.HostUnreachable : DisconnectReason.NetworkUnreachable, ex.SocketErrorCode, null);
					}
					CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
					return -1;
				case SocketError.Shutdown:
					CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
					return -1;
				default:
					NetDebug.WriteError($"[S] {ex}");
					return -1;
				}
			}
			catch (Exception value)
			{
				NetDebug.WriteError($"[S] {value}");
				return 0;
			}
			finally
			{
				if (netPacket != null)
				{
					PoolRecycle(netPacket);
				}
			}
			if (num <= 0)
			{
				return 0;
			}
			if (EnableStatistics)
			{
				Statistics.IncrementPacketsSent();
				Statistics.AddBytesSent(length);
			}
			return num;
		}

		public bool SendBroadcast(NetDataWriter writer, int port)
		{
			return SendBroadcast(writer.Data, 0, writer.Length, port);
		}

		public bool SendBroadcast(byte[] data, int port)
		{
			return SendBroadcast(data, 0, data.Length, port);
		}

		public bool SendBroadcast(byte[] data, int start, int length, int port)
		{
			if (!IsRunning)
			{
				return false;
			}
			NetPacket netPacket;
			if (_extraPacketLayer != null)
			{
				int headerSize = NetPacket.GetHeaderSize(PacketProperty.Broadcast);
				netPacket = PoolGetPacket(headerSize + length + _extraPacketLayer.ExtraPacketSizeForLayer);
				netPacket.Property = PacketProperty.Broadcast;
				Buffer.BlockCopy(data, start, netPacket.RawData, headerSize, length);
				int offset = 0;
				int length2 = length + headerSize;
				IPEndPoint endPoint = null;
				_extraPacketLayer.ProcessOutBoundPacket(ref endPoint, ref netPacket.RawData, ref offset, ref length2);
			}
			else
			{
				netPacket = PoolGetWithData(PacketProperty.Broadcast, data, start, length);
			}
			bool flag = false;
			bool flag2 = false;
			try
			{
				flag = _udpSocketv4.SendTo(netPacket.RawData, 0, netPacket.Size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port)) > 0;
				if (_udpSocketv6 != null)
				{
					flag2 = _udpSocketv6.SendTo(netPacket.RawData, 0, netPacket.Size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port)) > 0;
				}
			}
			catch (SocketException ex)
			{
				if (ex.SocketErrorCode == SocketError.HostUnreachable)
				{
					return flag;
				}
				NetDebug.WriteError($"[S][MCAST] {ex}");
				return flag;
			}
			catch (Exception value)
			{
				NetDebug.WriteError($"[S][MCAST] {value}");
				return flag;
			}
			finally
			{
				PoolRecycle(netPacket);
			}
			return flag || flag2;
		}

		private void CloseSocket()
		{
			IsRunning = false;
			_udpSocketv4?.Close();
			_udpSocketv6?.Close();
			_udpSocketv4 = null;
			_udpSocketv6 = null;
			if (_receiveThread != null && _receiveThread != Thread.CurrentThread)
			{
				_receiveThread.Join();
			}
			_receiveThread = null;
		}
	}
	internal enum PacketProperty : byte
	{
		Unreliable,
		Channeled,
		Ack,
		Ping,
		Pong,
		ConnectRequest,
		ConnectAccept,
		Disconnect,
		UnconnectedMessage,
		MtuCheck,
		MtuOk,
		Broadcast,
		Merged,
		ShutdownOk,
		PeerNotFound,
		InvalidProtocol,
		NatMessage,
		Empty
	}
	internal sealed class NetPacket
	{
		private static readonly int PropertiesCount;

		private static readonly int[] HeaderSizes;

		public byte[] RawData;

		public int Size;

		public object UserData;

		public NetPacket Next;

		public PacketProperty Property
		{
			get
			{
				return (PacketProperty)(RawData[0] & 0x1Fu);
			}
			set
			{
				RawData[0] = (byte)((RawData[0] & 0xE0u) | (uint)value);
			}
		}

		public byte ConnectionNumber
		{
			get
			{
				return (byte)((RawData[0] & 0x60) >> 5);
			}
			set
			{
				RawData[0] = (byte)((RawData[0] & 0x9Fu) | (uint)(value << 5));
			}
		}

		public ushort Sequence
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 1);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 1, value);
			}
		}

		public bool IsFragmented => (RawData[0] & 0x80) != 0;

		public byte ChannelId
		{
			get
			{
				return RawData[3];
			}
			set
			{
				RawData[3] = value;
			}
		}

		public ushort FragmentId
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 4);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 4, value);
			}
		}

		public ushort FragmentPart
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 6);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 6, value);
			}
		}

		public ushort FragmentsTotal
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 8);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 8, value);
			}
		}

		static NetPacket()
		{
			PropertiesCount = Enum.GetValues(typeof(PacketProperty)).Length;
			HeaderSizes = NetUtils.AllocatePinnedUninitializedArray<int>(PropertiesCount);
			for (int i = 0; i < HeaderSizes.Length; i++)
			{
				switch ((PacketProperty)(byte)i)
				{
				case PacketProperty.Channeled:
				case PacketProperty.Ack:
					HeaderSizes[i] = 4;
					break;
				case PacketProperty.Ping:
					HeaderSizes[i] = 3;
					break;
				case PacketProperty.ConnectRequest:
					HeaderSizes[i] = 18;
					break;
				case PacketProperty.ConnectAccept:
					HeaderSizes[i] = 15;
					break;
				case PacketProperty.Disconnect:
					HeaderSizes[i] = 9;
					break;
				case PacketProperty.Pong:
					HeaderSizes[i] = 11;
					break;
				default:
					HeaderSizes[i] = 1;
					break;
				}
			}
		}

		public void MarkFragmented()
		{
			RawData[0] |= 128;
		}

		public NetPacket(int size)
		{
			RawData = new byte[size];
			Size = size;
		}

		public NetPacket(PacketProperty property, int size)
		{
			size += GetHeaderSize(property);
			RawData = new byte[size];
			Property = property;
			Size = size;
		}

		public static int GetHeaderSize(PacketProperty property)
		{
			return HeaderSizes[(uint)property];
		}

		public int GetHeaderSize()
		{
			return HeaderSizes[RawData[0] & 0x1F];
		}

		public bool Verify()
		{
			byte b = (byte)(RawData[0] & 0x1Fu);
			if (b >= PropertiesCount)
			{
				return false;
			}
			int num = HeaderSizes[b];
			bool flag = (RawData[0] & 0x80) != 0;
			if (Size >= num)
			{
				if (flag)
				{
					return Size >= num + 6;
				}
				return true;
			}
			return false;
		}

		public static implicit operator Span<byte>(NetPacket p)
		{
			return new Span<byte>(p.RawData, 0, p.Size);
		}
	}
	[Flags]
	public enum ConnectionState : byte
	{
		Outgoing = 2,
		Connected = 4,
		ShutdownRequested = 8,
		Disconnected = 0x10,
		EndPointChange = 0x20,
		Any = 0x2E
	}
	internal enum ConnectRequestResult
	{
		None,
		P2PLose,
		Reconnection,
		NewConnection
	}
	internal enum DisconnectResult
	{
		None,
		Reject,
		Disconnect
	}
	internal enum ShutdownResult
	{
		None,
		Success,
		WasConnected
	}
	public class NetPeer : IPEndPoint
	{
		private class IncomingFragments
		{
			public NetPacket[] Fragments;

			public int ReceivedCount;

			public int TotalSize;

			public byte ChannelId;
		}

		private int _rtt;

		private int _avgRtt;

		private int _rttCount;

		private double _resendDelay = 27.0;

		private float _pingSendTimer;

		private float _rttResetTimer;

		private readonly Stopwatch _pingTimer = new Stopwatch();

		private volatile float _timeSinceLastPacket;

		private long _remoteDelta;

		private readonly object _shutdownLock = new object();

		internal volatile NetPeer NextPeer;

		internal NetPeer PrevPeer;

		private NetPacket[] _unreliableSecondQueue;

		private NetPacket[] _unreliableChannel;

		private int _unreliablePendingCount;

		private readonly object _unreliableChannelLock = new object();

		private readonly ConcurrentQueue<BaseChannel> _channelSendQueue;

		private readonly BaseChannel[] _channels;

		private int _mtu;

		private int _mtuIdx;

		private bool _finishMtu;

		private float _mtuCheckTimer;

		private int _mtuCheckAttempts;

		private const int MtuCheckDelay = 1000;

		private const int MaxMtuCheckAttempts = 4;

		private readonly object _mtuMutex = new object();

		private int _fragmentId;

		private readonly Dictionary<ushort, IncomingFragments> _holdedFragments;

		private readonly Dictionary<ushort, ushort> _deliveredFragments;

		private readonly NetPacket _mergeData;

		private int _mergePos;

		private int _mergeCount;

		private int _connectAttempts;

		private float _connectTimer;

		private long _connectTime;

		private byte _connectNum;

		private ConnectionState _connectionState;

		private NetPacket _shutdownPacket;

		private const int ShutdownDelay = 300;

		private float _shutdownTimer;

		private readonly NetPacket _pingPacket;

		private readonly NetPacket _pongPacket;

		private readonly NetPacket _connectRequestPacket;

		private readonly NetPacket _connectAcceptPacket;

		public readonly NetManager NetManager;

		public readonly int Id;

		public object Tag;

		public readonly NetStatistics Statistics;

		private SocketAddress _cachedSocketAddr;

		private int _cachedHashCode;

		internal byte[] NativeAddress;

		internal byte ConnectionNum
		{
			get
			{
				return _connectNum;
			}
			private set
			{
				_connectNum = value;
				_mergeData.ConnectionNumber = value;
				_pingPacket.ConnectionNumber = value;
				_pongPacket.ConnectionNumber = value;
			}
		}

		public ConnectionState ConnectionState => _connectionState;

		internal long ConnectTime => _connectTime;

		public int RemoteId { get; private set; }

		public int Ping => _avgRtt / 2;

		public int RoundTripTime => _avgRtt;

		public int Mtu => _mtu;

		public long RemoteTimeDelta => _remoteDelta;

		public DateTime RemoteUtcTime => new DateTime(DateTime.UtcNow.Ticks + _remoteDelta);

		public float TimeSinceLastPacket => _timeSinceLastPacket;

		internal double ResendDelay => _resendDelay;

		public override SocketAddress Serialize()
		{
			return _cachedSocketAddr;
		}

		public override int GetHashCode()
		{
			return _cachedHashCode;
		}

		internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id)
			: base(remoteEndPoint.Address, remoteEndPoint.Port)
		{
			Id = id;
			Statistics = new NetStatistics();
			NetManager = netManager;
			_cachedSocketAddr = base.Serialize();
			if (NetManager.UseNativeSockets)
			{
				NativeAddress = new byte[_cachedSocketAddr.Size];
				for (int i = 0; i < _cachedSocketAddr.Size; i++)
				{
					NativeAddress[i] = _cachedSocketAddr[i];
				}
			}
			_cachedHashCode = base.GetHashCode();
			ResetMtu();
			_connectionState = ConnectionState.Connected;
			_mergeData = new NetPacket(PacketProperty.Merged, NetConstants.MaxPacketSize);
			_pongPacket = new NetPacket(PacketProperty.Pong, 0);
			_pingPacket = new NetPacket(PacketProperty.Ping, 0)
			{
				Sequence = 1
			};
			_unreliableSecondQueue = new NetPacket[8];
			_unreliableChannel = new NetPacket[8];
			_holdedFragments = new Dictionary<ushort, IncomingFragments>();
			_deliveredFragments = new Dictionary<ushort, ushort>();
			_channels = new BaseChannel[netManager.ChannelsCount * 4];
			_channelSendQueue = new ConcurrentQueue<BaseChannel>();
		}

		internal void InitiateEndPointChange()
		{
			ResetMtu();
			_connectionState = ConnectionState.EndPointChange;
		}

		internal void FinishEndPointChange(IPEndPoint newEndPoint)
		{
			if (_connectionState != ConnectionState.EndPointChange)
			{
				return;
			}
			_connectionState = ConnectionState.Connected;
			base.Address = newEndPoint.Address;
			base.Port = newEndPoint.Port;
			if (NetManager.UseNativeSockets)
			{
				NativeAddress = new byte[_cachedSocketAddr.Size];
				for (int i = 0; i < _cachedSocketAddr.Size; i++)
				{
					NativeAddress[i] = _cachedSocketAddr[i];
				}
			}
			_cachedSocketAddr = base.Serialize();
			_cachedHashCode = base.GetHashCode();
		}

		internal void ResetMtu()
		{
			_finishMtu = !NetManager.MtuDiscovery;
			if (NetManager.MtuOverride > 0)
			{
				OverrideMtu(NetManager.MtuOverride);
			}
			else
			{
				SetMtu(0);
			}
		}

		private void SetMtu(int mtuIdx)
		{
			_mtuIdx = mtuIdx;
			_mtu = NetConstants.PossibleMtu[mtuIdx] - NetManager.ExtraPacketSizeForLayer;
		}

		private void OverrideMtu(int mtuValue)
		{
			_mtu = mtuValue;
			_finishMtu = true;
		}

		public int GetPacketsCountInReliableQueue(byte channelNumber, bool ordered)
		{
			int num = channelNumber * 4 + (ordered ? 2 : 0);
			BaseChannel baseChannel = _channels[num];
			if (baseChannel == null)
			{
				return 0;
			}
			return ((ReliableChannel)baseChannel).PacketsInQueue;
		}

		public PooledPacket CreatePacketFromPool(DeliveryMethod deliveryMethod, byte channelNumber)
		{
			int mtu = _mtu;
			NetPacket netPacket = NetManager.PoolGetPacket(mtu);
			if (deliveryMethod == DeliveryMethod.Unreliable)
			{
				netPacket.Property = PacketProperty.Unreliable;
				return new PooledPacket(netPacket, mtu, 0);
			}
			netPacket.Property = PacketProperty.Channeled;
			return new PooledPacket(netPacket, mtu, (byte)((uint)(channelNumber * 4) + (ui