Decompiled source of LethalGargoyles v0.5.0

plugins/LethalGargoyles/DropDaDeuce.LethalGargoyles.dll

Decompiled a week 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.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Coroner;
using DropDaDeuce.LethalGargoyles.NetcodePatcher;
using GameNetcodeStuff;
using HarmonyLib;
using LethalGargoyles.src.Config;
using LethalGargoyles.src.Enemy;
using LethalGargoyles.src.Patch;
using LethalGargoyles.src.SoftDepends;
using LethalGargoyles.src.Utility;
using LethalLib.Modules;
using Microsoft.CodeAnalysis;
using NVorbis;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DropDaDeuce.LethalGargoyles")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0+5beb05042c796f3ca74b6ba2a6d35ed4fc7b9c1d")]
[assembly: AssemblyProduct("LethalGargoyles")]
[assembly: AssemblyTitle("DropDaDeuce.LethalGargoyles")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[module: NetcodePatchedAssembly]
internal class <Module>
{
	static <Module>()
	{
	}
}
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 LethalGargoyles
{
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "DropDaDeuce.LethalGargoyles";

		public const string PLUGIN_NAME = "LethalGargoyles";

		public const string PLUGIN_VERSION = "0.5.0";
	}
}
namespace LethalGargoyles.src
{
	[BepInPlugin("DropDaDeuce.LethalGargoyles", "LethalGargoyles", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger = null;

		internal static readonly Harmony harmony = new Harmony("LethalLib");

		public static AssetBundle? ModAssets;

		public static Dictionary<string, List<string>> defaultAudioClipFilePaths = new Dictionary<string, List<string>>();

		public static Plugin Instance { get; private set; } = null;


		internal static PluginConfig BoundConfig { get; private set; } = null;


		public bool IsCoronerLoaded { get; private set; }

		public bool IsEmployeeClassesLoaded { get; private set; }

		public static string? CustomAudioFolderPath { get; private set; }

		[Conditional("DEBUG")]
		public void LogIfDebugBuild(string text)
		{
			Logger.LogInfo((object)text);
		}

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			BoundConfig = new PluginConfig(((BaseUnityPlugin)this).Config);
			InitializeNetworkBehaviours();
			Instance = this;
			string path = "gargoyleassets";
			ModAssets = AssetBundle.LoadFromFile(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), path));
			if ((Object)(object)ModAssets == (Object)null)
			{
				Logger.LogError((object)"Failed to load custom assets.");
				return;
			}
			CustomAudioFolderPath = Path.Combine(Path.GetDirectoryName(Paths.ExecutablePath), "Lethal Gargoyles", "Custom Voice Lines");
			if (!Directory.Exists(CustomAudioFolderPath))
			{
				try
				{
					Directory.CreateDirectory(CustomAudioFolderPath);
				}
				catch (Exception arg)
				{
					Logger.LogError((object)$"Failed to create custom audio directory: {arg}");
					return;
				}
			}
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Combat Dialog", "Attack"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Combat Dialog", "Hit"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - Activity"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - Aggro"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - Enemy"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - Gargoyle Death"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - General"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - Player Death"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - Prior Death", "Coroner"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - EmployeeClass"));
			Directory.CreateDirectory(Path.Combine(CustomAudioFolderPath, "Taunt - SteamIDs"));
			EnemyType val = ModAssets.LoadAsset<EnemyType>("LethalGargoyle");
			TerminalNode val2 = ModAssets.LoadAsset<TerminalNode>("LethalGargoyleTN");
			TerminalKeyword val3 = ModAssets.LoadAsset<TerminalKeyword>("LethalGargoyleTK");
			NetworkPrefabs.RegisterNetworkPrefab(val.enemyPrefab);
			Enemies.RegisterEnemy(val, BoundConfig.SpawnWeight.Value, (LevelTypes)(-1), val2, val3);
			harmony.PatchAll();
			IsCoronerLoaded = DepIsLoaded("com.elitemastereric.coroner");
			IsEmployeeClassesLoaded = DepIsLoaded("Jade.EmployeeClasses");
			Logger.LogInfo((object)("Coroner Is Loaded? " + IsCoronerLoaded));
			Logger.LogInfo((object)("EmployeeClasses Is Loaded? " + IsEmployeeClassesLoaded));
			if (IsCoronerLoaded)
			{
				CoronerClass.Init();
			}
			defaultAudioClipFilePaths = GetDefaultAudioClipFilePaths();
			BoundConfig.InitializeAudioClipConfigs(defaultAudioClipFilePaths);
			Logger.LogInfo((object)"Plugin DropDaDeuce.LethalGargoyles is loaded!");
		}

		private static void InitializeNetworkBehaviours()
		{
			Type[] types = Assembly.GetExecutingAssembly().GetTypes();
			Type[] array = types;
			foreach (Type type in array)
			{
				if (type.FullName == "LethalGargoyles.src.SoftDepends.CoronerClass")
				{
					continue;
				}
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
				MethodInfo[] array2 = methods;
				foreach (MethodInfo methodInfo in array2)
				{
					object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false);
					if (customAttributes.Length != 0)
					{
						methodInfo.Invoke(null, null);
					}
				}
			}
		}

		private bool DepIsLoaded(string pGUID)
		{
			try
			{
				return Chainloader.PluginInfos.ContainsKey(pGUID);
			}
			catch
			{
				return false;
			}
		}

		private Dictionary<string, List<string>> GetDefaultAudioClipFilePaths()
		{
			Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>
			{
				{
					"General",
					new List<string>()
				},
				{
					"Aggro",
					new List<string>()
				},
				{
					"Enemy",
					new List<string>()
				},
				{
					"PlayerDeath",
					new List<string>()
				},
				{
					"GargoyleDeath",
					new List<string>()
				},
				{
					"PriorDeath",
					new List<string>()
				},
				{
					"Attack",
					new List<string>()
				},
				{
					"Hit",
					new List<string>()
				},
				{
					"Activity",
					new List<string>()
				},
				{
					"SteamIDs",
					new List<string>()
				}
			};
			if (Instance.IsEmployeeClassesLoaded)
			{
				dictionary.Add("Class", new List<string>());
			}
			foreach (KeyValuePair<string, List<string>> item in dictionary)
			{
				string key = item.Key;
				List<string> value = item.Value;
				FileInfo[] mP3Files = AudioManager.GetMP3Files(key, "Voice Lines");
				FileInfo[] array = mP3Files;
				foreach (FileInfo fileInfo in array)
				{
					value.Add(fileInfo.FullName);
				}
				if (key == "PriorDeath" && Instance.IsCoronerLoaded)
				{
					FileInfo[] mP3Files2 = AudioManager.GetMP3Files("Coroner", "Voice Lines");
					FileInfo[] array2 = mP3Files2;
					foreach (FileInfo fileInfo2 in array2)
					{
						value.Add(fileInfo2.FullName);
					}
				}
				if (Instance.IsEmployeeClassesLoaded && key == "Class")
				{
					FileInfo[] mP3Files3 = AudioManager.GetMP3Files("EmployeeClass", "Voice Lines");
					FileInfo[] array3 = mP3Files3;
					foreach (FileInfo fileInfo3 in array3)
					{
						value.Add(fileInfo3.FullName);
					}
				}
			}
			return dictionary;
		}
	}
}
namespace LethalGargoyles.src.Utility
{
	public class AudioManager : NetworkBehaviour
	{
		public static List<AudioClip> tauntClips = new List<AudioClip>();

		public static List<AudioClip> aggroClips = new List<AudioClip>();

		public static List<AudioClip> enemyClips = new List<AudioClip>();

		public static List<AudioClip> playerDeathClips = new List<AudioClip>();

		public static List<AudioClip> deathClips = new List<AudioClip>();

		public static List<AudioClip> priorDeathClips = new List<AudioClip>();

		public static List<AudioClip> activityClips = new List<AudioClip>();

		public static List<AudioClip> attackClips = new List<AudioClip>();

		public static List<AudioClip> hitClips = new List<AudioClip>();

		public static List<AudioClip> classClips = new List<AudioClip>();

		public static List<AudioClip> playerClips = new List<AudioClip>();

		public static AudioManager? Instance;

		private readonly Dictionary<ulong, bool> clientReady = new Dictionary<ulong, bool>();

		public static Dictionary<string, List<string>> AudioClipFilePaths { get; private set; } = new Dictionary<string, List<string>>();


		[Conditional("DEBUG")]
		private static void LogIfDebugBuild(string text)
		{
			Plugin.Logger.LogInfo((object)text);
		}

		public override void OnNetworkSpawn()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			((NetworkBehaviour)this).OnNetworkSpawn();
			Instance = this;
			NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("SendLGAudioClip", new HandleNamedMessageDelegate(OnReceivedMessage));
			Plugin.Logger.LogInfo((object)"Registered message handler for 'SendLGAudioClip'");
			if (((NetworkBehaviour)this).IsServer)
			{
				NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnectedCallback;
				NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectedCallback;
			}
			else
			{
				Plugin.Logger.LogInfo((object)$"Connected to host with ID: {NetworkManager.Singleton.LocalClientId}");
			}
			if (((NetworkBehaviour)this).IsHost)
			{
				Plugin.Logger.LogInfo((object)"Creating Audio Clip List");
				LoadClipList(Plugin.defaultAudioClipFilePaths);
				LoadAudioClipsFromConfig();
			}
		}

		public override void OnNetworkDespawn()
		{
			Plugin.Logger.LogInfo((object)"Clearing clip lists");
			tauntClips.Clear();
			aggroClips.Clear();
			enemyClips.Clear();
			playerDeathClips.Clear();
			deathClips.Clear();
			priorDeathClips.Clear();
			activityClips.Clear();
			attackClips.Clear();
			hitClips.Clear();
			classClips.Clear();
			playerClips.Clear();
			if (((NetworkBehaviour)this).IsServer)
			{
				NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnectedCallback;
				NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectedCallback;
			}
		}

		private IEnumerator LogClipCounts()
		{
			yield return null;
		}

		private void OnClientConnectedCallback(ulong clientId)
		{
			Plugin.Logger.LogInfo((object)$"Client connected: {clientId}");
			if (!clientReady.ContainsKey(clientId))
			{
				clientReady.Add(clientId, value: true);
			}
			SendAudioClipsDelayed(clientId);
		}

		private void OnClientDisconnectedCallback(ulong clientId)
		{
			if (clientReady.ContainsKey(clientId))
			{
				clientReady.Remove(clientId);
			}
		}

		public async void SendAudioClipsDelayed(ulong clientId)
		{
			bool isPlayerFullyLoaded = false;
			List<ulong> fullyLoadedPlayers = StartOfRound.Instance.fullyLoadedPlayers;
			CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
			cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10.0));
			await Task.Run(delegate
			{
				while (!isPlayerFullyLoaded || !clientReady.ContainsKey(clientId))
				{
					for (int i = 0; i < fullyLoadedPlayers.Count; i++)
					{
						if (fullyLoadedPlayers[i] == clientId)
						{
							isPlayerFullyLoaded = true;
							break;
						}
					}
					Task.Yield();
				}
			}, cancellationTokenSource.Token);
			foreach (KeyValuePair<string, List<string>> audioClipFilePath in AudioClipFilePaths)
			{
				string category = audioClipFilePath.Key;
				List<string> value = audioClipFilePath.Value;
				GetClipListByCategory(category);
				foreach (string item in value)
				{
					string clipName = Path.GetFileNameWithoutExtension(item);
					byte[] audioData2;
					if (PluginConfig.AudioClipEnableConfig.TryGetValue(clipName, out ConfigEntry<bool> value2))
					{
						if (!value2.Value)
						{
							continue;
						}
						audioData2 = AudioFileToByteArray(item);
						byte[] array = audioData2;
						if (array != null && array.Length > 512000)
						{
							Plugin.Logger.LogError((object)("Sending Clip(" + clipName + ") failed. Max clip size is 512000 bytes or 500KB"));
							break;
						}
						if (audioData2 != null)
						{
							await WaitForClientReady(clientId);
							if (!clientReady.ContainsKey(clientId))
							{
								break;
							}
							await SendAudioClipToClient(clientId, audioData2, clipName, category);
							clientReady[clientId] = false;
							SetClientReadyClientRpc(isReady: false, clientId);
							Plugin.Logger.LogInfo((object)$"Sent Clip({clipName}) to ClientID({clientId})");
						}
						continue;
					}
					audioData2 = AudioFileToByteArray(item);
					byte[] array2 = audioData2;
					if (array2 != null && array2.Length > 512000)
					{
						Plugin.Logger.LogError((object)("Sending Clip(" + clipName + ") failed. Max clip size is 512000 bytes or 500KB"));
						break;
					}
					if (audioData2 != null)
					{
						await WaitForClientReady(clientId);
						if (!clientReady.ContainsKey(clientId))
						{
							break;
						}
						await SendAudioClipToClient(clientId, audioData2, clipName, category);
						clientReady[clientId] = false;
						SetClientReadyClientRpc(isReady: false, clientId);
						Plugin.Logger.LogInfo((object)$"Sent Clip({clipName}) to ClientID({clientId})");
					}
				}
			}
		}

		private async Task WaitForClientReady(ulong clientId)
		{
			CancellationTokenSource cts = new CancellationTokenSource();
			cts.CancelAfter(TimeSpan.FromSeconds(5.0));
			try
			{
				await Task.Run(async delegate
				{
					while (!clientReady[clientId])
					{
						await Task.Delay(100, cts.Token);
					}
				}, cts.Token);
			}
			catch (OperationCanceledException)
			{
				if (!clientReady.ContainsKey(clientId))
				{
					Plugin.Logger.LogWarning((object)$"Client {clientId} is disconnected.");
					return;
				}
				Plugin.Logger.LogWarning((object)$"Client {clientId} did not respond within the timeout, trying to send clip anyways.");
				clientReady[clientId] = true;
			}
		}

		private byte[]? AudioFileToByteArray(string filePath)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_0097: Invalid comparison between Unknown and I4
			AudioType val = (AudioType)(Path.GetExtension(filePath).ToLower() switch
			{
				".mp3" => 13, 
				".wav" => 20, 
				".ogg" => 14, 
				_ => 0, 
			});
			if ((int)val == 0)
			{
				Plugin.Logger.LogError((object)("Unsupported audio file format: " + filePath));
				return null;
			}
			UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(filePath, val);
			audioClip.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
			UnityWebRequestAsyncOperation val2 = audioClip.SendWebRequest();
			while (!((AsyncOperation)val2).isDone)
			{
				Task.Yield();
			}
			if ((int)audioClip.result == 3)
			{
				Plugin.Logger.LogError((object)audioClip.error);
				return null;
			}
			return audioClip.downloadHandler.data;
		}

		private async Task SendAudioClipToClient(ulong clientId, byte[] audioData, string clipName, string category)
		{
			int num = audioData.Length + 200;
			FastBufferWriter writer = new FastBufferWriter(num, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref writer)).WriteValueSafe(category, false);
				((FastBufferWriter)(ref writer)).WriteValueSafe(clipName, false);
				if (((FastBufferWriter)(ref writer)).Capacity < audioData.Length)
				{
					Plugin.Logger.LogError((object)"Writer Capacity is less than clip size!");
					return;
				}
				int num2 = audioData.Length;
				((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num2, default(ForPrimitives));
				((FastBufferWriter)(ref writer)).WriteBytesSafe(audioData, audioData.Length, 0);
				NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("SendLGAudioClip", clientId, writer, (NetworkDelivery)4);
				await Task.Yield();
			}
			finally
			{
				((IDisposable)(FastBufferWriter)(ref writer)).Dispose();
			}
		}

		public void OnReceivedMessage(ulong clientId, FastBufferReader messagePayload)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)this).IsServer && !((NetworkBehaviour)this).IsHost)
			{
				SetClientReadyServerRpc(isReady: true, NetworkManager.Singleton.LocalClientId);
				string category = default(string);
				((FastBufferReader)(ref messagePayload)).ReadValueSafe(ref category, false);
				string clipName = default(string);
				((FastBufferReader)(ref messagePayload)).ReadValueSafe(ref clipName, false);
				int num = default(int);
				((FastBufferReader)(ref messagePayload)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				byte[] audioData = new byte[num];
				((FastBufferReader)(ref messagePayload)).ReadBytesSafe(ref audioData, num, 0);
				((MonoBehaviour)this).StartCoroutine(ProcessAudioClip(audioData, clipName, category));
			}
		}

		private IEnumerator ProcessAudioClip(byte[] audioData, string clipName, string category)
		{
			VorbisReader val = new VorbisReader((Stream)new MemoryStream(audioData, writable: false), true);
			try
			{
				float[] array = new float[val.TotalSamples];
				AudioClip val2 = AudioClip.Create(clipName, (int)(val.TotalSamples / val.Channels), val.Channels, val.SampleRate, false);
				val.ReadSamples(array, 0, (int)val.TotalSamples);
				val2.SetData(array, 0);
				List<AudioClip> clipListByCategory = GetClipListByCategory(category);
				clipListByCategory.Add(val2);
				Plugin.Logger.LogInfo((object)("Clip Loaded: " + ((Object)val2).name));
			}
			finally
			{
				((IDisposable)val)?.Dispose();
			}
			yield break;
		}

		private void LoadAudioClipsFromConfig()
		{
			foreach (KeyValuePair<string, List<string>> audioClipFilePath in AudioClipFilePaths)
			{
				string key = audioClipFilePath.Key;
				List<string> value = audioClipFilePath.Value;
				List<AudioClip> clipListByCategory = GetClipListByCategory(key);
				foreach (string item in value)
				{
					string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item);
					if (PluginConfig.AudioClipEnableConfig.TryGetValue(fileNameWithoutExtension, out ConfigEntry<bool> value2))
					{
						if (value2.Value)
						{
							((MonoBehaviour)this).StartCoroutine(LoadAudioClip(item, key));
						}
					}
					else
					{
						((MonoBehaviour)this).StartCoroutine(LoadAudioClip(item, key));
					}
				}
			}
		}

		private IEnumerator LoadAudioClip(string filePath, string category)
		{
			string text = Path.GetExtension(filePath).ToLower();
			AudioType val = ((!(text == ".ogg")) ? ((AudioType)0) : ((AudioType)14));
			AudioType val2 = val;
			if ((int)val2 == 0)
			{
				Plugin.Logger.LogError((object)("Unsupported audio file format: " + filePath));
				yield break;
			}
			UnityWebRequest webRequest = UnityWebRequestMultimedia.GetAudioClip(filePath, val2);
			yield return webRequest.SendWebRequest();
			if ((int)webRequest.result == 3)
			{
				Plugin.Logger.LogError((object)webRequest.error);
				yield break;
			}
			AudioClip content = DownloadHandlerAudioClip.GetContent(webRequest);
			List<AudioClip> clipListByCategory = GetClipListByCategory(category);
			((Object)content).name = Path.GetFileNameWithoutExtension(filePath);
			clipListByCategory.Add(content);
			Plugin.Logger.LogInfo((object)("Loaded clip: " + ((Object)content).name + " | Catagory: " + category));
		}

		public static List<AudioClip> GetClipListByCategory(string category)
		{
			return category switch
			{
				"General" => tauntClips, 
				"Aggro" => aggroClips, 
				"Enemy" => enemyClips, 
				"PlayerDeath" => playerDeathClips, 
				"GargoyleDeath" => deathClips, 
				"PriorDeath" => priorDeathClips, 
				"Activity" => activityClips, 
				"Class" => classClips, 
				"Attack" => attackClips, 
				"Hit" => hitClips, 
				"SteamIDs" => playerClips, 
				_ => throw new ArgumentException("Invalid audio clip category: " + category), 
			};
		}

		public void LoadClipList(Dictionary<string, List<string>> defaultAudioClipFilePaths)
		{
			AudioClipFilePaths = defaultAudioClipFilePaths;
			foreach (KeyValuePair<string, List<string>> audioClipFilePath in AudioClipFilePaths)
			{
				string key = audioClipFilePath.Key;
				List<string> value = audioClipFilePath.Value;
				FileInfo[] mP3Files = GetMP3Files(key, "Custom Voice Lines");
				FileInfo[] array = mP3Files;
				foreach (FileInfo fileInfo in array)
				{
					string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
					bool flag = false;
					for (int j = 0; j < value.Count; j++)
					{
						string fileNameWithoutExtension2 = Path.GetFileNameWithoutExtension(value[j]);
						if (fileNameWithoutExtension == fileNameWithoutExtension2)
						{
							value[j] = fileInfo.FullName;
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						value.Add(fileInfo.FullName);
					}
				}
				if (key == "PriorDeath" && Plugin.Instance.IsCoronerLoaded)
				{
					FileInfo[] mP3Files2 = GetMP3Files("Coroner", "Voice Lines");
					FileInfo[] mP3Files3 = GetMP3Files("Coroner", "Custom Voice Lines");
					FileInfo[] array2 = mP3Files2;
					foreach (FileInfo fileInfo2 in array2)
					{
						value.Add(fileInfo2.FullName);
					}
					FileInfo[] array3 = mP3Files3;
					foreach (FileInfo fileInfo3 in array3)
					{
						string fileNameWithoutExtension3 = Path.GetFileNameWithoutExtension(fileInfo3.FullName);
						bool flag2 = false;
						for (int m = 0; m < value.Count; m++)
						{
							string fileNameWithoutExtension4 = Path.GetFileNameWithoutExtension(value[m]);
							if (fileNameWithoutExtension3 == fileNameWithoutExtension4)
							{
								value[m] = fileInfo3.FullName;
								flag2 = true;
								break;
							}
						}
						if (!flag2)
						{
							value.Add(fileInfo3.FullName);
						}
					}
				}
				if (!Plugin.Instance.IsEmployeeClassesLoaded || !(key == "Class"))
				{
					continue;
				}
				FileInfo[] mP3Files4 = GetMP3Files("EmployeeClass", "Voice Lines");
				FileInfo[] mP3Files5 = GetMP3Files("EmployeeClass", "Custom Voice Lines");
				FileInfo[] array4 = mP3Files4;
				foreach (FileInfo fileInfo4 in array4)
				{
					value.Add(fileInfo4.FullName);
				}
				FileInfo[] array5 = mP3Files5;
				foreach (FileInfo fileInfo5 in array5)
				{
					string fileNameWithoutExtension5 = Path.GetFileNameWithoutExtension(fileInfo5.FullName);
					bool flag3 = false;
					for (int num2 = 0; num2 < value.Count; num2++)
					{
						string fileNameWithoutExtension6 = Path.GetFileNameWithoutExtension(value[num2]);
						if (fileNameWithoutExtension5 == fileNameWithoutExtension6)
						{
							value[num2] = fileInfo5.FullName;
							flag3 = true;
							break;
						}
					}
					if (!flag3)
					{
						value.Add(fileInfo5.FullName);
					}
				}
			}
		}

		public static FileInfo[] GetMP3Files(string type, string folderName)
		{
			string text = ((folderName == "Voice Lines") ? Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location), folderName) : ((!(folderName == "Custom Voice Lines")) ? Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location), folderName) : Plugin.CustomAudioFolderPath));
			string path = text;
			switch (type)
			{
			case "General":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - General"));
				return directoryInfo.GetFiles("*.*");
			}
			case "Aggro":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - Aggro"));
				return directoryInfo.GetFiles("*.*");
			}
			case "Enemy":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - Enemy"));
				return directoryInfo.GetFiles("*.*");
			}
			case "PlayerDeath":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - Player Death"));
				return directoryInfo.GetFiles("*.*");
			}
			case "GargoyleDeath":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - Gargoyle Death"));
				return directoryInfo.GetFiles("*.*");
			}
			case "PriorDeath":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - Prior Death"));
				return directoryInfo.GetFiles("*.*");
			}
			case "Coroner":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - Prior Death", "Coroner"));
				return directoryInfo.GetFiles("*.*");
			}
			case "Class":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - EmployeeClass"));
				return directoryInfo.GetFiles("*.*");
			}
			case "Activity":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - Activity"));
				return directoryInfo.GetFiles("*.*");
			}
			case "Attack":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Combat Dialog", "Attack"));
				return directoryInfo.GetFiles("*.*");
			}
			case "Hit":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Combat Dialog", "Hit"));
				return directoryInfo.GetFiles("*.*");
			}
			case "SteamIDs":
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(path, "Taunt - SteamIDs"));
				return directoryInfo.GetFiles("*.*");
			}
			default:
				return Array.Empty<FileInfo>();
			}
		}

		[ClientRpc]
		private void SetClientReadyClientRpc(bool isReady, ulong clientId)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager;
			if (networkManager != null && networkManager.IsListening)
			{
				if ((int)base.__rpc_exec_stage != 2 && (networkManager.IsServer || networkManager.IsHost))
				{
					ClientRpcParams val = default(ClientRpcParams);
					FastBufferWriter val2 = ((NetworkBehaviour)this).__beginSendClientRpc(2917554051u, val, (RpcDelivery)0);
					((FastBufferWriter)(ref val2)).WriteValueSafe<bool>(ref isReady, default(ForPrimitives));
					BytePacker.WriteValueBitPacked(val2, clientId);
					((NetworkBehaviour)this).__endSendClientRpc(ref val2, 2917554051u, val, (RpcDelivery)0);
				}
				if ((int)base.__rpc_exec_stage == 2 && (networkManager.IsClient || networkManager.IsHost) && clientReady.ContainsKey(NetworkManager.Singleton.LocalClientId) && NetworkManager.Singleton.LocalClientId == clientId)
				{
					clientReady[NetworkManager.Singleton.LocalClientId] = isReady;
				}
			}
		}

		[ServerRpc(RequireOwnership = false)]
		private void SetClientReadyServerRpc(bool isReady, ulong clientId)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager;
			if (networkManager != null && networkManager.IsListening)
			{
				if ((int)base.__rpc_exec_stage != 1 && (networkManager.IsClient || networkManager.IsHost))
				{
					ServerRpcParams val = default(ServerRpcParams);
					FastBufferWriter val2 = ((NetworkBehaviour)this).__beginSendServerRpc(1384767487u, val, (RpcDelivery)0);
					((FastBufferWriter)(ref val2)).WriteValueSafe<bool>(ref isReady, default(ForPrimitives));
					BytePacker.WriteValueBitPacked(val2, clientId);
					((NetworkBehaviour)this).__endSendServerRpc(ref val2, 1384767487u, val, (RpcDelivery)0);
				}
				if ((int)base.__rpc_exec_stage == 1 && (networkManager.IsServer || networkManager.IsHost) && clientReady.ContainsKey(clientId))
				{
					clientReady[clientId] = isReady;
				}
			}
		}

		protected override void __initializeVariables()
		{
			((NetworkBehaviour)this).__initializeVariables();
		}

		[RuntimeInitializeOnLoadMethod]
		internal static void InitializeRPCS_AudioManager()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			NetworkManager.__rpc_func_table.Add(2917554051u, new RpcReceiveHandler(__rpc_handler_2917554051));
			NetworkManager.__rpc_func_table.Add(1384767487u, new RpcReceiveHandler(__rpc_handler_1384767487));
		}

		private static void __rpc_handler_2917554051(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager networkManager = target.NetworkManager;
			if (networkManager != null && networkManager.IsListening)
			{
				bool isReady = default(bool);
				((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref isReady, default(ForPrimitives));
				ulong clientId = default(ulong);
				ByteUnpacker.ReadValueBitPacked(reader, ref clientId);
				target.__rpc_exec_stage = (__RpcExecStage)2;
				((AudioManager)(object)target).SetClientReadyClientRpc(isReady, clientId);
				target.__rpc_exec_stage = (__RpcExecStage)0;
			}
		}

		private static void __rpc_handler_1384767487(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager networkManager = target.NetworkManager;
			if (networkManager != null && networkManager.IsListening)
			{
				bool isReady = default(bool);
				((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref isReady, default(ForPrimitives));
				ulong clientId = default(ulong);
				ByteUnpacker.ReadValueBitPacked(reader, ref clientId);
				target.__rpc_exec_stage = (__RpcExecStage)1;
				((AudioManager)(object)target).SetClientReadyServerRpc(isReady, clientId);
				target.__rpc_exec_stage = (__RpcExecStage)0;
			}
		}

		protected internal override string __getTypeName()
		{
			return "AudioManager";
		}
	}
}
namespace LethalGargoyles.src.SoftDepends
{
	internal class CoronerClass
	{
		public static object? GargoyleDeath { get; private set; }

