Decompiled source of LethalGargoyles v0.5.0
plugins/LethalGargoyles/DropDaDeuce.LethalGargoyles.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.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
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.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;