		public static object? GargoylePushDeath { get; private set; }

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void Init()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GargoyleDeath = API.Register("DeathEnemyLGargoyle");
				GargoylePushDeath = API.Register("DeathEnemyGargoylePush");
				Plugin.Logger.LogInfo((object)"Gargoyle causes of death registered with Coroner.");
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Skipping Coroner initialization. Exception: {arg}");
			}
		}

		internal static void CoronerSetCauseOfDeath(PlayerControllerB player, string deathType)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if (!(deathType == "Attack"))
			{
				if (deathType == "Push" && GargoylePushDeath != null)
				{
					SetCauseOfDeath((int)player.playerClientId, (AdvancedCauseOfDeath)GargoylePushDeath);
				}
			}
			else if (GargoyleDeath != null)
			{
				SetCauseOfDeath((int)player.playerClientId, (AdvancedCauseOfDeath)GargoyleDeath);
			}
		}

		internal static void SetCauseOfDeath(int playerId, AdvancedCauseOfDeath cause)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			Type type = typeof(Plugin).Assembly.GetType("Coroner.AdvancedDeathTracker");
			if (type != null)
			{
				MethodInfo method = type.GetMethod("SetCauseOfDeath", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
				{
					typeof(int),
					typeof(AdvancedCauseOfDeath),
					typeof(bool)
				}, null);
				method?.Invoke(null, new object[3] { playerId, cause, true });
				_ = method != null;
			}
		}

		internal static string? CoronerGetCauseOfDeath(PlayerControllerB player)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			AdvancedCauseOfDeath? causeOfDeath = API.GetCauseOfDeath(player);
			if (causeOfDeath.HasValue)
			{
				AdvancedCauseOfDeath value = causeOfDeath.Value;
				return Regex.Replace(((AdvancedCauseOfDeath)(ref value)).GetLanguageTag(), "Death", "");
			}
			return null;
		}
	}
	internal class EmployeeClassesClass
	{
		[Conditional("DEBUG")]
		private static void LogIfDebugBuild(string text)
		{
			Plugin.Logger.LogInfo((object)text);
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static string? GetPlayerClass(PlayerControllerB player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			try
			{
				if (Chainloader.PluginInfos.TryGetValue("Jade.EmployeeClasses", out var value))
				{
					Type type = ((object)value.Instance).GetType().Assembly.GetType("EmployeeClasses.Roles.RoleManager");
					if (type != null)
					{
						Component component = ((Component)player).GetComponent(type);
						if ((Object)(object)component != (Object)null)
						{
							FieldInfo field = type.GetField("selectedRole", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							if (field != null)
							{
								string text = (string)field.GetValue(component);
								if (text == null)
								{
									return null;
								}
								return text;
							}
						}
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Error getting player class from EmployeeClasses: {arg}");
				return null;
			}
			return null;
		}
	}
}
namespace LethalGargoyles.src.Patch
{
	[HarmonyPatch(typeof(DoorLock), "OnTriggerStay")]
	public class HarmonyDoorPatch
	{
		private static readonly FieldInfo enemyDoorMeterField = typeof(DoorLock).GetField("enemyDoorMeter", BindingFlags.Instance | BindingFlags.NonPublic);

		[HarmonyPostfix]
		private static void PostFixOnTriggerStay(DoorLock __instance, Collider other)
		{
			if ((Object)(object)other == (Object)null || (Object)(object)((Component)other).GetComponent<EnemyAICollisionDetect>() == (Object)null || (Object)(object)((Component)other).GetComponent<EnemyAICollisionDetect>().mainScript == (Object)null || !(((Component)other).GetComponent<EnemyAICollisionDetect>().mainScript is LethalGargoylesAI lethalGargoylesAI))
			{
				return;
			}
			if (enemyDoorMeterField != null)
			{
				float num = (float)enemyDoorMeterField.GetValue(__instance);
				if (num <= 0f && (Object)(object)lethalGargoylesAI.currentDoor == (Object)null)
				{
					lethalGargoylesAI.currentDoor = __instance;
					lethalGargoylesAI.lastDoorCloseTime = Time.time;
				}
			}
			else
			{
				Plugin.Logger.LogWarning((object)"enemyDoorMeter field not found in DoorLock.");
			}
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "HitEnemy")]
	public class KillEnemyPatch
	{
		[HarmonyPostfix]
		private static void Postfix(EnemyAI __instance, PlayerControllerB? playerWhoHit)
		{
			if ((Object)(object)playerWhoHit != (Object)null)
			{
				((MonoBehaviour)__instance).StartCoroutine(KillEnemyHelper.KillEnemy(__instance, playerWhoHit));
			}
		}
	}
	public class KillEnemyHelper
	{
		public static IEnumerator KillEnemy(EnemyAI enemyAI, PlayerControllerB playerWhoHit)
		{
			yield return (object)new WaitForSeconds(1f);
			if (enemyAI.isEnemyDead)
			{
				LethalGargoylesAI.PlayerActivityTracker.UpdatePlayerActivity(playerWhoHit, LethalGargoylesAI.PlayerActivityTracker.PlayerActivityType.KilledEnemy, enemyAI.enemyType.enemyName);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "GrabObjectServerRpc")]
	public class GrabObjectServerRpcPatch
	{
		[HarmonyPostfix]
		public static void Postfix(PlayerControllerB __instance, ref NetworkObjectReference grabbedObject)
		{
			NetworkObject val = default(NetworkObject);
			if (!((NetworkBehaviour)__instance).IsServer || !((NetworkObjectReference)(ref grabbedObject)).TryGet(ref val, (NetworkManager)null))
			{
				return;
			}
			GrabbableObject componentInChildren = ((Component)val).GetComponentInChildren<GrabbableObject>();
			if (componentInChildren != null)
			{
				string itemName = componentInChildren.itemProperties.itemName;
				if (LethalGargoylesAI.trackedItems.Contains(itemName))
				{
					LethalGargoylesAI.PlayerActivityTracker.UpdatePlayerActivity(__instance, LethalGargoylesAI.PlayerActivityTracker.PlayerActivityType.PickedUpItem, componentInChildren.itemProperties.itemName);
				}
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "SetObjectAsNoLongerHeld")]
	public class SetObjectAsNoLongerHeldPatch
	{
		[HarmonyPostfix]
		public static void Postfix(PlayerControllerB __instance, GrabbableObject dropObject)
		{
			if (((NetworkBehaviour)__instance).IsServer)
			{
				string itemName = dropObject.itemProperties.itemName;
				if (LethalGargoylesAI.trackedItems.Contains(itemName))
				{
					LethalGargoylesAI.PlayerActivityTracker.RemoveActivity(__instance, LethalGargoylesAI.PlayerActivityTracker.PlayerActivityType.PickedUpItem, itemName);
				}
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "Update")]
	public class PlayerInFacilityPatch
	{
		private static readonly Dictionary<PlayerControllerB, float> playerEnterTimes = new Dictionary<PlayerControllerB, float>();

		private static readonly Dictionary<PlayerControllerB, float> lastRanTimes = new Dictionary<PlayerControllerB, float>();

		private const float MinimumTimeInFacility = 5f;

		private const float delay = 1f;

		[HarmonyPostfix]
		private static void Postfix(PlayerControllerB __instance)
		{
			float value;
			float num = (lastRanTimes.TryGetValue(__instance, out value) ? value : 0f);
			if (!(Time.time - num > 1f))
			{
				return;
			}
			if (__instance.isPlayerControlled && __instance.isInsideFactory && !__instance.isPlayerDead)
			{
				if (!playerEnterTimes.ContainsKey(__instance))
				{
					playerEnterTimes[__instance] = Time.time;
				}
				else
				{
					float num2 = playerEnterTimes[__instance];
					float num3 = Time.time - num2;
					if (num3 >= 300f)
					{
						LethalGargoylesAI.PlayerActivityTracker.UpdatePlayerActivity(__instance, LethalGargoylesAI.PlayerActivityTracker.PlayerActivityType.InFacility, "InFacilityTime", num3);
					}
				}
			}
			else
			{
				if (playerEnterTimes.ContainsKey(__instance))
				{
					playerEnterTimes.Remove(__instance);
				}
				LethalGargoylesAI.PlayerActivityTracker.RemoveActivity(__instance, LethalGargoylesAI.PlayerActivityTracker.PlayerActivityType.InFacility);
			}
			lastRanTimes[__instance] = Time.time;
		}
	}
	[HarmonyPatch]
	public class NetworkObjectManager
	{
		public static GameObject? networkPrefab;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameNetworkManager), "Start")]
		public static void Init()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			if (!((Object)(object)networkPrefab != (Object)null) && (Object)(object)Plugin.ModAssets != (Object)null)
			{
				networkPrefab = (GameObject)Plugin.ModAssets.LoadAsset("LGNetworkHandler");
				networkPrefab.AddComponent<AudioManager>();
				NetworkManager.Singleton.AddNetworkPrefab(networkPrefab);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		public static void LoadClipsHostPostFix()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (NetworkManager.Singleton.IsHost && (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer))
			{
				GameObject val = Object.Instantiate<GameObject>(networkPrefab, Vector3.zero, Quaternion.identity);
				if (val != null)
				{
					val.GetComponent<NetworkObject>().Spawn(false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	public static class GetDeathCauses
	{
		public static List<(string playerName, string causeOfDeath, string source)> previousRoundDeaths = new List<(string, string, string)>();

		[HarmonyPostfix]
		[HarmonyPatch("WritePlayerNotes")]
		private static void PostFixWritePlayerNotes()
		{
			Plugin.Logger.LogInfo((object)"Getting Causes of Death.");
			previousRoundDeaths.Clear();
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (val.isPlayerDead)
				{
					if (Plugin.Instance.IsCoronerLoaded)
					{
						string text = ((object)(CauseOfDeath)(ref val.causeOfDeath)).ToString() ?? "Unknown";
						Plugin.Logger.LogInfo((object)("Vanilla caught " + val.playerUsername + "'s cause of death this round was " + text));
						previousRoundDeaths.Add((val.playerUsername, text, "Vanilla"));
						text = CoronerClass.CoronerGetCauseOfDeath(val) ?? "Unknown";
						Plugin.Logger.LogInfo((object)("Coroner caught " + val.playerUsername + "'s cause of death this round was " + text));
						previousRoundDeaths.Add((val.playerUsername, text, "Coroner"));
					}
					else
					{
						string text = ((object)(CauseOfDeath)(ref val.causeOfDeath)).ToString() ?? "Unknown";
						previousRoundDeaths.Add((val.playerUsername, text, "Vanilla"));
						Plugin.Logger.LogInfo((object)("Vanilla caught " + val.playerUsername + "'s cause of death this round was " + text));
					}
				}
			}
		}
	}
}
namespace LethalGargoyles.src.Enemy
{
	public class LethalGargoylesAI : EnemyAI
	{
		public static class PlayerActivityTracker
		{
			public enum PlayerActivityType
			{
				KilledEnemy,
				PickedUpItem,
				InFacility
			}

			public class ActivityData
			{
				public string? Data { get; set; }

				public float TimeValue { get; set; }

				public float LastActivityTime { get; set; }
			}

			private static readonly Dictionary<PlayerControllerB, Dictionary<PlayerActivityType, ActivityData>> playerActivities = new Dictionary<PlayerControllerB, Dictionary<PlayerActivityType, ActivityData>>();

			private static readonly Dictionary<PlayerControllerB, Dictionary<string, float>> playerTauntTimers = new Dictionary<PlayerControllerB, Dictionary<string, float>>();

			public static void UpdatePlayerActivity(PlayerControllerB player, PlayerActivityType activityType, string? data = null, float timeValue = 0f)
			{
				if (!playerActivities.ContainsKey(player))
				{
					playerActivities[player] = new Dictionary<PlayerActivityType, ActivityData>();
				}
				playerActivities[player][activityType] = new ActivityData
				{
					Data = data,
					TimeValue = timeValue,
					LastActivityTime = Time.time
				};
			}

			public static ActivityData GetPlayerActivity(PlayerControllerB player, PlayerActivityType activityType)
			{
				if (playerActivities.TryGetValue(player, out Dictionary<PlayerActivityType, ActivityData> value) && value.TryGetValue(activityType, out var value2))
				{
					return value2;
				}
				return new ActivityData
				{
					Data = null,
					TimeValue = 0f,
					LastActivityTime = 0f
				};
			}

			public static void RemoveActivity(PlayerControllerB player, PlayerActivityType activityType, string? dataValue = null)
			{
				if (!playerActivities.TryGetValue(player, out Dictionary<PlayerActivityType, ActivityData> value) || !value.ContainsKey(activityType))
				{
					return;
				}
				if (dataValue != null)
				{
					if (value[activityType].Data == dataValue)
					{
						value.Remove(activityType);
					}
				}
				else
				{
					value.Remove(activityType);
				}
				if (value.Count == 0)
				{
					playerActivities.Remove(player);
				}
			}

			public static float GetPlayerTauntTimer(PlayerControllerB player, string timerName)
			{
				if (!playerTauntTimers.TryGetValue(player, out Dictionary<string, float> value))
				{
					value = new Dictionary<string, float>
					{
						{
							"lastLostTauntTime",
							Time.time - 61f
						},
						{
							"lastGrabTauntTime",
							Time.time - 61f
						},
						{
							"lastKillTauntTime",
							Time.time - 61f
						}
					};
					playerTauntTimers[player] = value;
				}
				if (!value.TryGetValue(timerName, out var value2))
				{
					value2 = (value[timerName] = Time.time - 61f);
				}
				return value2;
			}

			public static void UpdatePlayerTauntTimer(PlayerControllerB player, string timerName)
			{
				if (playerTauntTimers.ContainsKey(player))
				{
					playerTauntTimers[player][timerName] = Time.time;
				}
			}
		}

		private enum State
		{
			SearchingForPlayer,
			StealthyPursuit,
			GetOutOfSight,
			AggressivePursuit,
			Idle,
			PushTarget
		}

		public enum RelativeZone
		{
			Front,
			FrontRight,
			Right,
			BackRight,
			Back,
			BackLeft,
			Left,
			FrontLeft
		}

		public static readonly HashSet<string> trackedItems = new HashSet<string> { "Key", "Apparatus", "Comedy", "Tragedy", "Maneater" };

		public Transform turnCompass;

		public Transform attackArea;

		public DoorLock? currentDoor;

		public float lastDoorCloseTime;

		private PlayerControllerB closestPlayer;

		private PlayerControllerB aggroPlayer;

		private float randGenTauntTime;

		private float randAgrTauntTime;

		private float randEnemyTauntTime;

		private float lastGenTauntTime;

		private float lastAgrTauntTime;

		private float lastEnemyTauntTime;

		private float lastSteamIDTauntTime;

		private static int lastGenTaunt = -1;

		private static int lastAgrTaunt = -1;

		private int genTauntCount;

		protected static ConcurrentDictionary<int, PlayerControllerB?> gargoyleTargets = new ConcurrentDictionary<int, PlayerControllerB>();

		protected static ConcurrentDictionary<PlayerControllerB, ConcurrentDictionary<int, bool>> playerPushStates = new ConcurrentDictionary<PlayerControllerB, ConcurrentDictionary<int, bool>>();

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

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

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

		private static readonly List<Transform> cachedKillTriggers = new List<Transform>();

		private static readonly List<Transform> cachedRailings = new List<Transform>();

		private static readonly List<LethalGargoylesAI> activeGargoyles = new List<LethalGargoylesAI>();

		private static readonly Dictionary<RelativeZone, float> bufferDistances = new Dictionary<RelativeZone, float>
		{
			{
				RelativeZone.Front,
				15f
			},
			{
				RelativeZone.FrontRight,
				12f
			},
			{
				RelativeZone.Right,
				10f
			},
			{
				RelativeZone.BackRight,
				6f
			},
			{
				RelativeZone.Back,
				3f
			},
			{
				RelativeZone.BackLeft,
				6f
			},
			{
				RelativeZone.Left,
				10f
			},
			{
				RelativeZone.FrontLeft,
				12f
			}
		};

		private static readonly Dictionary<RelativeZone, Vector3> RelativeZones = new Dictionary<RelativeZone, Vector3>();

		public AISearchRoutine? searchForPlayers;

		public int myID;

		private static float lastNodeCheckTime = 0f;

		private readonly float nodeCheckInterval = 5f;

		private float lastAttackTime;

		private float distanceToPlayerSqr;

		private float distanceToClosestPlayerSqr;

		private string? lastEnemy;

		private bool isSeen;

		private bool canSeePlayer;

		private bool targetSeesGargoyle;

		private float pushTimer;

		private int pushStage;

		private float targetTimer;

		private RelativeZone currentZone;

		private RelativeZone nextZoneRight;

		private RelativeZone nextZoneLeft;

		private float leftPathDist;

		private float rightPathDist;

		private static int lastGargoyleToSwitch = 0;

		private float playerCheckTimer;

		private float pathDelayTimer;

		private readonly List<PlayerControllerB> validPlayers = new List<PlayerControllerB>();

		private readonly List<LethalGargoylesAI> gargoyles = new List<LethalGargoylesAI>();

		private int previousStateIndex;

		private Transform? killTrigger;

		private float distToKillTriggerSqr;

		private readonly Dictionary<PlayerControllerB, string> playerClasses = new Dictionary<PlayerControllerB, string>();

		private float lastSeenCheckTime;

		private float baseSpeed;

		private float attackRangeSqr;

		private int attackDamage;

		private float aggroRangeSqr;

		private int minTaunt;

		private int maxTaunt;

		private float distWarnSqr;

		private float bufferDistSqr;

		private float awareDistSqr;

		private float idleDistanceSqr;

		private bool enablePush;

		public static LethalGargoylesAI? LGInstance { get; private set; }

		private string StateToString(int state)
		{
			return state switch
			{
				0 => "SearchingForPlayer", 
				1 => "StealthyPusuit", 
				2 => "GetOutOfSight", 
				3 => "AggressivePursuit", 
				4 => "Idle", 
				5 => "PushTarget", 
				_ => "Unknown", 
			};
		}

		[Conditional("DEBUG")]
		private void LogIfDebugBuild(string text)
		{
			Plugin.Logger.LogInfo((object)text);
		}

		public override void Start()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			((EnemyAI)this).Start();
			LGInstance = this;
			DoAnimationClientRpc("startWalk");
			((EnemyAI)this).SwitchToBehaviourClientRpc(0);
			((EnemyAI)this).StartSearch(((Component)this).transform.position, (AISearchRoutine)null);
			baseSpeed = Plugin.BoundConfig.baseSpeed.Value;
			attackDamage = Plugin.BoundConfig.attackDamage.Value;
			minTaunt = Plugin.BoundConfig.minTaunt.Value;
			maxTaunt = Plugin.BoundConfig.maxTaunt.Value;
			attackRangeSqr = Plugin.BoundConfig.attackRange.Value;
			attackRangeSqr *= attackRangeSqr;
			aggroRangeSqr = Plugin.BoundConfig.aggroRange.Value;
			aggroRangeSqr *= aggroRangeSqr;
			distWarnSqr = Plugin.BoundConfig.distWarn.Value;
			distWarnSqr *= distWarnSqr;
			idleDistanceSqr = Plugin.BoundConfig.idleDistance.Value;
			idleDistanceSqr *= idleDistanceSqr;
			bufferDistSqr = Plugin.BoundConfig.bufferDist.Value;
			bufferDistSqr *= bufferDistSqr;
			awareDistSqr = Plugin.BoundConfig.awareDist.Value;
			awareDistSqr *= awareDistSqr;
			enablePush = Plugin.BoundConfig.enablePush.Value;
			lastAttackTime = Time.time;
			pushTimer = Time.time;
			myID = ((Object)base.agent).GetInstanceID();
			gargoyleTargets[myID] = base.targetPlayer;
			AudioSource creatureVoice = base.creatureVoice;
			creatureVoice.maxDistance *= 3f;
			pathDelayTimer = Time.time;
			lastSteamIDTauntTime = Time.time - 91f;
			if (cachedOutsideAINodes.Count > 0)
			{
				cachedOutsideAINodes.Clear();
			}
			GameObject[] outsideAINodes = RoundManager.Instance.outsideAINodes;
			foreach (GameObject val in outsideAINodes)
			{
				if ((Object)(object)val != (Object)null)
				{
					cachedOutsideAINodes.Add(val);
				}
			}
			if (cachedInsideAINodes.Count > 0)
			{
				cachedInsideAINodes.Clear();
			}
			GameObject[] insideAINodes = RoundManager.Instance.insideAINodes;
			foreach (GameObject val2 in insideAINodes)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					cachedInsideAINodes.Add(val2);
				}
			}
			if (cachedAllAINodes.Count > 0)
			{
				cachedAllAINodes.Clear();
			}
			GameObject[] allAINodes = base.allAINodes;
			foreach (GameObject val3 in allAINodes)
			{
				if ((Object)(object)val3 != (Object)null)
				{
					cachedAllAINodes.Add(val3);
				}
			}
			CacheKillTriggers();
			playerClasses.Clear();
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val4 in allPlayerScripts)
			{
				playerClasses[val4] = EmployeeClassesClass.GetPlayerClass(val4) ?? "Employee";
			}
			activeGargoyles.Add(this);
		}

		public override void Update()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			((EnemyAI)this).Update();
			if (base.isEnemyDead || StartOfRound.Instance.allPlayersDead)
			{
				return;
			}
			gargoyleTargets[myID] = base.targetPlayer;
			if (Time.time - lastNodeCheckTime > nodeCheckInterval)
			{
				CheckAndRefreshAINodes();
				lastNodeCheckTime = Time.time;
			}
			HandleTargetPlayer();
			if (Time.time - lastSeenCheckTime > 0.2f)
			{
				closestPlayer = ((EnemyAI)this).GetClosestPlayer(false, false, false);
				float num;
				if (!((Object)(object)closestPlayer != (Object)null))
				{
					num = 0f;
				}
				else
				{
					Vector3 val = ((Component)this).transform.position - ((Component)closestPlayer).transform.position;
					num = ((Vector3)(ref val)).sqrMagnitude;
				}
				distanceToClosestPlayerSqr = num;
				isSeen = GargoyleIsSeen(((Component)this).transform);
				lastSeenCheckTime = Time.time;
			}
			HandlePushStage();
			HandleBehaviorState();
		}

		private void HandleTargetPlayer()
		{
			//IL_001d: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)base.targetPlayer == (Object)null)
			{
				((EnemyAI)this).SwitchToBehaviourClientRpc(0);
				return;
			}
			Vector3 val = ((Component)this).transform.position - ((Component)base.targetPlayer).transform.position;
			distanceToPlayerSqr = ((Vector3)(ref val)).sqrMagnitude;
			if (base.currentBehaviourStateIndex != 5)
			{
				ResetPushStage();
			}
			if (!base.isOutside != base.targetPlayer.isInsideFactory || !base.targetPlayer.isPlayerControlled || base.targetPlayer.isPlayerDead || distanceToPlayerSqr > awareDistSqr)
			{
				base.targetPlayer = null;
				((EnemyAI)this).SwitchToBehaviourClientRpc(0);
			}
		}

		private void HandleBehaviorState()
		{
			switch (base.currentBehaviourStateIndex)
			{
			case 4:
				HandleIdleState();
				break;
			case 0:
				HandleSearchingForPlayerState();
				break;
			case 1:
				HandleStealthyPursuitState();
				break;
			case 3:
				HandleAggressivePursuitState();
				break;
			case 2:
				HandleGetOutOfSightState();
				break;
			case 5:
				HandlePushTargetState();
				break;
			}
		}

		private void ResetPushStage()
		{
			pushStage = 0;
			ConcurrentDictionary<int, bool> orAdd = playerPushStates.GetOrAdd(base.targetPlayer, new ConcurrentDictionary<int, bool>());
			orAdd[myID] = false;
			foreach (KeyValuePair<PlayerControllerB, ConcurrentDictionary<int, bool>> playerPushState in playerPushStates)
			{
				if (!playerPushState.Key.playerUsername.Equals(base.targetPlayer.playerUsername))
				{
					playerPushState.Value.TryRemove(myID, out var _);
				}
			}
		}

		private void HandlePushStage()
		{
			if (pushStage < 1 && distanceToClosestPlayerSqr <= awareDistSqr)
			{
				HandleAggroAndPush();
			}
		}

		private void HandleOutOfAggroRange()
		{
			if (isSeen)
			{
				((EnemyAI)this).SwitchToBehaviourClientRpc(2);
			}
			else if (distanceToPlayerSqr <= idleDistanceSqr && (Object)(object)base.targetPlayer != (Object)null)
			{
				((EnemyAI)this).SwitchToBehaviourClientRpc(4);
			}
			else if ((Object)(object)base.targetPlayer != (Object)null)
			{
				((EnemyAI)this).SwitchToBehaviourClientRpc(1);
			}
			else if (base.currentBehaviourStateIndex != 0)
			{
				((EnemyAI)this).SwitchToBehaviourClientRpc(0);
			}
		}

		private void HandleAggroAndPush()
		{
			if (distanceToClosestPlayerSqr > aggroRangeSqr)
			{
				randAgrTauntTime = Time.time - lastAgrTauntTime;
			}
			if (distanceToClosestPlayerSqr <= aggroRangeSqr && isSeen)
			{
				((EnemyAI)this).SwitchToBehaviourClientRpc(3);
			}
			else if (distanceToClosestPlayerSqr <= attackRangeSqr && !isSeen && (Object)(object)closestPlayer != (Object)null && base.currentBehaviourStateIndex != 3 && enablePush)
			{
				PushPlayer(closestPlayer);
			}
			else if (distanceToClosestPlayerSqr > aggroRangeSqr)
			{
				HandleOutOfAggroRange();
			}
			if (!targetSeesGargoyle && (Object)(object)base.targetPlayer != (Object)null && base.currentBehaviourStateIndex != 3 && Time.time > pushTimer && enablePush && distToKillTriggerSqr <= 4f)
			{
				HandlePushTarget();
			}
		}

		private void HandlePushTarget()
		{
			lock (playerPushStates)
			{
				if (playerPushStates.TryGetValue(base.targetPlayer, out ConcurrentDictionary<int, bool> value) && !value.Any((KeyValuePair<int, bool> kvp) => kvp.Key != myID && kvp.Value))
				{
					playerPushStates.GetOrAdd(base.targetPlayer, new ConcurrentDictionary<int, bool>())[myID] = true;
					((EnemyAI)this).SwitchToBehaviourClientRpc(5);
				}
				else
				{
					pushTimer = Time.time + 10f;
				}
			}
		}

		private void HandleIdleState()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			base.agent.speed = 0f;
			base.agent.angularSpeed = 140f;
			base.creatureSFX.volume = 0f;
			base.agent.stoppingDistance = 0.1f;
			if ((Object)(object)base.targetPlayer != (Object)null)
			{
				LookAtTarget(((Component)base.targetPlayer).transform.position);
				if (Time.time - lastGenTauntTime >= randGenTauntTime)
				{
					Taunt();
				}
				else if (Time.time - lastEnemyTauntTime >= randEnemyTauntTime)
				{
					EnemyTaunt();
				}
			}
		}

		private void HandleSearchingForPlayerState()
		{
			base.agent.speed = baseSpeed * 1.5f;
			base.agent.angularSpeed = 250f;
			base.creatureSFX.volume = 1f;
			base.agent.stoppingDistance = 0.2f;
			SearchForPlayers();
		}

		private void HandleStealthyPursuitState()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			base.agent.speed = baseSpeed;
			base.agent.angularSpeed = 140f;
			base.creatureSFX.volume = 0.5f;
			base.agent.stoppingDistance = 0.1f;
			if ((Object)(object)base.targetPlayer != (Object)null)
			{
				if (!SetDestinationToHiddenPosition())
				{
					((EnemyAI)this).SetDestinationToPosition(((Component)base.targetPlayer).transform.position, false);
				}
				if (Time.time - lastGenTauntTime >= randGenTauntTime)
				{
					Taunt();
				}
				else if (Time.time - lastEnemyTauntTime >= randEnemyTauntTime)
				{
					EnemyTaunt();
				}
			}
			else
			{
				((EnemyAI)this).SwitchToBehaviourClientRpc(0);
			}
		}

		private void HandleAggressivePursuitState()
		{
			//IL_00c4: 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)
			base.agent.speed = baseSpeed * 1.8f;
			base.creatureSFX.volume = 1.7f;
			base.agent.angularSpeed = 180f;
			base.agent.stoppingDistance = 0.1f;
			if ((Object)(object)closestPlayer != (Object)null)
			{
				aggroPlayer = closestPlayer;
				canSeePlayer = CanSeePlayer(aggroPlayer);
				bool flag = GargoyleIsTalking();
				if (Time.time - lastAgrTauntTime >= randAgrTauntTime && !flag)
				{
					OtherTaunt("aggro", ref lastAgrTaunt, ref lastAgrTauntTime, ref randAgrTauntTime);
				}
				LookAtTarget(((Component)aggroPlayer).transform.position);
				((EnemyAI)this).SetDestinationToPosition(((Component)aggroPlayer).transform.position, false);
				if (Time.time - lastAttackTime >= 1f && canSeePlayer && attackRangeSqr >= distanceToClosestPlayerSqr)
				{
					AttackPlayer(aggroPlayer);
				}
			}
		}

		private void HandleGetOutOfSightState()
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			base.agent.speed = baseSpeed * 1.5f;
			base.agent.angularSpeed = 250f;
			base.creatureSFX.volume = 1f;
			base.agent.stoppingDistance = 0.2f;
			if ((Object)(object)base.targetPlayer != (Object)null)
			{
				bool flag = SetDestinationToHiddenPosition();
				if (Time.time - lastGenTauntTime >= randGenTauntTime)
				{
					Taunt();
				}
				if (!flag)
				{
					((EnemyAI)this).SetDestinationToPosition(((Component)base.targetPlayer).transform.position, false);
				}
			}
		}

		private void HandlePushTargetState()
		{
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			base.agent.speed = baseSpeed * 2.5f;
			base.creatureSFX.volume = 1.7f;
			base.agent.angularSpeed = 500f;
			base.agent.stoppingDistance = 0.3f;
			if (!((Object)(object)base.targetPlayer != (Object)null))
			{
				return;
			}
			canSeePlayer = CanSeePlayer(base.targetPlayer);
			if (distanceToPlayerSqr <= attackRangeSqr && (!targetSeesGargoyle || pushStage == 1))
			{
				PushPlayer(base.targetPlayer);
				pushStage = 0;
				pushTimer = Time.time + 45f;
				if (playerPushStates.TryGetValue(base.targetPlayer, out ConcurrentDictionary<int, bool> value))
				{
					value[myID] = false;
				}
				((EnemyAI)this).SwitchToBehaviourClientRpc(1);
			}
			if (pushStage < 1)
			{
				if ((double)distanceToPlayerSqr <= (double)aggroRangeSqr * 1.5 && !targetSeesGargoyle && canSeePlayer)
				{
					pushStage = 1;
					((EnemyAI)this).SetDestinationToPosition(((Component)base.targetPlayer).transform.position, false);
				}
			}
			else
			{
				((EnemyAI)this).SetDestinationToPosition(((Component)base.targetPlayer).transform.position, false);
			}
		}

		public override void DoAIInterval()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: 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_00ec: 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)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			((EnemyAI)this).DoAIInterval();
			if (base.isEnemyDead || StartOfRound.Instance.allPlayersDead)
			{
				return;
			}
			_ = base.currentBehaviourStateIndex;
			_ = previousStateIndex;
			previousStateIndex = base.currentBehaviourStateIndex;
			if ((Object)(object)base.targetPlayer != (Object)null)
			{
				killTrigger = FindNearestKillTrigger(((Component)base.targetPlayer).transform.position);
				if (Time.time - playerCheckTimer > 3f)
				{
					ChangeTarget();
					playerCheckTimer = Time.time;
				}
			}
			if ((Object)(object)LGInstance != (Object)null && (Object)(object)currentDoor != (Object)null && Time.time - lastDoorCloseTime >= 0.75f && !currentDoor.isLocked && ((Component)currentDoor).GetComponent<AnimatedObjectTrigger>().boolValue)
			{
				Vector3 val = ((Component)currentDoor).transform.position - ((Component)this).transform.position;
				if (((Vector3)(ref val)).sqrMagnitude > ((base.currentBehaviourStateIndex == 4) ? 8f : 16f))
				{
					((MonoBehaviour)this).StartCoroutine(DelayDoorClose(currentDoor));
					currentDoor = null;
				}
			}
			switch (base.currentBehaviourStateIndex)
			{
			case 0:
				if (FoundClosestPlayerInRange())
				{
					((EnemyAI)this).StopSearch(base.currentSearch, true);
					((EnemyAI)this).SwitchToBehaviourClientRpc(1);
				}
				if (base.agent.hasPath)
				{
					DoAnimationClientRpc("startWalk");
				}
				else
				{
					DoAnimationClientRpc("startIdle");
				}
				break;
			case 1:
			case 2:
				if (base.agent.hasPath)
				{
					DoAnimationClientRpc("startWalk");
				}
				else
				{
					DoAnimationClientRpc("startIdle");
				}
				break;
			case 4:
				DoAnimationClientRpc("startIdle");
				break;
			case 3:
				if (base.agent.hasPath)
				{
					DoAnimationClientRpc("startChase");
				}
				else
				{
					DoAnimationClientRpc("startIdle");
				}
				break;
			case 5:
				if ((Time.time - targetTimer > 0.5f || !base.agent.hasPath) && (Object)(object)base.targetPlayer != (Object)null)
				{
					if (distanceToPlayerSqr <= idleDistanceSqr)
					{
						Vector3 targetPosition = GetTargetPosition(base.targetPlayer);
						((EnemyAI)this).SetDestinationToPosition(targetPosition, true);
					}
					else
					{
						((EnemyAI)this).SetDestinationToPosition(((Component)base.targetPlayer).transform.position, false);
					}
					targetTimer = Time.time;
				}
				if (base.agent.hasPath)
				{
					DoAnimationClientRpc("startChase");
				}
				else
				{
					DoAnimationClientRpc("startIdle");
				}
				break;
			}
		}

		private void CacheKillTriggers()
		{
			GameObject[] array = Object.FindObjectsOfType<GameObject>();
			cachedKillTriggers.Clear();
			GameObject[] array2 = array;
			BoxCollider val2 = default(BoxCollider);
			foreach (GameObject val in array2)
			{
				if (((Object)val).name.StartsWith("KillTrigger") && val.TryGetComponent<BoxCollider>(ref val2))
				{
					cachedKillTriggers.Add(val.transform);
				}
			}
		}

		private void CheckAndRefreshAINodes()
		{
			RefreshNodesIfNull(cachedOutsideAINodes, RoundManager.Instance.outsideAINodes, "outside");
			RefreshNodesIfNull(cachedInsideAINodes, RoundManager.Instance.insideAINodes, "inside");
			RefreshNodesIfNull(cachedAllAINodes, base.allAINodes, "all");
		}

		private void RefreshNodesIfNull(List<GameObject> cachedNodes, IEnumerable<GameObject> sourceNodes, string nodeType)
		{
			if (!cachedNodes.Any((GameObject node) => (Object)(object)node == (Object)null) || !sourceNodes.Any())
			{
				return;
			}
			cachedNodes.Clear();
			foreach (GameObject sourceNode in sourceNodes)
			{
				if ((Object)(object)sourceNode != (Object)null)
				{
					cachedNodes.Add(sourceNode);
				}
			}
		}

		private Transform? FindNearestKillTrigger(Vector3 playerPosition)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: 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_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: 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_00f2: 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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			Transform val = null;
			float num = float.MaxValue;
			float num2 = float.MaxValue;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(playerPosition.x, playerPosition.z);
			Vector2 val5 = default(Vector2);
			Bounds bounds;
			foreach (Transform cachedKillTrigger in cachedKillTriggers)
			{
				if (!((Object)(object)cachedKillTrigger != (Object)null))
				{
					continue;
				}
				Vector3 val3 = playerPosition - cachedKillTrigger.position;
				float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
				if (!(sqrMagnitude <= num2))
				{
					continue;
				}
				BoxCollider component = ((Component)cachedKillTrigger).GetComponent<BoxCollider>();
				Vector3 val4 = ((Collider)component).ClosestPointOnBounds(playerPosition);
				((Vector2)(ref val5))..ctor(val4.x, val4.z);
				if (!(cachedKillTrigger.position.y < playerPosition.y))
				{
					continue;
				}
				float num3 = Mathf.Abs(val5.x - val2.x);
				bounds = ((Collider)component).bounds;
				if (!(num3 < ((Bounds)(ref bounds)).extents.x + 1f))
				{
					continue;
				}
				float num4 = Mathf.Abs(val5.y - val2.y);
				bounds = ((Collider)component).bounds;
				if (num4 < ((Bounds)(ref bounds)).extents.z + 1f)
				{
					Vector2 val6 = val5 - val2;
					float sqrMagnitude2 = ((Vector2)(ref val6)).sqrMagnitude;
					if (sqrMagnitude2 < num)
					{
						num = sqrMagnitude2;
						val = cachedKillTrigger;
					}
				}
			}
			distToKillTriggerSqr = num;
			if ((Object)(object)val != (Object)null)
			{
				BoxCollider component2 = ((Component)val).GetComponent<BoxCollider>();
				float x = playerPosition.x;
				bounds = ((Collider)component2).bounds;
				Vector3 val7 = default(Vector3);
				((Vector3)(ref val7))..ctor(x, ((Bounds)(ref bounds)).center.y, playerPosition.z);
				bounds = ((Collider)component2).bounds;
				if (((Bounds)(ref bounds)).Contains(val7))
				{
					distToKillTriggerSqr = 0f;
				}
			}
			return val;
		}

		private Transform? FindNearestRailing(Vector3 position)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			Transform result = null;
			float num = float.MaxValue;
			Collider[] array = Physics.OverlapSphere(position, 2f, 1 << LayerMask.NameToLayer("Railing"));
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				Vector3 val2 = position - ((Component)val).transform.position;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					num = sqrMagnitude;
					result = ((Component)val).transform;
				}
			}
			return result;
		}

		private IEnumerator DelayDoorClose(DoorLock door)
		{
			yield return (object)new WaitForSeconds(0.1f);
			if ((Object)(object)LGInstance != (Object)null)
			{
				AnimatedObjectTrigger component = ((Component)door).gameObject.GetComponent<AnimatedObjectTrigger>();
				component.TriggerAnimationNonPlayer(true, true, false);
			}
			door.CloseDoorNonPlayerServerRpc();
		}

		private EnemyAI? EnemyNearGargoyle()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)base.targetPlayer != (Object)null)
			{
				foreach (EnemyAI spawnedEnemy in RoundManager.Instance.SpawnedEnemies)
				{
					Vector3 val = ((Component)spawnedEnemy).transform.position - ((Component)this).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude <= distWarnSqr)
					{
						return spawnedEnemy;
					}
				}
			}
			return null;
		}

		private Dictionary<PlayerControllerB, int> GetGargoyleTargetCounts()
		{
			UpdateValidPlayersAndGargoyles();
			Dictionary<PlayerControllerB, int> dictionary = new Dictionary<PlayerControllerB, int>();
			foreach (PlayerControllerB validPlayer in validPlayers)
			{
				dictionary[validPlayer] = 0;
			}
			foreach (LethalGargoylesAI gargoyle in gargoyles)
			{
				if (gargoyleTargets.TryGetValue(gargoyle.myID, out PlayerControllerB value) && (Object)(object)value != (Object)null && validPlayers.Contains(value))
				{
					dictionary[value]++;
				}
			}
			return dictionary;
		}

		private bool FoundClosestPlayerInRange()
		{
			Dictionary<PlayerControllerB, int> gargoyleTargetCounts = GetGargoyleTargetCounts();
			int fairShare = Mathf.CeilToInt((float)gargoyles.Count / (float)validPlayers.Count);
			if ((Object)(object)base.targetPlayer != (Object)null && gargoyleTargetCounts.ContainsKey(base.targetPlayer) && gargoyleTargetCounts[base.targetPlayer] > 1 && validPlayers.Count > 1)
			{
				PlayerControllerB val = FindBestTarget(gargoyleTargetCounts, fairShare);
				if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)base.targetPlayer)
				{
					gargoyleTargets[myID] = val;
					base.targetPlayer = val;
				}
			}
			else
			{
				base.targetPlayer = null;
			}
			if ((Object)(object)base.targetPlayer == (Object)null)
			{
				base.targetPlayer = FindBestTarget(gargoyleTargetCounts, fairShare);
				if ((Object)(object)base.targetPlayer != (Object)null)
				{
					return true;
				}
				return false;
			}
			return true;
		}

		private void ChangeTarget()
		{
			Dictionary<PlayerControllerB, int> gargoyleTargetCounts = GetGargoyleTargetCounts();
			int num = Mathf.CeilToInt((float)gargoyles.Count / (float)validPlayers.Count);
			bool flag = false;
			foreach (KeyValuePair<PlayerControllerB, int> item in gargoyleTargetCounts)
			{
				if (item.Value > num)
				{
					flag = true;
					break;
				}
			}
			if (!((Object)(object)base.targetPlayer != (Object)null && gargoyleTargets.ContainsKey(myID) && (Object)(object)gargoyleTargets[myID] == (Object)(object)base.targetPlayer && gargoyleTargetCounts.ContainsKey(base.targetPlayer) && gargoyleTargetCounts[base.targetPlayer] > num && validPlayers.Count > 1 && flag))
			{
				return;
			}
			List<int> list = new List<int>(from g in gargoyles
				select g.myID into id
				orderby id
				select id);
			int num2 = list.IndexOf(myID);
			if (num2 == (lastGargoyleToSwitch = (lastGargoyleToSwitch + 1) % list.Count))
			{
				PlayerControllerB val = FindBestTarget(gargoyleTargetCounts, num);
				if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)base.targetPlayer)
				{
					gargoyleTargets[myID] = val;
					base.targetPlayer = val;
				}
			}
		}

		private PlayerControllerB? FindBestTarget(Dictionary<PlayerControllerB, int> targetCounts, int fairShare)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB result = null;
			int num = int.MaxValue;
			float num2 = awareDistSqr;
			foreach (KeyValuePair<PlayerControllerB, int> targetCount in targetCounts)
			{
				if (targetCount.Value >= fairShare)
				{
					continue;
				}
				Vector3 val = ((Component)this).transform.position - ((Component)targetCount.Key).transform.position;
				if (((Vector3)(ref val)).sqrMagnitude <= awareDistSqr)
				{
					val = ((Component)this).transform.position - ((Component)targetCount.Key).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (targetCount.Value < num || (targetCount.Value == num && sqrMagnitude < num2))
					{
						num = targetCount.Value;
						num2 = sqrMagnitude;
						result = targetCount.Key;
					}
				}
			}
			return result;
		}

		private void UpdateValidPlayersAndGargoyles()
		{
			validPlayers.Clear();
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!val.isPlayerDead && val.isInsideFactory == !base.isOutside)
				{
					validPlayers.Add(val);
				}
			}
			gargoyles.Clear();
			foreach (EnemyAI spawnedEnemy in RoundManager.Instance.SpawnedEnemies)
			{
				if (spawnedEnemy is LethalGargoylesAI lethalGargoylesAI && ((EnemyAI)lethalGargoylesAI).isOutside == base.isOutside)
				{
					gargoyles.Add(lethalGargoylesAI);
				}
			}
		}

		public void SearchForPlayers()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (searchForPlayers != null && !searchForPlayers.inProgress)
			{
				((EnemyAI)this).StartSearch(((Component)this).transform.position, searchForPlayers);
			}
		}

		private bool SetDestinationToHiddenPosition()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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_009f: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time - pathDelayTimer < 2f && base.agent.hasPath)
			{
				return true;
			}
			if (distanceToPlayerSqr > idleDistanceSqr)
			{
				((EnemyAI)this).SetDestinationToPosition(ChooseClosestNodeToPos(((Component)base.targetPlayer).transform.position, avoidLineOfSight: true), true);
				return true;
			}
			List<Vector3> list = FindCoverPointsAroundTarget();
			PlayerControllerB targetPlayer = base.targetPlayer;
			Transform val = ((targetPlayer != null) ? ((Component)targetPlayer).transform : null);
			if (list.Count == 0 || (Object)(object)val == (Object)null)
			{
				return false;
			}
			Vector3 val2 = default(Vector3);
			float num = awareDistSqr;
			Vector3 val3;
			foreach (Vector3 item in list)
			{
				val3 = val.position - item;
				float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
				if (sqrMagnitude >= bufferDistSqr && sqrMagnitude < num)
				{
					val2 = item;
					num = sqrMagnitude;
				}
			}
			Vector3 val4 = val2;
			val3 = default(Vector3);
			if (val4 == val3)
			{
				num = float.MaxValue;
				foreach (Vector3 item2 in list)
				{
					val3 = val.position - item2;
					float sqrMagnitude2 = ((Vector3)(ref val3)).sqrMagnitude;
					if (sqrMagnitude2 >= aggroRangeSqr + 2f && sqrMagnitude2 < num)
					{
						val2 = item2;
						num = sqrMagnitude2;
					}
				}
			}
			Vector3 val5 = val2;
			val3 = default(Vector3);
			if (val5 != val3)
			{
				((EnemyAI)this).SetDestinationToPosition(val2, true);
				pathDelayTimer = Time.time;
				return true;
			}
			return false;
		}

		public List<Vector3> FindCoverPointsAroundTarget()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: 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)
			//IL_01f4: 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)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: 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_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: 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_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0352: Unknown result type (might be due to invalid IL or missing references)
			//IL_033a: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			Vector3 position = ((Component)base.targetPlayer).transform.position;
			Bounds val = default(Bounds);
			((Bounds)(ref val))..ctor(position, new Vector3(40f, 2f, 40f));
			Bounds val2 = default(Bounds);
			((Bounds)(ref val2))..ctor(((Component)this).transform.position, new Vector3(40f, 2f, 40f));
			List<GameObject> list2 = new List<GameObject>();
			if (base.isOutside)
			{
				foreach (GameObject cachedOutsideAINode in cachedOutsideAINodes)
				{
					if ((Object)(object)cachedOutsideAINode != (Object)null && (((Bounds)(ref val2)).Contains(cachedOutsideAINode.transform.position) || ((Bounds)(ref val)).Contains(cachedOutsideAINode.transform.position)))
					{
						list2.Add(cachedOutsideAINode);
					}
				}
				if (list2.Count == 0)
				{
					foreach (GameObject cachedAllAINode in cachedAllAINodes)
					{
						if ((Object)(object)cachedAllAINode != (Object)null && (((Bounds)(ref val2)).Contains(cachedAllAINode.transform.position) || ((Bounds)(ref val)).Contains(cachedAllAINode.transform.position)))
						{
							list2.Add(cachedAllAINode);
						}
					}
				}
			}
			else
			{
				foreach (GameObject cachedInsideAINode in cachedInsideAINodes)
				{
					if ((Object)(object)cachedInsideAINode != (Object)null && (((Bounds)(ref val2)).Contains(cachedInsideAINode.transform.position) || ((Bounds)(ref val)).Contains(cachedInsideAINode.transform.position)))
					{
						list2.Add(cachedInsideAINode);
					}
				}
				if (list2.Count == 0)
				{
					foreach (GameObject cachedAllAINode2 in cachedAllAINodes)
					{
						if ((Object)(object)cachedAllAINode2 != (Object)null && (((Bounds)(ref val2)).Contains(cachedAllAINode2.transform.position) || ((Bounds)(ref val)).Contains(cachedAllAINode2.transform.position)))
						{
							list2.Add(cachedAllAINode2);
						}
					}
				}
			}
			foreach (GameObject item in list2)
			{
				for (int i = 0; i < 3; i++)
				{
					Vector3 position2 = item.transform.position;
					Vector2 val3 = Random.insideUnitCircle * 3f;
					position2 += new Vector3(val3.x, 0f, val3.y);
					position2 = ValidateZonePosition(position2);
					if (!(position2 != default(Vector3)))
					{
						continue;
					}
					bool flag = true;
					PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
					foreach (PlayerControllerB val4 in allPlayerScripts)
					{
						if (!val4.isPlayerDead && val4.isPlayerControlled && base.isOutside != val4.isInsideFactory && (((Bounds)(ref val)).Contains(((Component)val4).transform.position) || ((Bounds)(ref val2)).Contains(((Component)val4).transform.position)) && (val4.HasLineOfSightToPosition(position2, 60f, 60, 25f) || PathIsIntersectedByLOS(position2)) && CheckForPath(((Component)this).transform.position, position2))
						{
							flag = false;
							break;
						}
					}
					if (flag)
					{
						list.Add(position2);
					}
				}
			}
			return list;
		}

		public Vector3 ChooseClosestNodeToPos(Vector3 pos, bool avoidLineOfSight = false, int offset = 0)
		{
			//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_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			GameObject[] array = null;
			Vector3 zero = Vector3.zero;
			List<GameObject> list = new List<GameObject>();
			if (base.isOutside)
			{
				foreach (GameObject cachedOutsideAINode in cachedOutsideAINodes)
				{
					if ((Object)(object)cachedOutsideAINode != (Object)null)
					{
						list.Add(cachedOutsideAINode);
					}
				}
				if (list.Count == 0)
				{
					foreach (GameObject cachedAllAINode in cachedAllAINodes)
					{
						if ((Object)(object)cachedAllAINode != (Object)null)
						{
							list.Add(cachedAllAINode);
						}
					}
				}
			}
			else
			{
				foreach (GameObject cachedInsideAINode in cachedInsideAINodes)
				{
					if ((Object)(object)cachedInsideAINode != (Object)null)
					{
						list.Add(cachedInsideAINode);
					}
				}
				if (list.Count == 0)
				{
					foreach (GameObject cachedAllAINode2 in cachedAllAINodes)
					{
						if ((Object)(object)cachedAllAINode2 != (Object)null)
						{
							list.Add(cachedAllAINode2);
						}
					}
				}
			}
			if (array == null || zero != pos)
			{
				array = (GameObject[])(object)new GameObject[list.Count];
				list.CopyTo(array);
				for (int i = 0; i < array.Length - 1; i++)
				{
					for (int j = i + 1; j < array.Length; j++)
					{
						Vector3 val = pos - array[i].transform.position;
						float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
						val = pos - array[j].transform.position;
						if (sqrMagnitude > ((Vector3)(ref val)).sqrMagnitude)
						{
							ref GameObject reference = ref array[i];
							ref GameObject reference2 = ref array[j];
							GameObject val2 = array[j];
							GameObject val3 = array[i];
							reference = val2;
							reference2 = val3;
						}
					}
				}
			}
			Transform transform = array[0].transform;
			for (int k = 0; k < array.Length; k++)
			{
				if (!PathIsIntersectedByLOS(array[k].transform.position, calculatePathDistance: false, avoidLineOfSight))
				{
					base.mostOptimalDistance = Vector3.Distance(pos, array[k].transform.position);
					transform = array[k].transform;
					if (offset == 0 || k >= array.Length - 1)
					{
						break;
					}
					offset--;
				}
			}
			return transform.position;
		}

		public bool PathIsIntersectedByLOS(Vector3 targetPos, bool calculatePathDistance = false, bool avoidLineOfSight = true, bool checkLOSToTargetPlayer = false)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: 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_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: 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_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			base.pathDistance = 0f;
			if (base.agent.isOnNavMesh && !base.agent.CalculatePath(targetPos, base.path1))
			{
				return true;
			}
			if (base.path1 == null || base.path1.corners.Length == 0)
			{
				return true;
			}
			Vector3 val = base.path1.corners[^1] - RoundManager.Instance.GetNavMeshPosition(targetPos, RoundManager.Instance.navHit, 2.7f, -1);
			if (((Vector3)(ref val)).sqrMagnitude > 2.25f)
			{
				return true;
			}
			bool flag = false;
			if (calculatePathDistance)
			{
				for (int i = 1; i < base.path1.corners.Length; i++)
				{
					base.pathDistance += Vector3.Distance(base.path1.corners[i - 1], base.path1.corners[i]);
					if (i > 15 || !(avoidLineOfSight || checkLOSToTargetPlayer))
					{
						continue;
					}
					if (!flag && i > 8)
					{
						val = base.path1.corners[i - 1] - base.path1.corners[i];
						if (((Vector3)(ref val)).sqrMagnitude < 4f)
						{
							flag = true;
							i++;
							continue;
						}
					}
					flag = false;
					if (checkLOSToTargetPlayer && (Object)(object)base.targetPlayer != (Object)null && !Physics.Linecast(base.path1.corners[i - 1], ((Component)base.targetPlayer).transform.position + Vector3.up * 0.3f, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1))
					{
						return true;
					}
					if (avoidLineOfSight && Physics.Linecast(base.path1.corners[i - 1], base.path1.corners[i], 262144))
					{
						return true;
					}
				}
			}
			else if (avoidLineOfSight)
			{
				for (int j = 1; j < base.path1.corners.Length; j++)
				{
					if (!flag && j > 8

plugins/LethalGargoyles/lib/NVorbis/NVorbis.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using NVorbis.Contracts;
using NVorbis.Contracts.Ogg;
using NVorbis.Ogg;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("Andrew Ward")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Andrew Ward 2021")]
[assembly: AssemblyDescription("A fully managed implementation of a Xiph.org Foundation Ogg Vorbis decoder.")]
[assembly: AssemblyFileVersion("0.10.5.0")]
[assembly: AssemblyInformationalVersion("0.10.5")]
[assembly: AssemblyProduct("NVorbis")]
[assembly: AssemblyTitle("NVorbis")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/NVorbis/NVorbis")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.10.5.0")]
namespace NVorbis
{
	internal class Codebook : ICodebook
	{
		private class FastRange : IReadOnlyList<int>, IEnumerable<int>, IEnumerable, IReadOnlyCollection<int>
		{
			[ThreadStatic]
			private static FastRange _cachedRange;

			private int _start;

			private int _count;

			public int this[int index]
			{
				get
				{
					if (index > _count)
					{
						throw new ArgumentOutOfRangeException();
					}
					return _start + index;
				}
			}

			public int Count => _count;

			internal static FastRange Get(int start, int count)
			{
				FastRange obj = _cachedRange ?? (_cachedRange = new FastRange());
				obj._start = start;
				obj._count = count;
				return obj;
			}

			private FastRange()
			{
			}

			public IEnumerator<int> GetEnumerator()
			{
				throw new NotSupportedException();
			}

			IEnumerator IEnumerable.GetEnumerator()
			{
				return GetEnumerator();
			}
		}

		private int[] _lengths;

		private float[] _lookupTable;

		private IReadOnlyList<HuffmanListNode> _overflowList;

		private IReadOnlyList<HuffmanListNode> _prefixList;

		private int _prefixBitLength;

		private int _maxBits;

		public float this[int entry, int dim] => _lookupTable[entry * Dimensions + dim];

		public int Dimensions { get; private set; }

		public int Entries { get; private set; }

		public int MapType { get; private set; }

		public void Init(IPacket packet, IHuffman huffman)
		{
			if (packet.ReadBits(24) != 5653314)
			{
				throw new InvalidDataException("Book header had invalid signature!");
			}
			Dimensions = (int)packet.ReadBits(16);
			Entries = (int)packet.ReadBits(24);
			_lengths = new int[Entries];
			InitTree(packet, huffman);
			InitLookupTable(packet);
		}

		private void InitTree(IPacket packet, IHuffman huffman)
		{
			int num = 0;
			bool flag;
			int num5;
			if (packet.ReadBit())
			{
				int num2 = (int)packet.ReadBits(5) + 1;
				int num3 = 0;
				while (num3 < Entries)
				{
					int num4 = (int)packet.ReadBits(Utils.ilog(Entries - num3));
					while (--num4 >= 0)
					{
						_lengths[num3++] = num2;
					}
					num2++;
				}
				num = 0;
				flag = false;
				num5 = num2;
			}
			else
			{
				num5 = -1;
				flag = packet.ReadBit();
				for (int i = 0; i < Entries; i++)
				{
					if (!flag || packet.ReadBit())
					{
						_lengths[i] = (int)packet.ReadBits(5) + 1;
						num++;
					}
					else
					{
						_lengths[i] = -1;
					}
					if (_lengths[i] > num5)
					{
						num5 = _lengths[i];
					}
				}
			}
			if ((_maxBits = num5) > -1)
			{
				int[] array = null;
				if (flag && num >= Entries >> 2)
				{
					array = new int[Entries];
					Array.Copy(_lengths, array, Entries);
					flag = false;
				}
				int num6 = (flag ? num : 0);
				int[] array2 = null;
				int[] array3 = null;
				if (!flag)
				{
					array3 = new int[Entries];
				}
				else if (num6 != 0)
				{
					array = new int[num6];
					array3 = new int[num6];
					array2 = new int[num6];
				}
				if (!ComputeCodewords(flag, array3, array, _lengths, Entries, array2))
				{
					throw new InvalidDataException();
				}
				IReadOnlyList<int> readOnlyList = array2;
				IReadOnlyList<int> value = readOnlyList ?? FastRange.Get(0, array3.Length);
				huffman.GenerateTable(value, array ?? _lengths, array3);
				_prefixList = huffman.PrefixTree;
				_prefixBitLength = huffman.TableBits;
				_overflowList = huffman.OverflowList;
			}
		}

		private bool ComputeCodewords(bool sparse, int[] codewords, int[] codewordLengths, int[] len, int n, int[] values)
		{
			int num = 0;
			uint[] array = new uint[32];
			int i;
			for (i = 0; i < n && len[i] <= 0; i++)
			{
			}
			if (i == n)
			{
				return true;
			}
			AddEntry(sparse, codewords, codewordLengths, 0u, i, num++, len[i], values);
			for (int j = 1; j <= len[i]; j++)
			{
				array[j] = (uint)(1 << 32 - j);
			}
			for (int j = i + 1; j < n; j++)
			{
				int num2 = len[j];
				if (num2 <= 0)
				{
					continue;
				}
				while (num2 > 0 && array[num2] == 0)
				{
					num2--;
				}
				if (num2 == 0)
				{
					return false;
				}
				uint num3 = array[num2];
				array[num2] = 0u;
				AddEntry(sparse, codewords, codewordLengths, Utils.BitReverse(num3), j, num++, len[j], values);
				if (num2 != len[j])
				{
					for (int num4 = len[j]; num4 > num2; num4--)
					{
						array[num4] = num3 + (uint)(1 << 32 - num4);
					}
				}
			}
			return true;
		}

		private void AddEntry(bool sparse, int[] codewords, int[] codewordLengths, uint huffCode, int symbol, int count, int len, int[] values)
		{
			if (sparse)
			{
				codewords[count] = (int)huffCode;
				codewordLengths[count] = len;
				values[count] = symbol;
			}
			else
			{
				codewords[symbol] = (int)huffCode;
			}
		}

		private void InitLookupTable(IPacket packet)
		{
			MapType = (int)packet.ReadBits(4);
			if (MapType == 0)
			{
				return;
			}
			float num = Utils.ConvertFromVorbisFloat32((uint)packet.ReadBits(32));
			float num2 = Utils.ConvertFromVorbisFloat32((uint)packet.ReadBits(32));
			int count = (int)packet.ReadBits(4) + 1;
			bool flag = packet.ReadBit();
			int num3 = Entries * Dimensions;
			float[] array = new float[num3];
			if (MapType == 1)
			{
				num3 = lookup1_values();
			}
			uint[] array2 = new uint[num3];
			for (int i = 0; i < num3; i++)
			{
				array2[i] = (uint)packet.ReadBits(count);
			}
			if (MapType == 1)
			{
				for (int j = 0; j < Entries; j++)
				{
					double num4 = 0.0;
					int num5 = 1;
					for (int k = 0; k < Dimensions; k++)
					{
						int num6 = j / num5 % num3;
						double num7 = (double)((float)array2[num6] * num2 + num) + num4;
						array[j * Dimensions + k] = (float)num7;
						if (flag)
						{
							num4 = num7;
						}
						num5 *= num3;
					}
				}
			}
			else
			{
				for (int l = 0; l < Entries; l++)
				{
					double num8 = 0.0;
					int num9 = l * Dimensions;
					for (int m = 0; m < Dimensions; m++)
					{
						double num10 = (double)((float)array2[num9] * num2 + num) + num8;
						array[l * Dimensions + m] = (float)num10;
						if (flag)
						{
							num8 = num10;
						}
						num9++;
					}
				}
			}
			_lookupTable = array;
		}

		private int lookup1_values()
		{
			int num = (int)Math.Floor(Math.Exp(Math.Log(Entries) / (double)Dimensions));
			if (Math.Floor(Math.Pow(num + 1, Dimensions)) <= (double)Entries)
			{
				num++;
			}
			return num;
		}

		public int DecodeScalar(IPacket packet)
		{
			int index = (int)packet.TryPeekBits(_prefixBitLength, out var bitsRead);
			if (bitsRead == 0)
			{
				return -1;
			}
			HuffmanListNode huffmanListNode = _prefixList[index];
			if (huffmanListNode != null)
			{
				packet.SkipBits(huffmanListNode.Length);
				return huffmanListNode.Value;
			}
			index = (int)packet.TryPeekBits(_maxBits, out var _);
			for (int i = 0; i < _overflowList.Count; i++)
			{
				huffmanListNode = _overflowList[i];
				if (huffmanListNode.Bits == (index & huffmanListNode.Mask))
				{
					packet.SkipBits(huffmanListNode.Length);
					return huffmanListNode.Value;
				}
			}
			return -1;
		}
	}
	public abstract class DataPacket : IPacket
	{
		[Flags]
		protected enum PacketFlags : byte
		{
			IsResync = 1,
			IsEndOfStream = 2,
			IsShort = 4,
			User0 = 8,
			User1 = 0x10,
			User2 = 0x20,
			User3 = 0x40,
			User4 = 0x80
		}

		private ulong _bitBucket;

		private int _bitCount;

		private byte _overflowBits;

		private PacketFlags _packetFlags;

		private int _readBits;

		public int ContainerOverheadBits { get; set; }

		public long? GranulePosition { get; set; }

		public bool IsResync
		{
			get
			{
				return GetFlag(PacketFlags.IsResync);
			}
			set
			{
				SetFlag(PacketFlags.IsResync, value);
			}
		}

		public bool IsShort
		{
			get
			{
				return GetFlag(PacketFlags.IsShort);
			}
			private set
			{
				SetFlag(PacketFlags.IsShort, value);
			}
		}

		public bool IsEndOfStream
		{
			get
			{
				return GetFlag(PacketFlags.IsEndOfStream);
			}
			set
			{
				SetFlag(PacketFlags.IsEndOfStream, value);
			}
		}

		public int BitsRead => _readBits;

		public int BitsRemaining => TotalBits - _readBits;

		protected abstract int TotalBits { get; }

		private bool GetFlag(PacketFlags flag)
		{
			return _packetFlags.HasFlag(flag);
		}

		private void SetFlag(PacketFlags flag, bool value)
		{
			if (value)
			{
				_packetFlags |= flag;
			}
			else
			{
				_packetFlags &= (PacketFlags)(byte)(~(int)flag);
			}
		}

		protected abstract int ReadNextByte();

		public virtual void Done()
		{
		}

		public virtual void Reset()
		{
			_bitBucket = 0uL;
			_bitCount = 0;
			_overflowBits = 0;
			_readBits = 0;
		}

		ulong IPacket.ReadBits(int count)
		{
			if (count == 0)
			{
				return 0uL;
			}
			int bitsRead;
			ulong result = TryPeekBits(count, out bitsRead);
			SkipBits(count);
			return result;
		}

		public ulong TryPeekBits(int count, out int bitsRead)
		{
			switch (count)
			{
			default:
				throw new ArgumentOutOfRangeException("count");
			case 0:
				bitsRead = 0;
				return 0uL;
			case 1:
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
			case 8:
			case 9:
			case 10:
			case 11:
			case 12:
			case 13:
			case 14:
			case 15:
			case 16:
			case 17:
			case 18:
			case 19:
			case 20:
			case 21:
			case 22:
			case 23:
			case 24:
			case 25:
			case 26:
			case 27:
			case 28:
			case 29:
			case 30:
			case 31:
			case 32:
			case 33:
			case 34:
			case 35:
			case 36:
			case 37:
			case 38:
			case 39:
			case 40:
			case 41:
			case 42:
			case 43:
			case 44:
			case 45:
			case 46:
			case 47:
			case 48:
			case 49:
			case 50:
			case 51:
			case 52:
			case 53:
			case 54:
			case 55:
			case 56:
			case 57:
			case 58:
			case 59:
			case 60:
			case 61:
			case 62:
			case 63:
			case 64:
				break;
			}
			while (_bitCount < count)
			{
				int num = ReadNextByte();
				if (num == -1)
				{
					bitsRead = _bitCount;
					return _bitBucket;
				}
				_bitBucket = (ulong)((long)(num & 0xFF) << _bitCount) | _bitBucket;
				_bitCount += 8;
				if (_bitCount > 64)
				{
					_overflowBits = (byte)(num >> 72 - _bitCount);
				}
			}
			ulong num2 = _bitBucket;
			if (count < 64)
			{
				num2 &= (ulong)((1L << count) - 1);
			}
			bitsRead = count;
			return num2;
		}

		public void SkipBits(int count)
		{
			if (count <= 0)
			{
				return;
			}
			if (_bitCount > count)
			{
				if (count > 63)
				{
					_bitBucket = 0uL;
				}
				else
				{
					_bitBucket >>= count;
				}
				if (_bitCount > 64)
				{
					int num = _bitCount - 64;
					_bitBucket |= (ulong)_overflowBits << _bitCount - count - num;
					if (num > count)
					{
						_overflowBits = (byte)(_overflowBits >> count);
					}
				}
				_bitCount -= count;
				_readBits += count;
				return;
			}
			if (_bitCount == count)
			{
				_bitBucket = 0uL;
				_bitCount = 0;
				_readBits += count;
				return;
			}
			count -= _bitCount;
			_readBits += _bitCount;
			_bitCount = 0;
			_bitBucket = 0uL;
			while (count > 8)
			{
				if (ReadNextByte() == -1)
				{
					count = 0;
					IsShort = true;
					break;
				}
				count -= 8;
				_readBits += 8;
			}
			if (count > 0)
			{
				int num2 = ReadNextByte();
				if (num2 == -1)
				{
					IsShort = true;
					return;
				}
				_bitBucket = (ulong)(num2 >> count);
				_bitCount = 8 - count;
				_readBits += count;
			}
		}
	}
	public static class Extensions
	{
		public static int Read(this IPacket packet, byte[] buffer, int index, int count)
		{
			if (index < 0 || index >= buffer.Length)
			{
				throw new ArgumentOutOfRangeException("index");
			}
			if (count < 0 || index + count > buffer.Length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			for (int i = 0; i < count; i++)
			{
				int bitsRead;
				byte b = (byte)packet.TryPeekBits(8, out bitsRead);
				if (bitsRead == 0)
				{
					return i;
				}
				buffer[index++] = b;
				packet.SkipBits(8);
			}
			return count;
		}

		public static byte[] ReadBytes(this IPacket packet, int count)
		{
			byte[] array = new byte[count];
			int num = packet.Read(array, 0, count);
			if (num < count)
			{
				byte[] array2 = new byte[num];
				Buffer.BlockCopy(array, 0, array2, 0, num);
				return array2;
			}
			return array;
		}

		public static bool ReadBit(this IPacket packet)
		{
			return packet.ReadBits(1) == 1;
		}

		public static byte PeekByte(this IPacket packet)
		{
			int bitsRead;
			return (byte)packet.TryPeekBits(8, out bitsRead);
		}

		public static byte ReadByte(this IPacket packet)
		{
			return (byte)packet.ReadBits(8);
		}

		public static short ReadInt16(this IPacket packet)
		{
			return (short)packet.ReadBits(16);
		}

		public static int ReadInt32(this IPacket packet)
		{
			return (int)packet.ReadBits(32);
		}

		public static long ReadInt64(this IPacket packet)
		{
			return (long)packet.ReadBits(64);
		}

		public static ushort ReadUInt16(this IPacket packet)
		{
			return (ushort)packet.ReadBits(16);
		}

		public static uint ReadUInt32(this IPacket packet)
		{
			return (uint)packet.ReadBits(32);
		}

		public static ulong ReadUInt64(this IPacket packet)
		{
			return packet.ReadBits(64);
		}

		public static void SkipBytes(this IPacket packet, int count)
		{
			packet.SkipBits(count * 8);
		}
	}
	internal class Factory : IFactory
	{
		public IHuffman CreateHuffman()
		{
			return new Huffman();
		}

		public IMdct CreateMdct()
		{
			return new Mdct();
		}

		public ICodebook CreateCodebook()
		{
			return new Codebook();
		}

		public IFloor CreateFloor(IPacket packet)
		{
			return (int)packet.ReadBits(16) switch
			{
				0 => new Floor0(), 
				1 => new Floor1(), 
				_ => throw new InvalidDataException("Invalid floor type!"), 
			};
		}

		public IMapping CreateMapping(IPacket packet)
		{
			if (packet.ReadBits(16) != 0L)
			{
				throw new InvalidDataException("Invalid mapping type!");
			}
			return new Mapping();
		}

		public IMode CreateMode()
		{
			return new Mode();
		}

		public IResidue CreateResidue(IPacket packet)
		{
			return (int)packet.ReadBits(16) switch
			{
				0 => new Residue0(), 
				1 => new Residue1(), 
				2 => new Residue2(), 
				_ => throw new InvalidDataException("Invalid residue type!"), 
			};
		}
	}
	internal class Floor0 : IFloor
	{
		private class Data : IFloorData
		{
			internal float[] Coeff;

			internal float Amp;

			public bool ExecuteChannel
			{
				get
				{
					if (ForceEnergy || Amp > 0f)
					{
						return !ForceNoEnergy;
					}
					return false;
				}
			}

			public bool ForceEnergy { get; set; }

			public bool ForceNoEnergy { get; set; }
		}

		private int _order;

		private int _rate;

		private int _bark_map_size;

		private int _ampBits;

		private int _ampOfs;

		private int _ampDiv;

		private ICodebook[] _books;

		private int _bookBits;

		private Dictionary<int, float[]> _wMap;

		private Dictionary<int, int[]> _barkMaps;

		public void Init(IPacket packet, int channels, int block0Size, int block1Size, ICodebook[] codebooks)
		{
			_order = (int)packet.ReadBits(8);
			_rate = (int)packet.ReadBits(16);
			_bark_map_size = (int)packet.ReadBits(16);
			_ampBits = (int)packet.ReadBits(6);
			_ampOfs = (int)packet.ReadBits(8);
			_books = new ICodebook[(int)packet.ReadBits(4) + 1];
			if (_order < 1 || _rate < 1 || _bark_map_size < 1 || _books.Length == 0)
			{
				throw new InvalidDataException();
			}
			_ampDiv = (1 << _ampBits) - 1;
			for (int i = 0; i < _books.Length; i++)
			{
				int num = (int)packet.ReadBits(8);
				if (num < 0 || num >= codebooks.Length)
				{
					throw new InvalidDataException();
				}
				ICodebook codebook = codebooks[num];
				if (codebook.MapType == 0 || codebook.Dimensions < 1)
				{
					throw new InvalidDataException();
				}
				_books[i] = codebook;
			}
			_bookBits = Utils.ilog(_books.Length);
			_barkMaps = new Dictionary<int, int[]>
			{
				[block0Size] = SynthesizeBarkCurve(block0Size / 2),
				[block1Size] = SynthesizeBarkCurve(block1Size / 2)
			};
			_wMap = new Dictionary<int, float[]>
			{
				[block0Size] = SynthesizeWDelMap(block0Size / 2),
				[block1Size] = SynthesizeWDelMap(block1Size / 2)
			};
		}

		private int[] SynthesizeBarkCurve(int n)
		{
			float num = (float)_bark_map_size / toBARK(_rate / 2);
			int[] array = new int[n + 1];
			for (int i = 0; i < n - 1; i++)
			{
				array[i] = Math.Min(_bark_map_size - 1, (int)Math.Floor(toBARK((float)_rate / 2f / (float)n * (float)i) * num));
			}
			array[n] = -1;
			return array;
		}

		private static float toBARK(double lsp)
		{
			return (float)(13.1 * Math.Atan(0.00074 * lsp) + 2.24 * Math.Atan(1.85E-08 * lsp * lsp) + 0.0001 * lsp);
		}

		private float[] SynthesizeWDelMap(int n)
		{
			float num = (float)(Math.PI / (double)_bark_map_size);
			float[] array = new float[n];
			for (int i = 0; i < n; i++)
			{
				array[i] = 2f * (float)Math.Cos(num * (float)i);
			}
			return array;
		}

		public IFloorData Unpack(IPacket packet, int blockSize, int channel)
		{
			Data data = new Data
			{
				Coeff = new float[_order + 1]
			};
			data.Amp = packet.ReadBits(_ampBits);
			if (data.Amp > 0f)
			{
				Array.Clear(data.Coeff, 0, data.Coeff.Length);
				data.Amp = data.Amp / (float)_ampDiv * (float)_ampOfs;
				uint num = (uint)packet.ReadBits(_bookBits);
				if (num >= _books.Length)
				{
					data.Amp = 0f;
					return data;
				}
				ICodebook codebook = _books[num];
				int i = 0;
				while (i < _order)
				{
					int num2 = codebook.DecodeScalar(packet);
					if (num2 == -1)
					{
						data.Amp = 0f;
						return data;
					}
					int num3 = 0;
					for (; i < _order; i++)
					{
						if (num3 >= codebook.Dimensions)
						{
							break;
						}
						data.Coeff[i] = codebook[num2, num3];
						num3++;
					}
				}
				float num4 = 0f;
				int num5 = 0;
				while (num5 < _order)
				{
					int num6 = 0;
					while (num5 < _order && num6 < codebook.Dimensions)
					{
						data.Coeff[num5] += num4;
						num5++;
						num6++;
					}
					num4 = data.Coeff[num5 - 1];
				}
			}
			return data;
		}

		public void Apply(IFloorData floorData, int blockSize, float[] residue)
		{
			if (!(floorData is Data data))
			{
				throw new ArgumentException("Incorrect packet data!");
			}
			int num = blockSize / 2;
			if (data.Amp > 0f)
			{
				int[] array = _barkMaps[blockSize];
				float[] array2 = _wMap[blockSize];
				int num2 = 0;
				for (num2 = 0; num2 < _order; num2++)
				{
					data.Coeff[num2] = 2f * (float)Math.Cos(data.Coeff[num2]);
				}
				num2 = 0;
				while (num2 < num)
				{
					int num3 = array[num2];
					float num4 = 0.5f;
					float num5 = 0.5f;
					float num6 = array2[num3];
					int i;
					for (i = 1; i < _order; i += 2)
					{
						num5 *= num6 - data.Coeff[i - 1];
						num4 *= num6 - data.Coeff[i];
					}
					if (i == _order)
					{
						num5 *= num6 - data.Coeff[i - 1];
						num4 *= num4 * (4f - num6 * num6);
						num5 *= num5;
					}
					else
					{
						num4 *= num4 * (2f - num6);
						num5 *= num5 * (2f + num6);
					}
					num5 = data.Amp / (float)Math.Sqrt(num4 + num5) - (float)_ampOfs;
					num5 = (float)Math.Exp(num5 * 0.11512925f);
					residue[num2] *= num5;
					while (array[++num2] == num3)
					{
						residue[num2] *= num5;
					}
				}
			}
			else
			{
				Array.Clear(residue, 0, num);
			}
		}
	}
	internal class Floor1 : IFloor
	{
		private class Data : IFloorData
		{
			internal int[] Posts = new int[64];

			internal int PostCount;

			public bool ExecuteChannel
			{
				get
				{
					if (ForceEnergy || PostCount > 0)
					{
						return !ForceNoEnergy;
					}
					return false;
				}
			}

			public bool ForceEnergy { get; set; }

			public bool ForceNoEnergy { get; set; }
		}

		private int[] _partitionClass;

		private int[] _classDimensions;

		private int[] _classSubclasses;

		private int[] _xList;

		private int[] _classMasterBookIndex;

		private int[] _hNeigh;

		private int[] _lNeigh;

		private int[] _sortIdx;

		private int _multiplier;

		private int _range;

		private int _yBits;

		private ICodebook[] _classMasterbooks;

		private ICodebook[][] _subclassBooks;

		private int[][] _subclassBookIndex;

		private static readonly int[] _rangeLookup = new int[4] { 256, 128, 86, 64 };

		private static readonly int[] _yBitsLookup = new int[4] { 8, 7, 7, 6 };

		private static readonly float[] inverse_dB_table = new float[256]
		{
			1.0649863E-07f, 1.1341951E-07f, 1.2079015E-07f, 1.2863978E-07f, 1.369995E-07f, 1.459025E-07f, 1.5538409E-07f, 1.6548181E-07f, 1.7623574E-07f, 1.8768856E-07f,
			1.998856E-07f, 2.128753E-07f, 2.2670913E-07f, 2.4144197E-07f, 2.5713223E-07f, 2.7384212E-07f, 2.9163792E-07f, 3.1059022E-07f, 3.307741E-07f, 3.5226967E-07f,
			3.7516213E-07f, 3.995423E-07f, 4.255068E-07f, 4.5315863E-07f, 4.8260745E-07f, 5.1397E-07f, 5.4737063E-07f, 5.829419E-07f, 6.208247E-07f, 6.611694E-07f,
			7.041359E-07f, 7.4989464E-07f, 7.98627E-07f, 8.505263E-07f, 9.057983E-07f, 9.646621E-07f, 1.0273513E-06f, 1.0941144E-06f, 1.1652161E-06f, 1.2409384E-06f,
			1.3215816E-06f, 1.4074654E-06f, 1.4989305E-06f, 1.5963394E-06f, 1.7000785E-06f, 1.8105592E-06f, 1.9282195E-06f, 2.053526E-06f, 2.1869757E-06f, 2.3290977E-06f,
			2.4804558E-06f, 2.6416496E-06f, 2.813319E-06f, 2.9961443E-06f, 3.1908505E-06f, 3.39821E-06f, 3.619045E-06f, 3.8542307E-06f, 4.1047006E-06f, 4.371447E-06f,
			4.6555283E-06f, 4.958071E-06f, 5.280274E-06f, 5.623416E-06f, 5.988857E-06f, 6.3780467E-06f, 6.7925284E-06f, 7.2339453E-06f, 7.704048E-06f, 8.2047E-06f,
			8.737888E-06f, 9.305725E-06f, 9.910464E-06f, 1.0554501E-05f, 1.1240392E-05f, 1.1970856E-05f, 1.2748789E-05f, 1.3577278E-05f, 1.4459606E-05f, 1.5399271E-05f,
			1.6400005E-05f, 1.7465769E-05f, 1.8600793E-05f, 1.9809577E-05f, 2.1096914E-05f, 2.2467912E-05f, 2.3928002E-05f, 2.5482977E-05f, 2.7139005E-05f, 2.890265E-05f,
			3.078091E-05f, 3.2781227E-05f, 3.4911533E-05f, 3.718028E-05f, 3.9596467E-05f, 4.2169668E-05f, 4.491009E-05f, 4.7828602E-05f, 5.0936775E-05f, 5.424693E-05f,
			5.7772202E-05f, 6.152657E-05f, 6.552491E-05f, 6.9783084E-05f, 7.4317984E-05f, 7.914758E-05f, 8.429104E-05f, 8.976875E-05f, 9.560242E-05f, 0.00010181521f,
			0.00010843174f, 0.00011547824f, 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, 0.00015820454f, 0.00016848555f, 0.00017943469f, 0.00019109536f,
			0.00020351382f, 0.0002167393f, 0.00023082423f, 0.00024582449f, 0.00026179955f, 0.00027881275f, 0.00029693157f, 0.00031622787f, 0.00033677815f, 0.00035866388f,
			0.00038197188f, 0.00040679457f, 0.00043323037f, 0.0004613841f, 0.0004913675f, 0.00052329927f, 0.0005573062f, 0.0005935231f, 0.0006320936f, 0.0006731706f,
			0.000716917f, 0.0007635063f, 0.00081312325f, 0.00086596457f, 0.00092223985f, 0.0009821722f, 0.0010459992f, 0.0011139743f, 0.0011863665f, 0.0012634633f,
			0.0013455702f, 0.0014330129f, 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, 0.0019632196f, 0.0020908006f, 0.0022266726f, 0.0023713743f,
			0.0025254795f, 0.0026895993f, 0.0028643848f, 0.0030505287f, 0.003248769f, 0.0034598925f, 0.0036847359f, 0.0039241905f, 0.0041792067f, 0.004450795f,
			0.004740033f, 0.005048067f, 0.0053761187f, 0.005725489f, 0.0060975635f, 0.0064938175f, 0.0069158226f, 0.0073652514f, 0.007843887f, 0.008353627f,
			0.008896492f, 0.009474637f, 0.010090352f, 0.01074608f, 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, 0.014722068f, 0.015678791f,
			0.016697686f, 0.017782796f, 0.018938422f, 0.020169148f, 0.021479854f, 0.022875736f, 0.02436233f, 0.025945531f, 0.027631618f, 0.029427277f,
			0.031339627f, 0.03337625f, 0.035545226f, 0.037855156f, 0.0403152f, 0.042935107f, 0.045725275f, 0.048696756f, 0.05186135f, 0.05523159f,
			0.05882085f, 0.062643364f, 0.06671428f, 0.07104975f, 0.075666964f, 0.08058423f, 0.08582105f, 0.09139818f, 0.097337745f, 0.1036633f,
			0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, 0.14201812f, 0.15124726f, 0.16107617f, 0.1715438f, 0.18269168f, 0.19456401f,
			0.20720787f, 0.22067343f, 0.23501402f, 0.25028655f, 0.26655158f, 0.28387362f, 0.3023213f, 0.32196787f, 0.34289113f, 0.36517414f,
			0.3889052f, 0.41417846f, 0.44109413f, 0.4697589f, 0.50028646f, 0.53279793f, 0.5674221f, 0.6042964f, 0.64356697f, 0.6853896f,
			0.72993004f, 0.777365f, 0.8278826f, 0.88168305f, 0.9389798f, 1f
		};

		public void Init(IPacket packet, int channels, int block0Size, int block1Size, ICodebook[] codebooks)
		{
			int num = -1;
			_partitionClass = new int[(uint)packet.ReadBits(5)];
			for (int i = 0; i < _partitionClass.Length; i++)
			{
				_partitionClass[i] = (int)packet.ReadBits(4);
				if (_partitionClass[i] > num)
				{
					num = _partitionClass[i];
				}
			}
			_classDimensions = new int[++num];
			_classSubclasses = new int[num];
			_classMasterbooks = new ICodebook[num];
			_classMasterBookIndex = new int[num];
			_subclassBooks = new ICodebook[num][];
			_subclassBookIndex = new int[num][];
			for (int j = 0; j < num; j++)
			{
				_classDimensions[j] = (int)packet.ReadBits(3) + 1;
				_classSubclasses[j] = (int)packet.ReadBits(2);
				if (_classSubclasses[j] > 0)
				{
					_classMasterBookIndex[j] = (int)packet.ReadBits(8);
					_classMasterbooks[j] = codebooks[_classMasterBookIndex[j]];
				}
				_subclassBooks[j] = new ICodebook[1 << _classSubclasses[j]];
				_subclassBookIndex[j] = new int[_subclassBooks[j].Length];
				for (int k = 0; k < _subclassBooks[j].Length; k++)
				{
					int num2 = (int)packet.ReadBits(8) - 1;
					if (num2 >= 0)
					{
						_subclassBooks[j][k] = codebooks[num2];
					}
					_subclassBookIndex[j][k] = num2;
				}
			}
			_multiplier = (int)packet.ReadBits(2);
			_range = _rangeLookup[_multiplier];
			_yBits = _yBitsLookup[_multiplier];
			_multiplier++;
			int num3 = (int)packet.ReadBits(4);
			List<int> list = new List<int>();
			list.Add(0);
			list.Add(1 << num3);
			for (int l = 0; l < _partitionClass.Length; l++)
			{
				int num4 = _partitionClass[l];
				for (int m = 0; m < _classDimensions[num4]; m++)
				{
					list.Add((int)packet.ReadBits(num3));
				}
			}
			_xList = list.ToArray();
			_lNeigh = new int[list.Count];
			_hNeigh = new int[list.Count];
			_sortIdx = new int[list.Count];
			_sortIdx[0] = 0;
			_sortIdx[1] = 1;
			for (int n = 2; n < _lNeigh.Length; n++)
			{
				_lNeigh[n] = 0;
				_hNeigh[n] = 1;
				_sortIdx[n] = n;
				for (int num5 = 2; num5 < n; num5++)
				{
					int num6 = _xList[num5];
					if (num6 < _xList[n])
					{
						if (num6 > _xList[_lNeigh[n]])
						{
							_lNeigh[n] = num5;
						}
					}
					else if (num6 < _xList[_hNeigh[n]])
					{
						_hNeigh[n] = num5;
					}
				}
			}
			for (int num7 = 0; num7 < _sortIdx.Length - 1; num7++)
			{
				for (int num8 = num7 + 1; num8 < _sortIdx.Length; num8++)
				{
					if (_xList[num7] == _xList[num8])
					{
						throw new InvalidDataException();
					}
					if (_xList[_sortIdx[num7]] > _xList[_sortIdx[num8]])
					{
						int num9 = _sortIdx[num7];
						_sortIdx[num7] = _sortIdx[num8];
						_sortIdx[num8] = num9;
					}
				}
			}
		}

		public IFloorData Unpack(IPacket packet, int blockSize, int channel)
		{
			Data data = new Data();
			if (packet.ReadBit())
			{
				int num = 2;
				data.Posts[0] = (int)packet.ReadBits(_yBits);
				data.Posts[1] = (int)packet.ReadBits(_yBits);
				for (int i = 0; i < _partitionClass.Length; i++)
				{
					int num2 = _partitionClass[i];
					int num3 = _classDimensions[num2];
					int num4 = _classSubclasses[num2];
					int num5 = (1 << num4) - 1;
					uint num6 = 0u;
					if (num4 > 0 && (num6 = (uint)_classMasterbooks[num2].DecodeScalar(packet)) == uint.MaxValue)
					{
						num = 0;
						break;
					}
					for (int j = 0; j < num3; j++)
					{
						ICodebook codebook = _subclassBooks[num2][num6 & num5];
						num6 >>= num4;
						if (codebook != null && (data.Posts[num] = codebook.DecodeScalar(packet)) == -1)
						{
							num = 0;
							i = _partitionClass.Length;
							break;
						}
						num++;
					}
				}
				data.PostCount = num;
			}
			return data;
		}

		public void Apply(IFloorData floorData, int blockSize, float[] residue)
		{
			if (!(floorData is Data data))
			{
				throw new ArgumentException("Incorrect packet data!", "packetData");
			}
			int num = blockSize / 2;
			if (data.PostCount > 0)
			{
				bool[] array = UnwrapPosts(data);
				int num2 = 0;
				int num3 = data.Posts[0] * _multiplier;
				for (int i = 1; i < data.PostCount; i++)
				{
					int num4 = _sortIdx[i];
					if (array[num4])
					{
						int num5 = _xList[num4];
						int num6 = data.Posts[num4] * _multiplier;
						if (num2 < num)
						{
							RenderLineMulti(num2, num3, Math.Min(num5, num), num6, residue);
						}
						num2 = num5;
						num3 = num6;
					}
					if (num2 >= num)
					{
						break;
					}
				}
				if (num2 < num)
				{
					RenderLineMulti(num2, num3, num, num3, residue);
				}
			}
			else
			{
				Array.Clear(residue, 0, num);
			}
		}

		private bool[] UnwrapPosts(Data data)
		{
			bool[] array = new bool[64];
			array[0] = true;
			array[1] = true;
			int[] array2 = new int[64];
			array2[0] = data.Posts[0];
			array2[1] = data.Posts[1];
			for (int i = 2; i < data.PostCount; i++)
			{
				int num = _lNeigh[i];
				int num2 = _hNeigh[i];
				int num3 = RenderPoint(_xList[num], array2[num], _xList[num2], array2[num2], _xList[i]);
				int num4 = data.Posts[i];
				int num5 = _range - num3;
				int num6 = num3;
				int num7 = ((num5 >= num6) ? (num6 * 2) : (num5 * 2));
				if (num4 != 0)
				{
					array[num] = true;
					array[num2] = true;
					array[i] = true;
					if (num4 >= num7)
					{
						if (num5 > num6)
						{
							array2[i] = num4 - num6 + num3;
						}
						else
						{
							array2[i] = num3 - num4 + num5 - 1;
						}
					}
					else if (num4 % 2 == 1)
					{
						array2[i] = num3 - (num4 + 1) / 2;
					}
					else
					{
						array2[i] = num3 + num4 / 2;
					}
				}
				else
				{
					array[i] = false;
					array2[i] = num3;
				}
			}
			for (int j = 0; j < data.PostCount; j++)
			{
				data.Posts[j] = array2[j];
			}
			return array;
		}

		private int RenderPoint(int x0, int y0, int x1, int y1, int X)
		{
			int num = y1 - y0;
			int num2 = x1 - x0;
			int num3 = Math.Abs(num) * (X - x0) / num2;
			if (num < 0)
			{
				return y0 - num3;
			}
			return y0 + num3;
		}

		private void RenderLineMulti(int x0, int y0, int x1, int y1, float[] v)
		{
			int num = y1 - y0;
			int num2 = x1 - x0;
			int num3 = Math.Abs(num);
			int num4 = 1 - ((num >> 31) & 1) * 2;
			int num5 = num / num2;
			int num6 = x0;
			int num7 = y0;
			int num8 = -num2;
			v[x0] *= inverse_dB_table[y0];
			num3 -= Math.Abs(num5) * num2;
			while (++num6 < x1)
			{
				num7 += num5;
				num8 += num3;
				if (num8 >= 0)
				{
					num8 -= num2;
					num7 += num4;
				}
				v[num6] *= inverse_dB_table[num7];
			}
		}
	}
	internal class Huffman : IHuffman, IComparer<HuffmanListNode>
	{
		private const int MAX_TABLE_BITS = 10;

		public int TableBits { get; private set; }

		public IReadOnlyList<HuffmanListNode> PrefixTree { get; private set; }

		public IReadOnlyList<HuffmanListNode> OverflowList { get; private set; }

		public void GenerateTable(IReadOnlyList<int> values, int[] lengthList, int[] codeList)
		{
			HuffmanListNode[] array = new HuffmanListNode[lengthList.Length];
			int num = 0;
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new HuffmanListNode
				{
					Value = values[i],
					Length = ((lengthList[i] <= 0) ? 99999 : lengthList[i]),
					Bits = codeList[i],
					Mask = (1 << lengthList[i]) - 1
				};
				if (lengthList[i] > 0 && num < lengthList[i])
				{
					num = lengthList[i];
				}
			}
			Array.Sort(array, 0, array.Length, this);
			int num2 = ((num > 10) ? 10 : num);
			List<HuffmanListNode> list = new List<HuffmanListNode>(1 << num2);
			List<HuffmanListNode> list2 = null;
			for (int j = 0; j < array.Length && array[j].Length < 99999; j++)
			{
				int length = array[j].Length;
				if (length > num2)
				{
					list2 = new List<HuffmanListNode>(array.Length - j);
					for (; j < array.Length && array[j].Length < 99999; j++)
					{
						list2.Add(array[j]);
					}
					continue;
				}
				int num3 = 1 << num2 - length;
				HuffmanListNode huffmanListNode = array[j];
				for (int k = 0; k < num3; k++)
				{
					int num4 = (k << length) | huffmanListNode.Bits;
					while (list.Count <= num4)
					{
						list.Add(null);
					}
					list[num4] = huffmanListNode;
				}
			}
			while (list.Count < 1 << num2)
			{
				list.Add(null);
			}
			TableBits = num2;
			PrefixTree = list;
			OverflowList = list2;
		}

		int IComparer<HuffmanListNode>.Compare(HuffmanListNode x, HuffmanListNode y)
		{
			int num = x.Length - y.Length;
			if (num == 0)
			{
				return x.Bits - y.Bits;
			}
			return num;
		}
	}
	[Obsolete("Moved to NVorbis.Contracts.IContainerReader", true)]
	public interface IContainerReader : NVorbis.Contracts.IContainerReader, IDisposable
	{
		[Obsolete("Use Streams.Select(s => s.StreamSerial).ToArray() instead.", true)]
		int[] StreamSerials { get; }

		[Obsolete("No longer supported.", true)]
		int PagesRead { get; }

		[Obsolete("Moved to NewStreamCallback.", true)]
		event EventHandler<NewStreamEventArgs> NewStream;

		[Obsolete("Renamed to TryInit().", true)]
		bool Init();

		[Obsolete("No longer supported.", true)]
		int GetTotalPageCount();
	}
	[Obsolete("Moved to NVorbis.Contracts.IPacketProvider", true)]
	public interface IPacketProvider : NVorbis.Contracts.IPacketProvider
	{
		[Obsolete("Moved to per-stream IStreamStats instance on IStreamDecoder.Stats or VorbisReader.Stats.", true)]
		long ContainerBits { get; }

		[Obsolete("No longer supported.", true)]
		event EventHandler ParameterChange;

		[Obsolete("No longer supported.", true)]
		int GetTotalPageCount();

		[Obsolete("Getting a packet by index is no longer supported.", true)]
		DataPacket GetPacket(int packetIndex);

		[Obsolete("Moved to long SeekTo(long, int, GetPacketGranuleCount)", true)]
		DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback);

		[Obsolete("Seeking to a specified packet is no longer supported.  See SeekTo(...) instead.", true)]
		void SeekToPacket(DataPacket packet, int preRoll);
	}
	[Obsolete("Moved to NVorbis.Contracts.IStreamStats", true)]
	public interface IVorbisStreamStatus : IStreamStats
	{
		[Obsolete("No longer supported.", true)]
		TimeSpan PageLatency { get; }

		[Obsolete("No longer supported.", true)]
		TimeSpan PacketLatency { get; }

		[Obsolete("No longer supported.", true)]
		TimeSpan SecondLatency { get; }

		[Obsolete("No longer supported.", true)]
		int PagesRead { get; }

		[Obsolete("No longer supported.", true)]
		int TotalPages { get; }

		[Obsolete("Use IStreamDecoder.HasClipped instead.  VorbisReader.HasClipped will return the same value for the stream it is handling.", true)]
		bool Clipped { get; }
	}
	internal class Mapping : IMapping
	{
		private IMdct _mdct;

		private int[] _couplingAngle;

		private int[] _couplingMangitude;

		private IFloor[] _submapFloor;

		private IResidue[] _submapResidue;

		private IFloor[] _channelFloor;

		private IResidue[] _channelResidue;

		public void Init(IPacket packet, int channels, IFloor[] floors, IResidue[] residues, IMdct mdct)
		{
			int num = 1;
			if (packet.ReadBit())
			{
				num += (int)packet.ReadBits(4);
			}
			int num2 = 0;
			if (packet.ReadBit())
			{
				num2 = (int)packet.ReadBits(8) + 1;
			}
			int count = Utils.ilog(channels - 1);
			_couplingAngle = new int[num2];
			_couplingMangitude = new int[num2];
			for (int i = 0; i < num2; i++)
			{
				int num3 = (int)packet.ReadBits(count);
				int num4 = (int)packet.ReadBits(count);
				if (num3 == num4 || num3 > channels - 1 || num4 > channels - 1)
				{
					throw new InvalidDataException("Invalid magnitude or angle in mapping header!");
				}
				_couplingAngle[i] = num4;
				_couplingMangitude[i] = num3;
			}
			if (packet.ReadBits(2) != 0L)
			{
				throw new InvalidDataException("Reserved bits not 0 in mapping header.");
			}
			int[] array = new int[channels];
			if (num > 1)
			{
				for (int j = 0; j < channels; j++)
				{
					array[j] = (int)packet.ReadBits(4);
					if (array[j] > num)
					{
						throw new InvalidDataException("Invalid channel mux submap index in mapping header!");
					}
				}
			}
			_submapFloor = new IFloor[num];
			_submapResidue = new IResidue[num];
			for (int k = 0; k < num; k++)
			{
				packet.SkipBits(8);
				int num5 = (int)packet.ReadBits(8);
				if (num5 >= floors.Length)
				{
					throw new InvalidDataException("Invalid floor number in mapping header!");
				}
				int num6 = (int)packet.ReadBits(8);
				if (num6 >= residues.Length)
				{
					throw new InvalidDataException("Invalid residue number in mapping header!");
				}
				_submapFloor[k] = floors[num5];
				_submapResidue[k] = residues[num6];
			}
			_channelFloor = new IFloor[channels];
			_channelResidue = new IResidue[channels];
			for (int l = 0; l < channels; l++)
			{
				_channelFloor[l] = _submapFloor[array[l]];
				_channelResidue[l] = _submapResidue[array[l]];
			}
			_mdct = mdct;
		}

		public void DecodePacket(IPacket packet, int blockSize, int channels, float[][] buffer)
		{
			int num = blockSize >> 1;
			IFloorData[] array = new IFloorData[_channelFloor.Length];
			bool[] array2 = new bool[_channelFloor.Length];
			for (int i = 0; i < _channelFloor.Length; i++)
			{
				array[i] = _channelFloor[i].Unpack(packet, blockSize, i);
				array2[i] = !array[i].ExecuteChannel;
				Array.Clear(buffer[i], 0, num);
			}
			for (int j = 0; j < _couplingAngle.Length; j++)
			{
				if (array[_couplingAngle[j]].ExecuteChannel || array[_couplingMangitude[j]].ExecuteChannel)
				{
					array[_couplingAngle[j]].ForceEnergy = true;
					array[_couplingMangitude[j]].ForceEnergy = true;
				}
			}
			for (int k = 0; k < _submapFloor.Length; k++)
			{
				for (int l = 0; l < _channelFloor.Length; l++)
				{
					if (_submapFloor[k] != _channelFloor[l] || _submapResidue[k] != _channelResidue[l])
					{
						array[l].ForceNoEnergy = true;
					}
				}
				_submapResidue[k].Decode(packet, array2, blockSize, buffer);
			}
			for (int num2 = _couplingAngle.Length - 1; num2 >= 0; num2--)
			{
				if (array[_couplingAngle[num2]].ExecuteChannel || array[_couplingMangitude[num2]].ExecuteChannel)
				{
					float[] array3 = buffer[_couplingMangitude[num2]];
					float[] array4 = buffer[_couplingAngle[num2]];
					for (int m = 0; m < num; m++)
					{
						float num3 = array3[m];
						float num4 = array4[m];
						float num5;
						float num6;
						if (num3 > 0f)
						{
							if (num4 > 0f)
							{
								num5 = num3;
								num6 = num3 - num4;
							}
							else
							{
								num6 = num3;
								num5 = num3 + num4;
							}
						}
						else if (num4 > 0f)
						{
							num5 = num3;
							num6 = num3 + num4;
						}
						else
						{
							num6 = num3;
							num5 = num3 - num4;
						}
						array3[m] = num5;
						array4[m] = num6;
					}
				}
			}
			for (int n = 0; n < _channelFloor.Length; n++)
			{
				if (array[n].ExecuteChannel)
				{
					_channelFloor[n].Apply(array[n], blockSize, buffer[n]);
					_mdct.Reverse(buffer[n], blockSize);
				}
				else
				{
					Array.Clear(buffer[n], num, num);
				}
			}
		}
	}
	internal class Mdct : IMdct
	{
		private class MdctImpl
		{
			private readonly int _n;

			private readonly int _n2;

			private readonly int _n4;

			private readonly int _n8;

			private readonly int _ld;

			private readonly float[] _a;

			private readonly float[] _b;

			private readonly float[] _c;

			private readonly ushort[] _bitrev;

			public MdctImpl(int n)
			{
				_n = n;
				_n2 = n >> 1;
				_n4 = _n2 >> 1;
				_n8 = _n4 >> 1;
				_ld = Utils.ilog(n) - 1;
				_a = new float[_n2];
				_b = new float[_n2];
				_c = new float[_n4];
				int num;
				int num2 = (num = 0);
				while (num2 < _n4)
				{
					_a[num] = (float)Math.Cos((float)(4 * num2) * MathF.PI / (float)n);
					_a[num + 1] = (float)(0.0 - Math.Sin((float)(4 * num2) * MathF.PI / (float)n));
					_b[num] = (float)Math.Cos((float)(num + 1) * MathF.PI / (float)n / 2f) * 0.5f;
					_b[num + 1] = (float)Math.Sin((float)(num + 1) * MathF.PI / (float)n / 2f) * 0.5f;
					num2++;
					num += 2;
				}
				num2 = (num = 0);
				while (num2 < _n8)
				{
					_c[num] = (float)Math.Cos((float)(2 * (num + 1)) * MathF.PI / (float)n);
					_c[num + 1] = (float)(0.0 - Math.Sin((float)(2 * (num + 1)) * MathF.PI / (float)n));
					num2++;
					num += 2;
				}
				_bitrev = new ushort[_n8];
				for (int i = 0; i < _n8; i++)
				{
					_bitrev[i] = (ushort)(Utils.BitReverse((uint)i, _ld - 3) << 2);
				}
			}

			internal void CalcReverse(float[] buffer)
			{
				float[] array = new float[_n2];
				int num = _n2 - 2;
				int num2 = 0;
				int i = 0;
				for (int n = _n2; i != n; i += 4)
				{
					array[num + 1] = buffer[i] * _a[num2] - buffer[i + 2] * _a[num2 + 1];
					array[num] = buffer[i] * _a[num2 + 1] + buffer[i + 2] * _a[num2];
					num -= 2;
					num2 += 2;
				}
				i = _n2 - 3;
				while (num >= 0)
				{
					array[num + 1] = (0f - buffer[i + 2]) * _a[num2] - (0f - buffer[i]) * _a[num2 + 1];
					array[num] = (0f - buffer[i + 2]) * _a[num2 + 1] + (0f - buffer[i]) * _a[num2];
					num -= 2;
					num2 += 2;
					i -= 4;
				}
				float[] array2 = array;
				int num3 = _n2 - 8;
				int num4 = _n4;
				int num5 = 0;
				int num6 = _n4;
				int num7 = 0;
				while (num3 >= 0)
				{
					float num8 = array2[num4 + 1] - array2[num5 + 1];
					float num9 = array2[num4] - array2[num5];
					buffer[num6 + 1] = array2[num4 + 1] + array2[num5 + 1];
					buffer[num6] = array2[num4] + array2[num5];
					buffer[num7 + 1] = num8 * _a[num3 + 4] - num9 * _a[num3 + 5];
					buffer[num7] = num9 * _a[num3 + 4] + num8 * _a[num3 + 5];
					num8 = array2[num4 + 3] - array2[num5 + 3];
					num9 = array2[num4 + 2] - array2[num5 + 2];
					buffer[num6 + 3] = array2[num4 + 3] + array2[num5 + 3];
					buffer[num6 + 2] = array2[num4 + 2] + array2[num5 + 2];
					buffer[num7 + 3] = num8 * _a[num3] - num9 * _a[num3 + 1];
					buffer[num7 + 2] = num9 * _a[num3] + num8 * _a[num3 + 1];
					num3 -= 8;
					num6 += 4;
					num7 += 4;
					num4 += 4;
					num5 += 4;
				}
				int n2 = _n >> 4;
				int num10 = _n2 - 1;
				_ = _n4;
				step3_iter0_loop(n2, buffer, num10 - 0, -_n8);
				step3_iter0_loop(_n >> 4, buffer, _n2 - 1 - _n4, -_n8);
				int lim = _n >> 5;
				int num11 = _n2 - 1;
				_ = _n8;
				step3_inner_r_loop(lim, buffer, num11 - 0, -(_n >> 4), 16);
				step3_inner_r_loop(_n >> 5, buffer, _n2 - 1 - _n8, -(_n >> 4), 16);
				step3_inner_r_loop(_n >> 5, buffer, _n2 - 1 - _n8 * 2, -(_n >> 4), 16);
				step3_inner_r_loop(_n >> 5, buffer, _n2 - 1 - _n8 * 3, -(_n >> 4), 16);
				int j;
				for (j = 2; j < _ld - 3 >> 1; j++)
				{
					int num12 = _n >> j + 2;
					int num13 = num12 >> 1;
					int num14 = 1 << j + 1;
					for (int k = 0; k < num14; k++)
					{
						step3_inner_r_loop(_n >> j + 4, buffer, _n2 - 1 - num12 * k, -num13, 1 << j + 3);
					}
				}
				for (; j < _ld - 6; j++)
				{
					int num15 = _n >> j + 2;
					int num16 = 1 << j + 3;
					int num17 = num15 >> 1;
					int num18 = _n >> j + 6;
					int n3 = 1 << j + 1;
					int num19 = _n2 - 1;
					int num20 = 0;
					for (int num21 = num18; num21 > 0; num21--)
					{
						step3_inner_s_loop(n3, buffer, num19, -num17, num20, num16, num15);
						num20 += num16 * 4;
						num19 -= 8;
					}
				}
				step3_inner_s_loop_ld654(_n >> 5, buffer, _n2 - 1, _n);
				int num22 = 0;
				int num23 = _n4 - 4;
				int num24 = _n2 - 4;
				while (num23 >= 0)
				{
					int num25 = _bitrev[num22];
					array2[num24 + 3] = buffer[num25];
					array2[num24 + 2] = buffer[num25 + 1];
					array2[num23 + 3] = buffer[num25 + 2];
					array2[num23 + 2] = buffer[num25 + 3];
					num25 = _bitrev[num22 + 1];
					array2[num24 + 1] = buffer[num25];
					array2[num24] = buffer[num25 + 1];
					array2[num23 + 1] = buffer[num25 + 2];
					array2[num23] = buffer[num25 + 3];
					num23 -= 4;
					num24 -= 4;
					num22 += 2;
				}
				int num26 = 0;
				int num27 = 0;
				int num28 = _n2 - 4;
				while (num27 < num28)
				{
					float num29 = array2[num27] - array2[num28 + 2];
					float num30 = array2[num27 + 1] + array2[num28 + 3];
					float num31 = _c[num26 + 1] * num29 + _c[num26] * num30;
					float num32 = _c[num26 + 1] * num30 - _c[num26] * num29;
					float num33 = array2[num27] + array2[num28 + 2];
					float num34 = array2[num27 + 1] - array2[num28 + 3];
					array2[num27] = num33 + num31;
					array2[num27 + 1] = num34 + num32;
					array2[num28 + 2] = num33 - num31;
					array2[num28 + 3] = num32 - num34;
					num29 = array2[num27 + 2] - array2[num28];
					num30 = array2[num27 + 3] + array2[num28 + 1];
					num31 = _c[num26 + 3] * num29 + _c[num26 + 2] * num30;
					num32 = _c[num26 + 3] * num30 - _c[num26 + 2] * num29;
					num33 = array2[num27 + 2] + array2[num28];
					num34 = array2[num27 + 3] - array2[num28 + 1];
					array2[num27 + 2] = num33 + num31;
					array2[num27 + 3] = num34 + num32;
					array2[num28] = num33 - num31;
					array2[num28 + 1] = num32 - num34;
					num26 += 4;
					num27 += 4;
					num28 -= 4;
				}
				int num35 = _n2 - 8;
				int num36 = _n2 - 8;
				int num37 = 0;
				int num38 = _n2 - 4;
				int num39 = _n2;
				int num40 = _n - 4;
				while (num36 >= 0)
				{
					float num41 = array[num36 + 6] * _b[num35 + 7] - array[num36 + 7] * _b[num35 + 6];
					float num42 = (0f - array[num36 + 6]) * _b[num35 + 6] - array[num36 + 7] * _b[num35 + 7];
					buffer[num37] = num41;
					buffer[num38 + 3] = 0f - num41;
					buffer[num39] = num42;
					buffer[num40 + 3] = num42;
					float num43 = array[num36 + 4] * _b[num35 + 5] - array[num36 + 5] * _b[num35 + 4];
					float num44 = (0f - array[num36 + 4]) * _b[num35 + 4] - array[num36 + 5] * _b[num35 + 5];
					buffer[num37 + 1] = num43;
					buffer[num38 + 2] = 0f - num43;
					buffer[num39 + 1] = num44;
					buffer[num40 + 2] = num44;
					num41 = array[num36 + 2] * _b[num35 + 3] - array[num36 + 3] * _b[num35 + 2];
					num42 = (0f - array[num36 + 2]) * _b[num35 + 2] - array[num36 + 3] * _b[num35 + 3];
					buffer[num37 + 2] = num41;
					buffer[num38 + 1] = 0f - num41;
					buffer[num39 + 2] = num42;
					buffer[num40 + 1] = num42;
					num43 = array[num36] * _b[num35 + 1] - array[num36 + 1] * _b[num35];
					num44 = (0f - array[num36]) * _b[num35] - array[num36 + 1] * _b[num35 + 1];
					buffer[num37 + 3] = num43;
					buffer[num38] = 0f - num43;
					buffer[num39 + 3] = num44;
					buffer[num40] = num44;
					num35 -= 8;
					num36 -= 8;
					num37 += 4;
					num39 += 4;
					num38 -= 4;
					num40 -= 4;
				}
			}

			private void step3_iter0_loop(int n, float[] e, int i_off, int k_off)
			{
				int num = i_off;
				int num2 = num + k_off;
				int num3 = 0;
				for (int num4 = n >> 2; num4 > 0; num4--)
				{
					float num5 = e[num] - e[num2];
					float num6 = e[num - 1] - e[num2 - 1];
					e[num] += e[num2];
					e[num - 1] += e[num2 - 1];
					e[num2] = num5 * _a[num3] - num6 * _a[num3 + 1];
					e[num2 - 1] = num6 * _a[num3] + num5 * _a[num3 + 1];
					num3 += 8;
					num5 = e[num - 2] - e[num2 - 2];
					num6 = e[num - 3] - e[num2 - 3];
					e[num - 2] += e[num2 - 2];
					e[num - 3] += e[num2 - 3];
					e[num2 - 2] = num5 * _a[num3] - num6 * _a[num3 + 1];
					e[num2 - 3] = num6 * _a[num3] + num5 * _a[num3 + 1];
					num3 += 8;
					num5 = e[num - 4] - e[num2 - 4];
					num6 = e[num - 5] - e[num2 - 5];
					e[num - 4] += e[num2 - 4];
					e[num - 5] += e[num2 - 5];
					e[num2 - 4] = num5 * _a[num3] - num6 * _a[num3 + 1];
					e[num2 - 5] = num6 * _a[num3] + num5 * _a[num3 + 1];
					num3 += 8;
					num5 = e[num - 6] - e[num2 - 6];
					num6 = e[num - 7] - e[num2 - 7];
					e[num - 6] += e[num2 - 6];
					e[num - 7] += e[num2 - 7];
					e[num2 - 6] = num5 * _a[num3] - num6 * _a[num3 + 1];
					e[num2 - 7] = num6 * _a[num3] + num5 * _a[num3 + 1];
					num3 += 8;
					num -= 8;
					num2 -= 8;
				}
			}

			private void step3_inner_r_loop(int lim, float[] e, int d0, int k_off, int k1)
			{
				int num = d0;
				int num2 = num + k_off;
				int num3 = 0;
				for (int num4 = lim >> 2; num4 > 0; num4--)
				{
					float num5 = e[num] - e[num2];
					float num6 = e[num - 1] - e[num2 - 1];
					e[num] += e[num2];
					e[num - 1] += e[num2 - 1];
					e[num2] = num5 * _a[num3] - num6 * _a[num3 + 1];
					e[num2 - 1] = num6 * _a[num3] + num5 * _a[num3 + 1];
					num3 += k1;
					num5 = e[num - 2] - e[num2 - 2];
					num6 = e[num - 3] - e[num2 - 3];
					e[num - 2] += e[num2 - 2];
					e[num - 3] += e[num2 - 3];
					e[num2 - 2] = num5 * _a[num3] - num6 * _a[num3 + 1];
					e[num2 - 3] = num6 * _a[num3] + num5 * _a[num3 + 1];
					num3 += k1;
					num5 = e[num - 4] - e[num2 - 4];
					num6 = e[num - 5] - e[num2 - 5];
					e[num - 4] += e[num2 - 4];
					e[num - 5] += e[num2 - 5];
					e[num2 - 4] = num5 * _a[num3] - num6 * _a[num3 + 1];
					e[num2 - 5] = num6 * _a[num3] + num5 * _a[num3 + 1];
					num3 += k1;
					num5 = e[num - 6] - e[num2 - 6];
					num6 = e[num - 7] - e[num2 - 7];
					e[num - 6] += e[num2 - 6];
					e[num - 7] += e[num2 - 7];
					e[num2 - 6] = num5 * _a[num3] - num6 * _a[num3 + 1];
					e[num2 - 7] = num6 * _a[num3] + num5 * _a[num3 + 1];
					num3 += k1;
					num -= 8;
					num2 -= 8;
				}
			}

			private void step3_inner_s_loop(int n, float[] e, int i_off, int k_off, int a, int a_off, int k0)
			{
				float num = _a[a];
				float num2 = _a[a + 1];
				float num3 = _a[a + a_off];
				float num4 = _a[a + a_off + 1];
				float num5 = _a[a + a_off * 2];
				float num6 = _a[a + a_off * 2 + 1];
				float num7 = _a[a + a_off * 3];
				float num8 = _a[a + a_off * 3 + 1];
				int num9 = i_off;
				int num10 = num9 + k_off;
				for (int num11 = n; num11 > 0; num11--)
				{
					float num12 = e[num9] - e[num10];
					float num13 = e[num9 - 1] - e[num10 - 1];
					e[num9] += e[num10];
					e[num9 - 1] += e[num10 - 1];
					e[num10] = num12 * num - num13 * num2;
					e[num10 - 1] = num13 * num + num12 * num2;
					num12 = e[num9 - 2] - e[num10 - 2];
					num13 = e[num9 - 3] - e[num10 - 3];
					e[num9 - 2] += e[num10 - 2];
					e[num9 - 3] += e[num10 - 3];
					e[num10 - 2] = num12 * num3 - num13 * num4;
					e[num10 - 3] = num13 * num3 + num12 * num4;
					num12 = e[num9 - 4] - e[num10 - 4];
					num13 = e[num9 - 5] - e[num10 - 5];
					e[num9 - 4] += e[num10 - 4];
					e[num9 - 5] += e[num10 - 5];
					e[num10 - 4] = num12 * num5 - num13 * num6;
					e[num10 - 5] = num13 * num5 + num12 * num6;
					num12 = e[num9 - 6] - e[num10 - 6];
					num13 = e[num9 - 7] - e[num10 - 7];
					e[num9 - 6] += e[num10 - 6];
					e[num9 - 7] += e[num10 - 7];
					e[num10 - 6] = num12 * num7 - num13 * num8;
					e[num10 - 7] = num13 * num7 + num12 * num8;
					num9 -= k0;
					num10 -= k0;
				}
			}

			private void step3_inner_s_loop_ld654(int n, float[] e, int i_off, int base_n)
			{
				int num = base_n >> 3;
				float num2 = _a[num];
				int num3 = i_off;
				int num4 = num3 - 16 * n;
				while (num3 > num4)
				{
					float num5 = e[num3] - e[num3 - 8];
					float num6 = e[num3 - 1] - e[num3 - 9];
					e[num3] += e[num3 - 8];
					e[num3 - 1] += e[num3 - 9];
					e[num3 - 8] = num5;
					e[num3 - 9] = num6;
					num5 = e[num3 - 2] - e[num3 - 10];
					num6 = e[num3 - 3] - e[num3 - 11];
					e[num3 - 2] += e[num3 - 10];
					e[num3 - 3] += e[num3 - 11];
					e[num3 - 10] = (num5 + num6) * num2;
					e[num3 - 11] = (num6 - num5) * num2;
					num5 = e[num3 - 12] - e[num3 - 4];
					num6 = e[num3 - 5] - e[num3 - 13];
					e[num3 - 4] += e[num3 - 12];
					e[num3 - 5] += e[num3 - 13];
					e[num3 - 12] = num6;
					e[num3 - 13] = num5;
					num5 = e[num3 - 14] - e[num3 - 6];
					num6 = e[num3 - 7] - e[num3 - 15];
					e[num3 - 6] += e[num3 - 14];
					e[num3 - 7] += e[num3 - 15];
					e[num3 - 14] = (num5 + num6) * num2;
					e[num3 - 15] = (num5 - num6) * num2;
					iter_54(e, num3);
					iter_54(e, num3 - 8);
					num3 -= 16;
				}
			}

			private void iter_54(float[] e, int z)
			{
				float num = e[z] - e[z - 4];
				float num2 = e[z] + e[z - 4];
				float num3 = e[z - 2] + e[z - 6];
				float num4 = e[z - 2] - e[z - 6];
				e[z] = num2 + num3;
				e[z - 2] = num2 - num3;
				float num5 = e[z - 3] - e[z - 7];
				e[z - 4] = num + num5;
				e[z - 6] = num - num5;
				float num6 = e[z - 1] - e[z - 5];
				float num7 = e[z - 1] + e[z - 5];
				float num8 = e[z - 3] + e[z - 7];
				e[z - 1] = num7 + num8;
				e[z - 3] = num7 - num8;
				e[z - 5] = num6 - num4;
				e[z - 7] = num6 + num4;
			}
		}

		private const float M_PI = MathF.PI;

		private Dictionary<int, MdctImpl> _setupCache = new Dictionary<int, MdctImpl>();

		public void Reverse(float[] samples, int sampleCount)
		{
			if (!_setupCache.TryGetValue(sampleCount, out var value))
			{
				value = new MdctImpl(sampleCount);
				_setupCache[sampleCount] = value;
			}
			value.CalcReverse(samples);
		}
	}
	internal class Mode : IMode
	{
		private struct OverlapInfo
		{
			public int PacketStartIndex;

			public int PacketTotalLength;

			public int PacketValidLength;
		}

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

		private int _channels;

		private bool _blockFlag;

		private int _blockSize;

		private IMapping _mapping;

		private float[][] _windows;

		private OverlapInfo[] _overlapInfo;

		public void Init(IPacket packet, int channels, int block0Size, int block1Size, IMapping[] mappings)
		{
			_channels = channels;
			_blockFlag = packet.ReadBit();
			if (packet.ReadBits(32) != 0L)
			{
				throw new InvalidDataException("Mode header had invalid window or transform type!");
			}
			int num = (int)packet.ReadBits(8);
			if (num >= mappings.Length)
			{
				throw new InvalidDataException("Mode header had invalid mapping index!");
			}
			_mapping = mappings[num];
			if (_blockFlag)
			{
				_blockSize = block1Size;
				_windows = new float[4][]
				{
					CalcWindow(block0Size, block1Size, block0Size),
					CalcWindow(block1Size, block1Size, block0Size),
					CalcWindow(block0Size, block1Size, block1Size),
					CalcWindow(block1Size, block1Size, block1Size)
				};
				_overlapInfo = new OverlapInfo[4]
				{
					CalcOverlap(block0Size, block1Size, block0Size),
					CalcOverlap(block1Size, block1Size, block0Size),
					CalcOverlap(block0Size, block1Size, block1Size),
					CalcOverlap(block1Size, block1Size, block1Size)
				};
			}
			else
			{
				_blockSize = block0Size;
				_windows = new float[1][] { CalcWindow(block0Size, block0Size, block0Size) };
			}
		}

		private static float[] CalcWindow(int prevBlockSize, int blockSize, int nextBlockSize)
		{
			float[] array = new float[blockSize];
			int num = prevBlockSize / 2;
			int num2 = nextBlockSize / 2;
			int num3 = blockSize / 4 - num / 2;
			int num4 = blockSize - blockSize / 4 - num2 / 2;
			for (int i = 0; i < num; i++)
			{
				float num5 = (float)Math.Sin(((double)i + 0.5) / (double)num * 1.5707963705062866);
				num5 *= num5;
				array[num3 + i] = (float)Math.Sin(num5 * (MathF.PI / 2f));
			}
			for (int j = num3 + num; j < num4; j++)
			{
				array[j] = 1f;
			}
			for (int k = 0; k < num2; k++)
			{
				float num6 = (float)Math.Sin(((double)(num2 - k) - 0.5) / (double)num2 * 1.5707963705062866);
				num6 *= num6;
				array[num4 + k] = (float)Math.Sin(num6 * (MathF.PI / 2f));
			}
			return array;
		}

		private static OverlapInfo CalcOverlap(int prevBlockSize, int blockSize, int nextBlockSize)
		{
			int num = prevBlockSize / 4;
			int num2 = nextBlockSize / 4;
			int packetStartIndex = blockSize / 4 - num;
			int num3 = blockSize / 4 * 3 + num2;
			int packetValidLength = num3 - num2 * 2;
			OverlapInfo result = default(OverlapInfo);
			result.PacketStartIndex = packetStartIndex;
			result.PacketValidLength = packetValidLength;
			result.PacketTotalLength = num3;
			return result;
		}

		private bool GetPacketInfo(IPacket packet, out int windowIndex, out int packetStartIndex, out int packetValidLength, out int packetTotalLength)
		{
			if (packet.IsShort)
			{
				windowIndex = 0;
				packetStartIndex = 0;
				packetValidLength = 0;
				packetTotalLength = 0;
				return false;
			}
			if (_blockFlag)
			{
				bool flag = packet.ReadBit();
				bool flag2 = packet.ReadBit();
				windowIndex = (flag ? 1 : 0) + (flag2 ? 2 : 0);
				OverlapInfo overlapInfo = _overlapInfo[windowIndex];
				packetStartIndex = overlapInfo.PacketStartIndex;
				packetValidLength = overlapInfo.PacketValidLength;
				packetTotalLength = overlapInfo.PacketTotalLength;
			}
			else
			{
				windowIndex = 0;
				packetStartIndex = 0;
				packetValidLength = _blockSize / 2;
				packetTotalLength = _blockSize;
			}
			return true;
		}

		public bool Decode(IPacket packet, float[][] buffer, out int packetStartindex, out int packetValidLength, out int packetTotalLength)
		{
			if (GetPacketInfo(packet, out var windowIndex, out packetStartindex, out packetValidLength, out packetTotalLength))
			{
				_mapping.DecodePacket(packet, _blockSize, _channels, buffer);
				float[] array = _windows[windowIndex];
				for (int i = 0; i < _blockSize; i++)
				{
					for (int j = 0; j < _channels; j++)
					{
						buffer[j][i] *= array[i];
					}
				}
				return true;
			}
			return false;
		}

		public int GetPacketSampleCount(IPacket packet)
		{
			GetPacketInfo(packet, out var _, out var packetStartIndex, out var packetValidLength, out var _);
			return packetValidLength - packetStartIndex;
		}
	}
	[Serializable]
	public class NewStreamEventArgs : EventArgs
	{
		public IStreamDecoder StreamDecoder { get; }

		public bool IgnoreStream { get; set; }

		public NewStreamEventArgs(IStreamDecoder streamDecoder)
		{
			StreamDecoder = streamDecoder ?? throw new ArgumentNullException("streamDecoder");
		}
	}
	internal class Residue0 : IResidue
	{
		private int _channels;

		private int _begin;

		private int _end;

		private int _partitionSize;

		private int _classifications;

		private int _maxStages;

		private ICodebook[][] _books;

		private ICodebook _classBook;

		private int[] _cascade;

		private int[][] _decodeMap;

		private static int icount(int v)
		{
			int num = 0;
			while (v != 0)
			{
				num += v & 1;
				v >>= 1;
			}
			return num;
		}

		public virtual void Init(IPacket packet, int channels, ICodebook[] codebooks)
		{
			_begin = (int)packet.ReadBits(24);
			_end = (int)packet.ReadBits(24);
			_partitionSize = (int)packet.ReadBits(24) + 1;
			_classifications = (int)packet.ReadBits(6) + 1;
			_classBook = codebooks[(uint)packet.ReadBits(8)];
			_cascade = new int[_classifications];
			int num = 0;
			for (int i = 0; i < _classifications; i++)
			{
				int num2 = (int)packet.ReadBits(3);
				if (packet.ReadBit())
				{
					_cascade[i] = ((int)packet.ReadBits(5) << 3) | num2;
				}
				else
				{
					_cascade[i] = num2;
				}
				num += icount(_cascade[i]);
			}
			int[] array = new int[num];
			for (int j = 0; j < num; j++)
			{
				array[j] = (int)packet.ReadBits(8);
				if (codebooks[array[j]].MapType == 0)
				{
					throw new InvalidDataException();
				}
			}
			int entries = _classBook.Entries;
			int num3 = _classBook.Dimensions;
			int num4 = 1;
			while (num3 > 0)
			{
				num4 *= _classifications;
				if (num4 > entries)
				{
					throw new InvalidDataException();
				}
				num3--;
			}
			_books = new ICodebook[_classifications][];
			num = 0;
			int num5 = 0;
			for (int k = 0; k < _classifications; k++)
			{
				int num6 = Utils.ilog(_cascade[k]);
				_books[k] = new ICodebook[num6];
				if (num6 <= 0)
				{
					continue;
				}
				num5 = Math.Max(num5, num6);
				for (int l = 0; l < num6; l++)
				{
					if ((_cascade[k] & (1 << l)) > 0)
					{
						_books[k][l] = codebooks[array[num++]];
					}
				}
			}
			_maxStages = num5;
			_decodeMap = new int[num4][];
			for (int m = 0; m < num4; m++)
			{
				int num7 = m;
				int num8 = num4 / _classifications;
				_decodeMap[m] = new int[_classBook.Dimensions];
				for (int n = 0; n < _classBook.Dimensions; n++)
				{
					int num9 = num7 / num8;
					num7 -= num9 * num8;
					num8 /= _classifications;
					_decodeMap[m][n] = num9;
				}
			}
			_channels = channels;
		}

		public virtual void Decode(IPacket packet, bool[] doNotDecodeChannel, int blockSize, float[][] buffer)
		{
			int num = ((_end < blockSize / 2) ? _end : (blockSize / 2)) - _begin;
			if (num <= 0 || Array.IndexOf(doNotDecodeChannel, value: false) == -1)
			{
				return;
			}
			int num2 = num / _partitionSize;
			int num3 = (num2 + _classBook.Dimensions - 1) / _classBook.Dimensions;
			int[,][] array = new int[_channels, num3][];
			for (int i = 0; i < _maxStages; i++)
			{
				int j = 0;
				int num4 = 0;
				while (j < num2)
				{
					if (i == 0)
					{
						for (int k = 0; k < _channels; k++)
						{
							int num5 = _classBook.DecodeScalar(packet);
							if (num5 >= 0 && num5 < _decodeMap.Length)
							{
								array[k, num4] = _decodeMap[num5];
								continue;
							}
							j = num2;
							i = _maxStages;
							break;
						}
					}
					int num6 = 0;
					for (; j < num2; j++)
					{
						if (num6 >= _classBook.Dimensions)
						{
							break;
						}
						int offset = _begin + j * _partitionSize;
						for (int l = 0; l < _channels; l++)
						{
							int num7 = array[l, num4][num6];
							if ((_cascade[num7] & (1 << i)) != 0)
							{
								ICodebook codebook = _books[num7][i];
								if (codebook != null && WriteVectors(codebook, packet, buffer, l, offset, _partitionSize))
								{
									j = num2;
									i = _maxStages;
									break;
								}
							}
						}
						num6++;
					}
					num4++;
				}
			}
		}

		protected virtual bool WriteVectors(ICodebook codebook, IPacket packet, float[][] residue, int channel, int offset, int partitionSize)
		{
			float[] array = residue[channel];
			int num = partitionSize / codebook.Dimensions;
			int[] array2 = new int[num];
			for (int i = 0; i < num; i++)
			{
				if ((array2[i] = codebook.DecodeScalar(packet)) == -1)
				{
					return true;
				}
			}
			for (int j = 0; j < codebook.Dimensions; j++)
			{
				int num2 = 0;
				while (num2 < num)
				{
					array[offset] += codebook[array2[num2], j];
					num2++;
					offset++;
				}
			}
			return false;
		}
	}
	internal class Residue1 : Residue0
	{
		protected override bool WriteVectors(ICodebook codebook, IPacket packet, float[][] residue, int channel, int offset, int partitionSize)
		{
			float[] array = residue[channel];
			int num = 0;
			while (num < partitionSize)
			{
				int num2 = codebook.DecodeScalar(packet);
				if (num2 == -1)
				{
					return true;
				}
				for (int i = 0; i < codebook.Dimensions; i++)
				{
					array[offset + num] += codebook[num2, i];
					num++;
				}
			}
			return false;
		}
	}
	internal class Residue2 : Residue0
	{
		private int _channels;

		public override void Init(IPacket packet, int channels, ICodebook[] codebooks)
		{
			_channels = channels;
			base.Init(packet, 1, codebooks);
		}

		public override void Decode(IPacket packet, bool[] doNotDecodeChannel, int blockSize, float[][] buffer)
		{
			base.Decode(packet, doNotDecodeChannel, blockSize * _channels, buffer);
		}

		protected override bool WriteVectors(ICodebook codebook, IPacket packet, float[][] residue, int channel, int offset, int partitionSize)
		{
			int num = 0;
			offset /= _channels;
			int num2 = 0;
			while (num2 < partitionSize)
			{
				int num3 = codebook.DecodeScalar(packet);
				if (num3 == -1)
				{
					return true;
				}
				int num4 = 0;
				while (num4 < codebook.Dimensions)
				{
					residue[num][offset] += codebook[num3, num4];
					if (++num == _channels)
					{
						num = 0;
						offset++;
					}
					num4++;
					num2++;
				}
			}
			return false;
		}
	}
	public sealed class StreamDecoder : IStreamDecoder, IDisposable
	{
		private NVorbis.Contracts.IPacketProvider _packetProvider;

		private IFactory _factory;

		private StreamStats _stats;

		private byte _channels;

		private int _sampleRate;

		private int _block0Size;

		private int _block1Size;

		private IMode[] _modes;

		private int _modeFieldBits;

		private string _vendor;

		private string[] _comments;

		private ITagData _tags;

		private long _currentPosition;

		private bool _hasClipped;

		private bool _hasPosition;

		private bool _eosFound;

		private float[][] _nextPacketBuf;

		private float[][] _prevPacketBuf;

		private int _prevPacketStart;

		private int _prevPacketEnd;

		private int _prevPacketStop;

		private static readonly byte[] PacketSignatureStream = new byte[11]
		{
			1, 118, 111, 114, 98, 105, 115, 0, 0, 0,
			0
		};

		private static readonly byte[] PacketSignatureComments = new byte[7] { 3, 118, 111, 114, 98, 105, 115 };

		private static readonly byte[] PacketSignatureBooks = new byte[7] { 5, 118, 111, 114, 98, 105, 115 };

		internal static Func<IFactory> CreateFactory { get; set; } = () => new Factory();


		public int Channels => _channels;

		public int SampleRate => _sampleRate;

		public int UpperBitrate { get; private set; }

		public int NominalBitrate { get; private set; }

		public int LowerBitrate { get; private set; }

		public ITagData Tags => _tags ?? (_tags = new TagData(_vendor, _comments));

		public TimeSpan TotalTime => TimeSpan.FromSeconds((double)TotalSamples / (double)_sampleRate);

		public long TotalSamples => (_packetProvider ?? throw new ObjectDisposedException("StreamDecoder")).GetGranuleCount();

		public TimeSpan TimePosition
		{
			get
			{
				return TimeSpan.FromSeconds((double)_currentPosition / (double)_sampleRate);
			}
			set
			{
				SeekTo(value);
			}
		}

		public long SamplePosition
		{
			get
			{
				return _currentPosition;
			}
			set
			{
				SeekTo(value);
			}
		}

		public bool ClipSamples { get; set; }

		public bool HasClipped => _hasClipped;

		public bool IsEndOfStream
		{
			get
			{
				if (_eosFound)
				{
					return _prevPacketBuf == null;
				}
				return false;
			}
		}

		public IStreamStats Stats => _stats;

		public StreamDecoder(NVorbis.Contracts.IPacketProvider packetProvider)
			: this(packetProvider, new Factory())
		{
		}

		internal StreamDecoder(NVorbis.Contracts.IPacketProvider packetProvider, IFactory factory)
		{
			_packetProvider = packetProvider ?? throw new ArgumentNullException("packetProvider");
			_factory = factory ?? throw new ArgumentNullException("factory");
			_stats = new StreamStats();
			_currentPosition = 0L;
			ClipSamples = true;
			IPacket packet = _packetProvider.PeekNextPacket();
			if (!ProcessHeaderPackets(packet))
			{
				_packetProvider = null;
				packet.Reset();
				throw GetInvalidStreamException(packet);
			}
		}

		private static Exception GetInvalidStreamException(IPacket packet)
		{
			try
			{
				ulong num = packet.ReadBits(64);
				if (num == 7233173838382854223L)
				{
					return new ArgumentException("Found OPUS bitstream.");
				}
				if ((num & 0xFF) == 127)
				{
					return new ArgumentException("Found FLAC bitstream.");
				}
				switch (num)
				{
				case 2314885909937746003uL:
					return new ArgumentException("Found Speex bitstream.");
				case 28254585843050854uL:
					return new ArgumentException("Found Skeleton metadata bitstream.");
				default:
					if ((num & 0xFFFFFFFFFFFF00L) == 27428895509214208L)
					{
						return new ArgumentException("Found Theora bitsream.");
					}
					return new ArgumentException("Could not find Vorbis data to decode.");
				}
			}
			finally
			{
				packet.Reset();
			}
		}

		private bool ProcessHeaderPackets(IPacket packet)
		{
			if (!ProcessHeaderPacket(packet, LoadStreamHeader, delegate
			{
				_packetProvider.GetNextPacket().Done();
			}))
			{
				return false;
			}
			if (!ProcessHeaderPacket(_packetProvider.GetNextPacket(), LoadComments, delegate(IPacket pkt)
			{
				pkt.Done();
			}))
			{
				return false;
			}
			if (!ProcessHeaderPacket(_packetProvider.GetNextPacket(), LoadBooks, delegate(IPacket pkt)
			{
				pkt.Done();
			}))
			{
				return false;
			}
			_currentPosition = 0L;
			ResetDecoder();
			return true;
		}

		private static bool ProcessHeaderPacket(IPacket packet, Func<IPacket, bool> processAction, Action<IPacket> doneAction)
		{
			if (packet != null)
			{
				try
				{
					return processAction(packet);
				}
				finally
				{
					doneAction(packet);
				}
			}
			return false;
		}

		private static bool ValidateHeader(IPacket packet, byte[] expected)
		{
			for (int i = 0; i < expected.Length; i++)
			{
				if (expected[i] != packet.ReadBits(8))
				{
					return false;
				}
			}
			return true;
		}

		private static string ReadString(IPacket packet)
		{
			int num = (int)packet.ReadBits(32);
			if (num == 0)
			{
				return string.Empty;
			}
			byte[] array = new byte[num];
			if (packet.Read(array, 0, num) < num)
			{
				throw new InvalidDataException("Could not read full string!");
			}
			return Encoding.UTF8.GetString(array);
		}

		private bool LoadStreamHeader(IPacket packet)
		{
			if (!ValidateHeader(packet, PacketSignatureStream))
			{
				return false;
			}
			_channels = (byte)packet.ReadBits(8);
			_sampleRate = (int)packet.ReadBits(32);
			UpperBitrate = (int)packet.ReadBits(32);
			NominalBitrate = (int)packet.ReadBits(32);
			LowerBitrate = (int)packet.ReadBits(32);
			_block0Size = 1 << (int)packet.ReadBits(4);
			_block1Size = 1 << (int)packet.ReadBits(4);
			if (NominalBitrate == 0 && UpperBitrate > 0 && LowerBitrate > 0)
			{
				NominalBitrate = (UpperBitrate + LowerBitrate) / 2;
			}
			_stats.SetSampleRate(_sampleRate);
			_stats.AddPacket(-1, packet.BitsRead, packet.BitsRemaining, packet.ContainerOverheadBits);
			return true;
		}

		private bool LoadComments(IPacket packet)
		{
			if (!ValidateHeader(packet, PacketSignatureComments))
			{
				return false;
			}
			_vendor = ReadString(packet);
			_comments = new string[packet.ReadBits(32)];
			for (int i = 0; i < _comments.Length; i++)
			{
				_comments[i] = ReadString(packet);
			}
			_stats.AddPacket(-1, packet.BitsRead, packet.BitsRemaining, packet.ContainerOverheadBits);
			return true;
		}

		private bool LoadBooks(IPacket packet)
		{
			if (!ValidateHeader(packet, PacketSignatureBooks))
			{
				return false;
			}
			IMdct mdct = _factory.CreateMdct();
			IHuffman huffman = _factory.CreateHuffman();
			ICodebook[] array = new ICodebook[packet.ReadBits(8) + 1];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = _factory.CreateCodebook();
				array[i].Init(packet, huffman);
			}
			int num = (int)packet.ReadBits(6) + 1;
			packet.SkipBits(16 * num);
			IFloor[] array2 = new IFloor[packet.ReadBits(6) + 1];
			for (int j = 0; j < array2.Length; j++)
			{
				array2[j] = _factory.CreateFloor(packet);
				array2[j].Init(packet, _channels, _block0Size, _block1Size, array);
			}
			IResidue[] array3 = new IResidue[packet.ReadBits(6) + 1];
			for (int k = 0; k < array3.Length; k++)
			{
				array3[k] = _factory.CreateResidue(packet);
				array3[k].Init(packet, _channels, array);
			}
			IMapping[] array4 = new IMapping[packet.ReadBits(6) + 1];
			for (int l = 0; l < array4.Length; l++)
			{
				array4[l] = _factory.CreateMapping(packet);
				array4[l].Init(packet, _channels, array2, array3, mdct);
			}
			_modes = new IMode[packet.ReadBits(6) + 1];
			for (int m = 0; m < _modes.Length; m++)
			{
				_modes[m] = _factory.CreateMode();
				_modes[m].Init(packet, _channels, _block0Size, _block1Size, array4);
			}
			if (!packet.ReadBit())
			{
				throw new InvalidDataException("Book packet did not end on correct bit!");
			}
			_modeFieldBits = Utils.ilog(_modes.Length - 1);
			_stats.AddPacket(-1, packet.BitsRead, packet.BitsRemaining, packet.ContainerOverheadBits);
			return true;
		}

		private void ResetDecoder()
		{
			_prevPacketBuf = null;
			_prevPacketStart = 0;
			_prevPacketEnd = 0;
			_prevPacketStop = 0;
			_nextPacketBuf = null;
			_eosFound = false;
			_hasClipped = false;
			_hasPosition = false;
		}

		public int Read(Span<float> buffer, int offset, int count)
		{
			if (buffer == null)
			{
				throw new ArgumentNullException("buffer");
			}
			if (offset < 0 || offset + count > buffer.Length)
			{
				throw new ArgumentOutOfRangeException("offset");
			}
			if (count % _channels != 0)
			{
				throw new ArgumentOutOfRangeException("count", "Must be a multiple of Channels!");
			}
			if (_packetProvider == null)
			{
				throw new ObjectDisposedException("StreamDecoder");
			}
			if (count == 0)
			{
				return 0;
			}
			int num = offset;
			int num2 = offset + count;
			while (num < num2)
			{
				if (_prevPacketStart == _prevPacketEnd)
				{
					if (_eosFound)
					{
						_nextPacketBuf = null;
						_prevPacketBuf = null;
						break;
					}
					if (!ReadNextPacket((num - offset) / _channels, out var samplePosition))
					{
						_prevPacketEnd = _prevPacketStop;
					}
					if (samplePosition.HasValue && !_hasPosition)
					{
						_hasPosition = true;
						_currentPosition = samplePosition.Value - (_prevPacketEnd - _prevPacketStart) - (num - offset) / _channels;
					}
				}
				int num3 = Math.Min((num2 - num) / _channels, _prevPacketEnd - _prevPacketStart);
				if (num3 > 0)
				{
					num = ((!ClipSamples) ? (num + CopyBuffer(buffer, num, num3)) : (num + ClippingCopyBuffer(buffer, num, num3)));
				}
			}
			count = num - offset;
			_currentPosition += count / _channels;
			return count;
		}

		private int ClippingCopyBuffer(Span<float> target, int targetIndex, int count)
		{
			int num = targetIndex;
			while (count > 0)
			{
				for (int i = 0; i < _channels; i++)
				{
					target[num++] = Utils.ClipValue(_prevPacketBuf[i][_prevPacketStart], ref _hasClipped);
				}
				_prevPacketStart++;
				count--;
			}
			return num - targetIndex;
		}

		private int CopyBuffer(Span<float> target, int targetIndex, int count)
		{
			int num = targetIndex;
			while (count > 0)
			{
				for (int i = 0; i < _channels; i++)
				{
					target[num++] = _prevPacketBuf[i][_prevPacketStart];
				}
				_prevPacketStart++;
				count--;
			}
			return num - targetIndex;
		}

		private bool ReadNextPacket(int bufferedSamples, out long? samplePosition)
		{
			int packetStartindex;
			int packetValidLength;
			int packetTotalLength;
			bool isEndOfStream;
			int bitsRead;
			int bitsRemaining;
			int containerOverheadBits;
			float[][] array = DecodeNextPacket(out packetStartindex, out packetValidLength, out packetTotalLength, out isEndOfStream, out samplePosition, out bitsRead, out bitsRemaining, out containerOverheadBits);
			_eosFound |= isEndOfStream;
			if (array == null)
			{
				_stats.AddPacket(0, bitsRead, bitsRemaining, containerOverheadBits);
				return false;
			}
			if (samplePosition.HasValue && isEndOfStream)
			{
				long num = _currentPosition + bufferedSamples + packetValidLength - packetStartindex;
				int num2 = (int)(samplePosition.Value - num);
				if (num2 < 0)
				{
					packetValidLength += num2;
				}
			}
			if (_prevPacketEnd > 0)
			{
				OverlapBuffers(_prevPacketBuf, array, _prevPacketStart, _prevPacketStop, packetStartindex, _channels);
				_prevPacketStart = packetStartindex;
			}
			else if (_prevPacketBuf == null)
			{
				_prevPacketStart = packetValidLength;
			}
			_stats.AddPacket(packetValidLength - _prevPacketStart, bitsRead, bitsRemaining, containerOverheadBits);
			_nextPacketBuf = _prevPacketBuf;
			_prevPacketEnd = packetValidLength;
			_prevPacketStop = packetTotalLength;
			_prevPacketBuf = array;
			return true;
		}

		private float[][] DecodeNextPacket(out int packetStartindex, out int packetValidLength, out int packetTotalLength, out bool isEndOfStream, out long? samplePosition, out int bitsRead, out int bitsRemaining, out int containerOverheadBits)
		{
			IPacket packet = null;
			try
			{
				if ((packet = _packetProvider.GetNextPacket()) == null)
				{
					isEndOfStream = true;
				}
				else
				{
					isEndOfStream = packet.IsEndOfStream;
					if (packet.IsResync)
					{
						_hasPosition = false;
					}
					containerOverheadBits = packet.ContainerOverheadBits;
					if (packet.ReadBit())
					{
						bitsRemaining = packet.BitsRemaining + 1;
					}
					else
					{
						IMode mode = _modes[(uint)packet.ReadBits(_modeFieldBits)];
						if (_nextPacketBuf == null)
						{
							_nextPacketBuf = new float[_channels][];
							for (int i = 0; i < _channels; i++)
							{
								_nextPacketBuf[i] = new float[_block1Size];
							}
						}
						if (mode.Decode(packet, _nextPacketBuf, out packetStartindex, out packetValidLength, out packetTotalLength))
						{
							samplePosition = packet.GranulePosition;
							bitsRead = packet.BitsRead;
							bitsRemaining = packet.BitsRemaining;
							return _nextPacketBuf;
						}
						bitsRemaining = packet.BitsRead + packet.BitsRemaining;
					}
				}
				packetStartindex = 0;
				packetValidLength = 0;
				packetTotalLength = 0;
				samplePosition = null;
				bitsRead = 0;
				bitsRemaining = 0;
				containerOverheadBits = 0;
				return null;
			}
			finally
			{
				packet?.Done();
			}
		}

		private static void OverlapBuffers(float[][] previous, float[][] next, int prevStart, int prevLen, int nextStart, int channels)
		{
			while (prevStart < prevLen)
			{
				for (int i = 0; i < channels; i++)
				{
					next[i][nextStart] += previous[i][prevStart];
				}
				prevStart++;
				nextStart++;
			}
		}

		public void SeekTo(TimeSpan timePosition, SeekOrigin seekOrigin = SeekOrigin.Begin)
		{
			SeekTo((long)((double)SampleRate * timePosition.TotalSeconds), seekOrigin);
		}

		public void SeekTo(long samplePosition, SeekOrigin seekOrigin = SeekOrigin.Begin)
		{
			if (_packetProvider == null)
			{
				throw new ObjectDisposedException("StreamDecoder");
			}
			if (!_packetProvider.CanSeek)
			{
				throw new InvalidOperationException("Seek is not supported by the Contracts.IPacketProvider instance.");
			}
			switch (seekOrigin)
			{
			case SeekOrigin.Current:
				samplePosition = SamplePosition - samplePosition;
				break;
			case SeekOrigin.End:
				samplePosition = TotalSamples - samplePosition;
				break;
			default:
				throw new ArgumentOutOfRangeException("seekOrigin");
			case SeekOrigin.Begin:
				break;
			}
			if (samplePosition < 0)
			{
				throw new ArgumentOutOfRangeException("samplePosition");
			}
			int num;
			if (samplePosition == 0L)
			{
				_packetProvider.SeekTo(0L, 0, GetPacketGranules);
				num = 0;
			}
			else
			{
				long num2 = _packetProvider.SeekTo(samplePosition, 1, GetPacketGranules);
				num = (int)(samplePosition - num2);
			}
			ResetDecoder();
			_hasPosition = true;
			if (!ReadNextPacket(0, out var samplePosition2))
			{
				_eosFound = true;
				if (_packetProvider.GetGranuleCount() != samplePosition)
				{
					throw new InvalidOperationException("Could not read pre-roll packet!  Try seeking again prior to reading more samples.");
				}
				_prevPacketStart = _prevPacketStop;
				_currentPosition = samplePosition;
				return;
			}
			if (!ReadNextPacket(0, out samplePosition2))
			{
				ResetDecoder();
				_eosFound = true;
				throw new InvalidOperationException("Could not read pre-roll packet!  Try seeking again prior to reading more samples.");
			}
			_prevPacketStart += num;
			_currentPosition = samplePosition;
		}

		private int GetPacketGranules(IPacket curPacket)
		{
			if (curPacket.IsResync)
			{
				return 0;
			}
			if (curPacket.ReadBit())
			{
				return 0;
			}
			int num = (int)curPacket.ReadBits(_modeFieldBits);
			if (num < 0 || num >= _modes.Length)
			{
				return 0;
			}
			return _modes[num].GetPacketSampleCount(curPacket);
		}

		public void Dispose()
		{
			(_packetProvider as IDisposable)?.Dispose();
			_packetProvider = null;
		}
	}
	internal class StreamStats : IStreamStats
	{
		private int _sampleRate;

		private readonly int[] _packetBits = new int[2];

		private readonly int[] _packetSamples = new int[2];

		private int _packetIndex;

		private long _totalSamples;

		private long _audioBits;

		private long _headerBits;

		private long _containerBits;

		private long _wasteBits;

		private object _lock = new object();

		private int _packetCount;

		public int EffectiveBitRate
		{
			get
			{
				long totalSamples;
				long num;
				lock (_lock)
				{
					totalSamples = _totalSamples;
					num = _audioBits + _headerBits + _containerBits + _wasteBits;
				}
				if (totalSamples > 0)
				{
					return (int)((double)num / (double)totalSamples * (double)_sampleRate);
				}
				return 0;
			}
		}

		public int InstantBitRate
		{
			get
			{
				int num;
				int num2;
				lock (_lock)
				{
					num = _packetBits[0] + _packetBits[1];
					num2 = _packetSamples[0] + _packetSamples[1];
				}
				if (num2 > 0)
				{
					return (int)((double)num / (double)num2 * (double)_sampleRate);
				}
				return 0;
			}
		}

		public long ContainerBits => _containerBits;

		public long OverheadBits => _headerBits;

		public long AudioBits => _audioBits;

		public long WasteBits => _wasteBits;

		public int PacketCount => _packetCount;

		public void ResetStats()
		{
			lock (_lock)
			{
				_packetBits[0] = (_packetBits[1] = 0);
				_packetSamples[0] = (_packetSamples[1] = 0);
				_packetIndex = 0;
				_packetCount = 0;
				_audioBits = 0L;
				_totalSamples = 0L;
				_headerBits = 0L;
				_containerBits = 0L;
				_wasteBits = 0L;
			}
		}

		internal void SetSampleRate(int sampleRate)
		{
			lock (_lock)
			{
				_sampleRate = sampleRate;
				ResetStats();
			}
		}

		internal void AddPacket(int samples, int bits, int waste, int container)
		{
			lock (_lock)
			{
				if (samples >= 0)
				{
					_audioBits += bits;
					_wasteBits += waste;
					_containerBits += container;
					_totalSamples += samples;
					_packetBits[_packetIndex] = bits + waste;
					_packetSamples[_packetIndex] = samples;
					if (++_packetIndex == 2)
					{
						_packetIndex = 0;
					}
				}
				else
				{
					_headerBits += bits;
					_wasteBits += waste;
					_containerBits += container;
				}
			}
		}
	}
	internal class TagData : ITagData
	{
		private static IReadOnlyList<string> s_emptyList = new List<string>();

		private Dictionary<string, IReadOnlyList<string>> _tags;

		public IReadOnlyDictionary<string, IReadOnlyList<string>> All => _tags;

		public string EncoderVendor { get; }

		public string Title => GetTagSingle("TITLE");

		public string Version => GetTagSingle("VERSION");

		public string Album => GetTagSingle("ALBUM");

		public string TrackNumber => GetTagSingle("TRACKNUMBER");

		public string Artist => GetTagSingle("ARTIST");

		public IReadOnlyList<string> Performers => GetTagMulti("PERFORMER");

		public string Copyright => GetTagSingle("COPYRIGHT");

		public string License => GetTagSingle("LICENSE");

		public string Organization => GetTagSingle("ORGANIZATION");

		public string Description => GetTagSingle("DESCRIPTION");

		public IReadOnlyList<string> Genres => GetTagMulti("GENRE");

		public IReadOnlyList<string> Dates => GetTagMulti("DATE");

		public IReadOnlyList<string> Locations => GetTagMulti("LOCATION");

		public string Contact => GetTagSingle("CONTACT");

		public string Isrc => GetTagSingle("ISRC");

		public TagData(string vendor, string[] comments)
		{
			EncoderVendor = vendor;
			Dictionary<string, IReadOnlyList<string>> dictionary = new Dictionary<string, IReadOnlyList<string>>();
			for (int i = 0; i < comments.Length; i++)
			{
				string[] array = comments[i].Split(new char[1] { '=' });
				if (array.Length == 1)
				{
					array = new string[2]
					{
						array[0],
						string.Empty
					};
				}
				int num = array[0].IndexOf('[');
				if (num > -1)
				{
					array[1] = array[0].Substring(num + 1, array[0].Length - num - 2).ToUpper(CultureInfo.CurrentCulture) + ": " + array[1];
					array[0] = array[0].Substring(0, num);
				}
				if (dictionary.TryGetValue(array[0].ToUpperInvariant(), out var value))
				{
					((List<string>)value).Add(array[1]);
					continue;
				}
				dictionary.Add(array[0].ToUpperInvariant(), new List<string> { array[1] });
			}
			_tags = dictionary;
		}

		public string GetTagSingle(string key, bool concatenate = false)
		{
			IReadOnlyList<string> tagMulti = GetTagMulti(key);
			if (tagMulti.Count > 0)
			{
				if (concatenate)
				{
					return string.Join(Environment.NewLine, tagMulti.ToArray());
				}
				return tagMulti[tagMulti.Count - 1];
			}
			return string.Empty;
		}

		public IReadOnlyList<string> GetTagMulti(string key)
		{
			if (_tags.TryGetValue(key.ToUpperInvariant(), out var value))
			{
				return value;
			}
			return s_emptyList;
		}
	}
	internal static class Utils
	{
		internal static int ilog(int x)
		{
			int num = 0;
			while (x > 0)
			{
				num++;
				x >>= 1;
			}
			return num;
		}

		internal static uint BitReverse(uint n)
		{
			return BitReverse(n, 32);
		}

		internal static uint BitReverse(uint n, int bits)
		{
			n = ((n & 0xAAAAAAAAu) >> 1) | ((n & 0x55555555) << 1);
			n = ((n & 0xCCCCCCCCu) >> 2) | ((n & 0x33333333) << 2);
			n = ((n & 0xF0F0F0F0u) >> 4) | ((n & 0xF0F0F0F) << 4);
			n = ((n & 0xFF00FF00u) >> 8) | ((n & 0xFF00FF) << 8);
			return ((n >> 16) | (n << 16)) >> 32 - bits;
		}

		internal static float ClipValue(float value, ref bool clipped)
		{
			if (value > MathF.PI * 113f / 355f)
			{
				clipped = true;
				return MathF.PI * 113f / 355f;
			}
			if (value < MathF.PI * -113f / 355f)
			{
				clipped = true;
				return MathF.PI * -113f / 355f;
			}
			return value;
		}

		internal static float ConvertFromVorbisFloat32(uint bits)
		{
			int num = (int)bits >> 31;
			double y = (int)(((bits & 0x7FE00000) >> 21) - 788);
			return (float)(((bits & 0x1FFFFF) ^ num) + (num & 1)) * (float)Math.Pow(2.0, y);
		}
	}
	public sealed class VorbisReader : IVorbisReader, IDisposable
	{
		private readonly List<IStreamDecoder> _decoders;

		private readonly NVorbis.Contracts.IContainerReader _containerReader;

		private readonly bool _closeOnDispose;

		private IStreamDecoder _streamDecoder;

		internal static Func<Stream, bool, NVorbis.Contracts.IContainerReader> CreateContainerReader { get; set; } = (Stream s, bool cod) => new ContainerReader(s, cod);


		internal static Func<NVorbis.Contracts.IPacketProvider, IStreamDecoder> CreateStreamDecoder { get; set; } = (NVorbis.Contracts.IPacketProvider pp) => new StreamDecoder(pp, new Factory());


		public IReadOnlyList<IStreamDecoder> Streams => _decoders;

		public int Channels => _streamDecoder.Channels;

		public int SampleRate => _streamDecoder.SampleRate;

		public int UpperBitrate => _streamDecoder.UpperBitrate;

		public int NominalBitrate => _streamDecoder.NominalBitrate;

		public int LowerBitrate => _streamDecoder.LowerBitrate;

		public ITagData Tags => _streamDecoder.Tags;

		[Obsolete("Use .Tags.EncoderVendor instead.")]
		public string Vendor => _streamDecoder.Tags.EncoderVendor;

		[Obsolete("Use .Tags.All instead.")]
		public string[] Comments => _streamDecoder.Tags.All.SelectMany((KeyValuePair<string, IReadOnlyList<string>> k) => k.Value, (KeyValuePair<string, IReadOnlyList<string>> kvp, string Item) => kvp.Key + "=" + Item).ToArray();

		[Obsolete("No longer supported.  Will receive a new stream when parameters change.", true)]
		public bool IsParameterChange
		{
			get
			{
				throw new NotSupportedException();
			}
		}

		public long ContainerOverheadBits => _containerReader?.ContainerBits ?? 0;

		public long ContainerWasteBits => _containerReader?.WasteBits ?? 0;

		public int StreamIndex => _decoders.IndexOf(_streamDecoder);

		[Obsolete("Use .Streams.Count instead.")]
		public int StreamCount => _decoders.Count;

		[Obsolete("Use VorbisReader.TimePosition instead.")]
		public TimeSpan DecodedTime
		{
			get
			{
				return _streamDecoder.TimePosition;
			}
			set
			{
				TimePosition = value;
			}
		}

		[Obsolete("Use VorbisReader.SamplePosition instead.")]
		public long DecodedPosition
		{
			get
			{
				return _streamDecoder.SamplePosition;
			}
			set
			{
				SamplePosition = value;
			}
		}

		public TimeSpan TotalTime => _streamDecoder.TotalTime;

		public long TotalSamples => _streamDecoder.TotalSamples;

		public TimeSpan TimePosition
		{
			get
			{
				return _streamDecoder.TimePosition;
			}
			set
			{
				_streamDecoder.TimePosition = value;
			}
		}

		public long SamplePosition
		{
			get
			{
				return _streamDecoder.SamplePosition;
			}
			set
			{
				_streamDecoder.SamplePosition = value;
			}
		}

		public bool IsEndOfStream => _streamDecoder.IsEndOfStream;

		public bool ClipSamples
		{
			get
			{
				return _streamDecoder.ClipSamples;
			}
			set
			{
				_streamDecoder.ClipSamples = value;
			}
		}

		public bool HasClipped => _streamDecoder.HasClipped;

		public IStreamStats StreamStats => _streamDecoder.Stats;

		[Obsolete("Use Streams[*].Stats instead.", true)]
		public IVorbisStreamStatus[] Stats
		{
			get
			{
				throw new NotSupportedException();
			}
		}

		public event EventHandler<NewStreamEventArgs> NewStream;

		public VorbisReader(string fileName)
			: this(File.OpenRead(fileName))
		{
		}

		public VorbisReader(Stream stream, bool closeOnDispose = true)
		{
			_decoders = new List<IStreamDecoder>();
			NVorbis.Contracts.IContainerReader containerReader = CreateContainerReader(stream, closeOnDispose);
			containerReader.NewStreamCallback = ProcessNewStream;
			if (!containerReader.TryInit() || _decoders.Count == 0)
			{
				containerReader.NewStreamCallback = null;
				containerReader.Dispose();
				if (closeOnDispose)
				{
					stream.Dispose();
				}
				throw new ArgumentException("Could not load the specified container!", "containerReader");
			}
			_closeOnDispose = closeOnDispose;
			_containerReader = containerReader;
			_streamDecoder = _decoders[0];
		}

		[Obsolete("Use \"new StreamDecoder(Contracts.IPacketProvider)\" and the container's NewStreamCallback or Streams property instead.", true)]
		public VorbisReader(NVorbis.Contracts.IContainerReader containerReader)
		{
			throw new NotSupportedException();
		}

		[Obsolete("Use \"new StreamDecoder(Contracts.IPacketProvider)\" instead.", true)]
		public VorbisReader(NVorbis.Contracts.IPacketProvider packetProvider)
		{
			throw new NotSupportedException();
		}

		private bool ProcessNewStream(NVorbis.Contracts.IPacketProvider packetProvider)
		{
			IStreamDecoder streamDecoder = CreateStreamDecoder(packetProvider);
			streamDecoder.ClipSamples = true;
			NewStreamEventArgs newStreamEventArgs = new NewStreamEventArgs(streamDecoder);
			this.NewStream?.Invoke(this, newStreamEventArgs);
			if (!newStreamEventArgs.IgnoreStream)
			{
				_decoders.Add(streamDecoder);
				return true;
			}
			return false;
		}

		public void Dispose()
		{
			if (_decoders != null)
			{
				foreach (IStreamDecoder decoder in _decoders)
				{
					decoder.Dispose();
				}
				_decoders.Clear();
			}
			if (_containerReader != null)
			{
				_containerReader.NewStreamCallback = null;
				if (_closeOnDispose)
				{
					_containerReader.Dispose();
				}
			}
		}

		public bool FindNextStream()
		{
			if (_containerReader == null)
			{
				return false;
			}
			return _containerReader.FindNextStream();
		}

		public bool SwitchStreams(int index)
		{
			if (index < 0 || index >= _decoders.Count)
			{
				throw new ArgumentOutOfRangeException("index");
			}
			IStreamDecoder streamDecoder = _decoders[index];
			IStreamDecoder streamDecoder2 = _streamDecoder;
			if (streamDecoder == streamDecoder2)
			{
				return false;
			}
			streamDecoder.ClipSamples = streamDecoder2.ClipSamples;
			_streamDecoder = streamDecoder;
			if (streamDecoder.Channels == streamDecoder2.Channels)
			{
				return streamDecoder.SampleRate != streamDecoder2.SampleRate;
			}
			return true;
		}

		public void SeekTo(TimeSpan timePosition, SeekOrigin seekOrigin = SeekOrigin.Begin)
		{
			_streamDecoder.SeekTo(timePosition, seekOrigin);
		}

		public void SeekTo(long samplePosition, SeekOrigin seekOrigin = SeekOrigin.Begin)
		{
			_streamDecoder.SeekTo(samplePosition, seekOrigin);
		}

		public int ReadSamples(float[] buffer, int offset, int count)
		{
			count -= count % _streamDecoder.Channels;
			if (count > 0)
			{
				return _streamDecoder.Read(buffer, offset, count);
			}
			return 0;
		}

		public int ReadSamples(Span<float> buffer)
		{
			int count = buffer.Length - buffer.Length % _streamDecoder.Channels;
			if (!buffer.IsEmpty)
			{
				return _streamDecoder.Read(buffer, 0, count);
			}
			return 0;
		}

		[Obsolete("No longer needed.", true)]
		public void ClearParameterChange()
		{
			throw new NotSupportedException();
		}
	}
}
namespace NVorbis.Ogg
{
	public sealed class ContainerReader : NVorbis.Contracts.IContainerReader, IDisposable
	{
		private IPageReader _reader;

		private List<WeakReference<NVorbis.Contracts.IPacketProvider>> _packetProviders;

		private bool _foundStream;

		internal static Func<Stream, bool, Func<NVorbis.Contracts.IPacketProvider, bool>, IPageReader> CreatePageReader { get; set; } = (Stream s, bool cod, Func<NVorbis.Contracts.IPacketProvider, bool> cb) => new PageReader(s, cod, cb);


		internal static Func<Stream, bool, Func<NVorbis.Contracts.IPacketProvider, bool>, IPageReader> CreateForwardOnlyPageReader { get; set; } = (Stream s, bool cod, Func<NVorbis.Contracts.IPacketProvider, bool> cb) => new ForwardOnlyPageReader(s, cod, cb);


		public NewStreamHandler NewStreamCallback { get; set; }

		public bool CanSeek { get; }

		public long WasteBits => _reader.WasteBits;

		public long ContainerBits => _reader.ContainerBits;

		public IReadOnlyList<NVorbis.Contracts.IPacketProvider> GetStreams()
		{
			List<NVorbis.Contracts.IPacketProvider> list = new List<NVorbis.Contracts.IPacketProvider>(_packetProviders.Count);
			for (int i = 0; i < _packetProviders.Count; i++)
			{
				if (_packetProviders[i].TryGetTarget(out var target))
				{
					list.Add(target);
					continue;
				}
				list.RemoveAt(i);
				i--;
			}
			return list;
		}

		public ContainerReader(Stream stream, bool closeOnDispose)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			_packetProviders = new List<WeakReference<NVorbis.Contracts.IPacketProvider>>();
			if (stream.CanSeek)
			{
				_reader = CreatePageReader(stream, closeOnDispose, ProcessNewStream);
				CanSeek = true;
			}
			else
			{
				_reader = CreateForwardOnlyPageReader(stream, closeOnDispose, ProcessNewStream);
			}
		}

		public bool TryInit()
		{
			return FindNextStream();
		}

		public bool FindNextStream()
		{
			_reader.Lock();
			try
			{
				_foundStream = false;
				while (_reader.ReadNextPage())
				{
					if (_foundStream)
					{
						return true;
					}
				}
				return false;
			}
			finally
			{
				_reader.Release();
			}
		}

		private bool ProcessNewStream(NVorbis.Contracts.IPacketProvider packetProvider)
		{
			bool flag = _reader.Release();
			try
			{
				NewStreamHandler newStreamCallback = NewStreamCallback;
				if (newStreamCallback == null || newStreamCallback(packetProvider))
				{
					_packetProviders.Add(new WeakReference<NVorbis.Contracts.IPacketProvider>(packetProvider));
					_foundStream = true;
					return true;
				}
				return false;
			}
			finally
			{
				if (flag)
				{
					_reader.Lock();
				}
			}
		}

		public void Dispose()
		{
			_reader?.Dispose();
			_reader = null;
		}
	}
	internal class Crc : ICrc
	{
		private const uint CRC32_POLY = 79764919u;

		private static readonly uint[] s_crcTable;

		private uint _crc;

		static Crc()
		{
			s_crcTable = new uint[256];
			for (uint num = 0u; num < 256; num++)
			{
				uint num2 = num << 24;
				for (int i = 0; i < 8; i++)
				{
					num2 = (num2 << 1) ^ ((num2 >= 2147483648u) ? 79764919u : 0u);
				}
				s_crcTable[num] = num2;
			}
		}

		public Crc()
		{
			Reset();
		}

		public void Reset()
		{
			_crc = 0u;
		}

		public void Update(int nextVal)
		{
			_crc = (_crc << 8) ^ s_crcTable[nextVal ^ (_crc >> 24)];
		}

		public bool Test(uint checkCrc)
		{
			return _crc == checkCrc;
		}
	}
	internal class ForwardOnlyPacketProvider : DataPacket, IForwardOnlyPacketProvider, NVorbis.Contracts.IPacketProvider
	{
		private int _lastSeqNo;

		private readonly Queue<(byte[] buf, bool isResync)> _pageQueue = new Queue<(byte[], bool)>();

		private readonly IPageReader _reader;

		private byte[] _pageBuf;

		private int _packetIndex;

		private bool _isEndOfStream;

		private int _dataStart;

		private bool _lastWasPeek;

		private Memory<byte> _packetBuf;

		private int _dataIndex;

		public bool CanSeek => false;

		public int StreamSerial { get; }

		protected override int TotalBits => _packetBuf.Length * 8;

		public ForwardOnlyPacketProvider(IPageReader reader, int streamSerial)
		{
			_reader = reader;
			StreamSerial = streamSerial;
			_packetIndex = int.MaxValue;
		}

		public bool AddPage(byte[] buf, bool isResync)
		{
			if ((buf[5] & 2u) != 0)
			{
				if (_isEndOfStream)
				{
					return false;
				}
				isResync = true;
				_lastSeqNo = BitConverter.ToInt32(buf, 18);
			}
			else
			{
				int num = BitConverter.ToInt32(buf, 18);
				isResync |= num != _lastSeqNo + 1;
				_lastSeqNo = num;
			}
			int num2 = 0;
			for (int i = 0; i < buf[26]; i++)
			{
				num2 += buf[27 + i];
			}
			if (num2 == 0)
			{
				return false;
			}
			_pageQueue.Enqueue((buf, isResync));
			return true;
		}

		public void SetEndOfStream()
		{
			_isEndOfStream = true;
		}

		public IPacket GetNextPacket()
		{
			if (_packetBuf.Length > 0)
			{
				if (!_lastWasPeek)
				{
					throw new InvalidOperationException("Must call Done() on previous packet first.");
				}
				_lastWasPeek = false;
				return this;
			}
			_lastWasPeek = false;
			if (GetPacket())
			{
				return this;
			}
			return null;
		}

		public IPacket PeekNextPacket()
		{
			if (_packetBuf.Length > 0)
			{
				if (!_lastWasPeek)
				{
					throw new InvalidOperationException("Must call Done() on previous packet first.");
				}
				return this;
			}
			_lastWasPeek = true;
			if (GetPacket())
			{
				return this;
			}
			return null;
		}

		private bool GetPacket()
		{
			byte[] pageBuf;
			bool isResync;
			int packetIndex;
			bool isContinuation;
			bool isContinued;
			int dataStart;
			if (_pageBuf != null && _packetIndex < 27 + _pageBuf[26])
			{
				pageBuf = _pageBuf;
				isResync = false;
				dataStart = _dataStart;
				packetIndex = _packetIndex;
				isContinuation = false;
				isContinued = pageBuf[26 + pageBuf[26]] == byte.MaxValue;
			}
			else if (!ReadNextPage(out pageBuf, out isResync, out dataStart, out packetIndex, out isContinuation, out isContinued))
			{
				return false;
			}
			int num = dataStart;
			bool flag = packetIndex == 27;
			if (isContinuation && flag)
			{
				isResync = true;
				num += GetPacketLength(pageBuf, ref packetIndex);
				if (packetIndex == 27 + pageBuf[26])
				{
					return GetPacket();
				}
			}
			if (!flag)
			{
				num = 0;
			}
			int packetLength = GetPacketLength(pageBuf, ref packetIndex);
			Memory<byte> memory = new Memory<byte>(pageBuf, dataStart, packetLength);
			dataStart += packetLength;
			bool flag2 = packetIndex == 27 + pageBuf[26];
			if (isContinued)
			{
				if (flag2)
				{
					flag2 = false;
				}
				else
				{
					int packetIndex2 = packetIndex;
					GetPacketLength(pageBuf, ref packetIndex2);
					flag2 = packetIndex2 == 27 + pageBuf[26];
				}
			}
			bool flag3 = false;
			long? granulePosition = null;
			if (flag2)
			{
				granulePosition = BitConverter.ToInt64(pageBuf, 6);
				if ((pageBuf[5] & 4u) != 0 || (_isEndOfStream && _pageQueue.Count == 0))
				{
					flag3 = true;
				}
			}
			else
			{
				while (isContinued && packetIndex == 27 + pageBuf[26] && ReadNextPage(out pageBuf, out isResync, out dataStart, out packetIndex, out isContinuation, out isContinued) && !isResync && isContinuation)
				{
					num += 27 + pageBuf[26];
					Memory<byte> memory2 = memory;
					int packetLength2 = GetPacketLength(pageBuf, ref packetIndex);
					memory = new Memory<byte>(new byte[memory2.Length + packetLength2]);
					memory2.CopyTo(memory);
					new Memory<byte>(pageBuf, dataStart, packetLength2).CopyTo(memory.Slice(memory2.Length));
					dataStart += packetLength2;
				}
			}
			base.IsResync = isResync;
			base.GranulePosition = granulePosition;
			base.IsEndOfStream = flag3;
			base.ContainerOverheadBits = num * 8;
			_pageBuf = pageBuf;
			_dataStart = dataStart;
			_packetIndex = packetIndex;
			_packetBuf = memory;
			_isEndOfStream |= flag3;
			Reset();
			return true;
		}

		private bool ReadNextPage(out byte[] pageBuf, out bool isResync, out int dataStart, out int packetIndex, out bool isContinuation, out bool isContinued)
		{
			while (_pageQueue.Count == 0)
			{
				if (_isEndOfStream || !_reader.ReadNextPage())
				{
					pageBuf = null;
					isResync = false;
					dataStart = 0;
					packetIndex = 0;
					isContinuation = false;
					isContinued = false;
					return false;
				}
			}
			(byte[], bool) tuple = _pageQueue.Dequeue();
			pageBuf = tuple.Item1;
			isResync = tuple.Item2;
			dataStart = pageBuf[26] + 27;
			packetIndex = 27;
			isContinuation = (pageBuf[5] & 1) != 0;
			isContinued = pageBuf[26 + pageBuf[26]] == byte.MaxValue;
			return true;
		}

		private int GetPacketLength(byte[] pageBuf, ref int packetIndex)
		{
			int num = 0;