Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalGargoyles v0.8.0
plugins/LethalGargoyles/DropDaDeuce.LethalGargoyles.dll
Decompiled a month 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 EnhancedMonsters.Utils; using GameNetcodeStuff; using HarmonyLib; using LethalGargoyles.src.Config; using LethalGargoyles.src.Enemy; using LethalGargoyles.src.Patch; using LethalGargoyles.src.Scrap; using LethalGargoyles.src.SoftDepends; using LethalGargoyles.src.Utility; using LethalLib.Modules; using Microsoft.CodeAnalysis; using NVorbis; using PathfindingLib.API.SmartPathfinding; using PathfindingLib.Utilities; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.Networking; using UnityEngine.SceneManagement; [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.8.0.0")] [assembly: AssemblyInformationalVersion("0.8.0+d1a9313eae80031bfcc362d7e985f604af1448cb")] [assembly: AssemblyProduct("LethalGargoyles")] [assembly: AssemblyTitle("DropDaDeuce.LethalGargoyles")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.8.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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.8.0"; } } namespace LethalGargoyles.src { [BepInPlugin("DropDaDeuce.LethalGargoyles", "LethalGargoyles", "0.8.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [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 AudioClip? stepSound; 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 bool IsEnhancedMonstersLoaded { 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); BoundConfig.InitializeDiagnostics(); IsCoronerLoaded = DepIsLoaded("com.elitemastereric.coroner"); IsEmployeeClassesLoaded = DepIsLoaded("Jade.EmployeeClasses"); IsEnhancedMonstersLoaded = DepIsLoaded("com.velddev.enhancedmonsters"); Instance = this; InitializeNetworkBehaviours(); string text = "gargoyleassets"; ModAssets = AssetBundle.LoadFromFile(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), text)); 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"); try { Directory.CreateDirectory(CustomAudioFolderPath); 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")); } catch (Exception ex) { Logger.LogError((object)("Could not create the Custom Voice Lines folders at '" + CustomAudioFolderPath + "': " + ex.GetType().Name + ": " + ex.Message + ". Custom voice lines will be unavailable; the rest of the mod will load normally.")); } EnemyType val = ModAssets.LoadAsset<EnemyType>("LethalGargoyle"); TerminalNode val2 = ModAssets.LoadAsset<TerminalNode>("LethalGargoyleTN"); TerminalKeyword val3 = ModAssets.LoadAsset<TerminalKeyword>("LethalGargoyleTK"); if ((Object)(object)val == (Object)null || (Object)(object)val.enemyPrefab == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { Logger.LogError((object)$"Failed to load the gargoyle from '{text}' (EnemyType={(Object)(object)val != (Object)null}, prefab={(Object)(object)val?.enemyPrefab != (Object)null}, TN={(Object)(object)val2 != (Object)null}, TK={(Object)(object)val3 != (Object)null}). The enemy will not be registered. This usually means the asset bundle is stale relative to the DLL."); return; } NetworkPrefabs.RegisterNetworkPrefab(val.enemyPrefab); Enemies.RegisterEnemy(val, BoundConfig.SpawnWeight.Value, (LevelTypes)(-1), val2, val3); harmony.PatchAll(); Logger.LogInfo((object)("Checking Soft Dependencies:\nCoroner Is Loaded? " + IsCoronerLoaded + "\nEmployeeClasses Is Loaded? " + IsEmployeeClassesLoaded + "\nEnhancedMonsters Is Loaded? " + IsEnhancedMonstersLoaded)); if (IsCoronerLoaded) { CoronerClass.Init(); } defaultAudioClipFilePaths = GetDefaultAudioClipFilePaths(); BoundConfig.InitializeAudioClipConfigs(defaultAudioClipFilePaths); if (BoundConfig.enableScrap.Value) { int value = BoundConfig.scrapWeight.Value; Item val4 = ModAssets.LoadAsset<Item>("Assets/ModAssets/LethalGargoyle/Scrap/GargoyleScrap.asset"); if ((Object)(object)val4 == (Object)null || (Object)(object)val4.spawnPrefab == (Object)null) { Logger.LogError((object)"Failed to load the Gargoyle Statue scrap from the asset bundle. Scrap registration skipped; the rest of the mod is unaffected."); } else { GargoyleStatue gargoyleStatue = val4.spawnPrefab.AddComponent<GargoyleStatue>(); ((GrabbableObject)gargoyleStatue).grabbable = true; ((GrabbableObject)gargoyleStatue).grabbableToEnemies = true; ((GrabbableObject)gargoyleStatue).itemProperties = val4; Utilities.FixMixerGroups(val4.spawnPrefab); NetworkPrefabs.RegisterNetworkPrefab(val4.spawnPrefab); Items.RegisterScrap(val4, value, (LevelTypes)(-1)); Logger.LogInfo((object)"Gargoyle Statue scrap is registered."); } } stepSound = ModAssets.LoadAsset<AudioClip>("Assets/ModAssets/LethalGargoyle/Audio/sfx_Step.ogg"); if ((Object)(object)stepSound == (Object)null) { Logger.LogError((object)"Failed to load sfx_Step.ogg from the asset bundle - the gargoyle will walk silently."); } LogSessionBanner(); Logger.LogInfo((object)"Plugin DropDaDeuce.LethalGargoyles is loaded!"); } private void LogSessionBanner() { Logger.LogInfo((object)("=== LethalGargoyles 0.8.0 | bundle '" + (((Object)(object)ModAssets != (Object)null) ? "loaded" : "MISSING") + "' | Unity " + Application.unityVersion + " ===")); Logger.LogInfo((object)$"Soft deps: Coroner={IsCoronerLoaded} EmployeeClasses={IsEmployeeClassesLoaded} EnhancedMonsters={IsEnhancedMonstersLoaded}"); Logger.LogInfo((object)$"Config: sightRange={Math.Sqrt(120.0):0.00}m aggroRange={BoundConfig.aggroRange.Value} awareDist={BoundConfig.awareDist.Value} push={BoundConfig.enablePush.Value} scrap={BoundConfig.enableScrap.Value}"); Logger.LogInfo((object)("Custom voice lines: " + (CustomAudioFolderPath ?? "(unavailable)"))); Logger.LogInfo((object)$"Diagnostics: enabled={BoundConfig.diagEnabled.Value} default={BoundConfig.diagDefaultLevel.Value} repeatLimit={BoundConfig.diagRepeatLimit.Value}"); } private void OnEnable() { if (IsEnhancedMonstersLoaded) { EnhancedMonstersCompatibilityLayer.RegisterCustomMonsterEnemyData(); Logger.LogInfo((object)"Gargoyle has been registered with EnhancedMonsters."); } } private static void InitializeNetworkBehaviours() { Type[] array; try { array = Assembly.GetExecutingAssembly().GetTypes(); } catch (ReflectionTypeLoadException ex) { Logger.LogError((object)("Could not enumerate assembly types for netcode initialization: " + ex.Message + ". Continuing with whatever loaded.")); array = Array.FindAll(ex.Types, (Type t) => t != null); } Type[] array2 = array; foreach (Type type in array2) { if (type.FullName == "LethalGargoyles.src.SoftDepends.CoronerClass" && !Instance.IsCoronerLoaded) { continue; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); MethodInfo[] array3 = methods; foreach (MethodInfo methodInfo in array3) { object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false); if (customAttributes.Length != 0) { try { methodInfo.Invoke(null, null); } catch (Exception ex2) { Logger.LogError((object)("Netcode initializer " + type.FullName + "." + methodInfo.Name + " threw: " + (ex2.InnerException?.Message ?? ex2.Message) + ". RPCs on that type will not work.")); } } } } } 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 const int MaxClipBytes = 512000; 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[] audioData; if (PluginConfig.AudioClipEnableConfig.TryGetValue(clipName, out ConfigEntry<bool> value2)) { if (!value2.Value) { continue; } audioData = AudioFileToByteArray(item); byte[] array = audioData; if (array != null && array.Length > 512000) { Plugin.Logger.LogError((object)$"Skipping Clip({clipName}): {audioData?.Length} bytes exceeds the {512000} byte (500KB) limit. Remaining clips in '{category}' will still be sent."); } else if (audioData != null) { await WaitForClientReady(clientId); if (!clientReady.ContainsKey(clientId)) { break; } await SendAudioClipToClient(clientId, audioData, clipName, category); clientReady[clientId] = false; SetClientReadyClientRpc(isReady: false, clientId); Plugin.Logger.LogInfo((object)$"Sent Clip({clipName}) to ClientID({clientId})"); } continue; } audioData = AudioFileToByteArray(item); byte[] array2 = audioData; if (array2 != null && array2.Length > 512000) { Plugin.Logger.LogError((object)$"Skipping Clip({clipName}): {audioData?.Length} bytes exceeds the {512000} byte (500KB) limit. Remaining clips in '{category}' will still be sent."); } else if (audioData != null) { await WaitForClientReady(clientId); if (!clientReady.ContainsKey(clientId)) { break; } await SendAudioClipToClient(clientId, audioData, 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 unsafe 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*)(&writer))/*cast due to .constrained prefix*/).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 { int channels = val.Channels; int num = (int)val.TotalSamples; float[] array = new float[num * channels]; AudioClip val2 = AudioClip.Create(clipName, num, channels, val.SampleRate, false); int num2 = val.ReadSamples(array, 0, array.Length); if (num2 < array.Length) { LGLog.Warn(LogCat.Audio, $"Clip '{clipName}' decoded short: got {num2} of {array.Length} samples ({channels}ch). It will play truncated."); } val2.SetData(array, 0); List<AudioClip> clipListByCategory = GetClipListByCategory(category); clipListByCategory.Add(val2); LGLog.Info(LogCat.Audio, $"Clip loaded: {((Object)val2).name} ({channels}ch, {val.SampleRate}Hz, {val2.length:0.00}s)"); } 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); try { FileInfo fileInfo = new FileInfo(item); if (fileInfo.Exists && fileInfo.Length > 512000) { LGLog.Warn(LogCat.Audio, $"Skipping '{fileNameWithoutExtension}' locally too: {fileInfo.Length} bytes exceeds the {512000} byte limit, so no client can receive it. Re-export it smaller."); continue; } } catch (Exception ex) { LGLog.Warn(LogCat.Audio, "Could not stat '" + item + "': " + ex.GetType().Name + ". Loading anyway."); } 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 = new Dictionary<string, List<string>>(); foreach (KeyValuePair<string, List<string>> defaultAudioClipFilePath in defaultAudioClipFilePaths) { AudioClipFilePaths[defaultAudioClipFilePath.Key] = new List<string>(defaultAudioClipFilePath.Value); } 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", "Custom Voice Lines"); FileInfo[] array2 = mP3Files2; foreach (FileInfo fileInfo2 in array2) { string fileNameWithoutExtension3 = Path.GetFileNameWithoutExtension(fileInfo2.FullName); bool flag2 = false; for (int l = 0; l < value.Count; l++) { string fileNameWithoutExtension4 = Path.GetFileNameWithoutExtension(value[l]); if (fileNameWithoutExtension3 == fileNameWithoutExtension4) { value[l] = fileInfo2.FullName; flag2 = true; break; } } if (!flag2) { value.Add(fileInfo2.FullName); } } } if (!Plugin.Instance.IsEmployeeClassesLoaded || !(key == "Class")) { continue; } FileInfo[] mP3Files3 = GetMP3Files("EmployeeClass", "Voice Lines"); FileInfo[] mP3Files4 = GetMP3Files("EmployeeClass", "Custom Voice Lines"); FileInfo[] array3 = mP3Files3; foreach (FileInfo fileInfo3 in array3) { value.Add(fileInfo3.FullName); } FileInfo[] array4 = mP3Files4; foreach (FileInfo fileInfo4 in array4) { string fileNameWithoutExtension5 = Path.GetFileNameWithoutExtension(fileInfo4.FullName); bool flag3 = false; for (int num = 0; num < value.Count; num++) { string fileNameWithoutExtension6 = Path.GetFileNameWithoutExtension(value[num]); if (fileNameWithoutExtension5 == fileNameWithoutExtension6) { value[num] = fileInfo4.FullName; flag3 = true; break; } } if (!flag3) { value.Add(fileInfo4.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": text = Path.Combine("Taunt - General"); break; case "Aggro": text = Path.Combine("Taunt - Aggro"); break; case "Enemy": text = Path.Combine("Taunt - Enemy"); break; case "PlayerDeath": text = Path.Combine("Taunt - Player Death"); break; case "GargoyleDeath": text = Path.Combine("Taunt - Gargoyle Death"); break; case "PriorDeath": text = Path.Combine("Taunt - Prior Death"); break; case "Coroner": text = Path.Combine("Taunt - Prior Death", "Coroner"); break; case "Class": case "EmployeeClass": text = Path.Combine("Taunt - EmployeeClass"); break; case "Activity": text = Path.Combine("Taunt - Activity"); break; case "Attack": text = Path.Combine("Combat Dialog", "Attack"); break; case "Hit": text = Path.Combine("Combat Dialog", "Hit"); break; case "SteamIDs": text = Path.Combine("Taunt - SteamIDs"); break; default: text = null; break; } string text2 = text; if (text2 == null) { LGLog.Warn(LogCat.Audio, "Unknown voice line category '" + type + "' requested - no clips will load for it. This is a code bug, not a user error."); return Array.Empty<FileInfo>(); } string text3 = Path.Combine(path, text2); try { DirectoryInfo directoryInfo = new DirectoryInfo(text3); if (!directoryInfo.Exists) { LGLog.Debug(LogCat.Audio, "Voice line folder missing, skipping: " + text3); return Array.Empty<FileInfo>(); } return directoryInfo.GetFiles("*.ogg"); } catch (Exception ex) { LGLog.Warn(LogCat.Audio, "Could not read voice line folder '" + text3 + "': " + ex.GetType().Name + ": " + ex.Message + ". That category will be empty rather than silencing the whole mod."); 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) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)base.__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(2917554051u, val2, (RpcDelivery)0); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref isReady, default(ForPrimitives)); BytePacker.WriteValueBitPacked(val, clientId); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 2917554051u, val2, (RpcDelivery)0); } if ((int)base.__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { base.__rpc_exec_stage = (__RpcExecStage)0; if (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) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)base.__rpc_exec_stage != 1 && (networkManager.IsClient || networkManager.IsHost)) { ServerRpcParams val2 = default(ServerRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendServerRpc(1384767487u, val2, (RpcDelivery)0); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref isReady, default(ForPrimitives)); BytePacker.WriteValueBitPacked(val, clientId); ((NetworkBehaviour)this).__endSendServerRpc(ref val, 1384767487u, val2, (RpcDelivery)0); } if ((int)base.__rpc_exec_stage == 1 && (networkManager.IsServer || networkManager.IsHost)) { base.__rpc_exec_stage = (__RpcExecStage)0; if (clientReady.ContainsKey(clientId)) { clientReady[clientId] = isReady; } } } protected override void __initializeVariables() { ((NetworkBehaviour)this).__initializeVariables(); } protected override void __initializeRpcs() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown ((NetworkBehaviour)this).__registerRpc(2917554051u, new RpcReceiveHandler(__rpc_handler_2917554051), "SetClientReadyClientRpc"); ((NetworkBehaviour)this).__registerRpc(1384767487u, new RpcReceiveHandler(__rpc_handler_1384767487), "SetClientReadyServerRpc"); ((NetworkBehaviour)this).__initializeRpcs(); } 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)1; ((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"; } } public enum LGLevel { Off, Error, Warn, Info, Debug, Trace } [Flags] public enum LogCat { None = 0, Lifecycle = 1, StateMachine = 2, Targeting = 4, Movement = 8, Perception = 0x10, Combat = 0x20, Taunt = 0x40, Audio = 0x80, Netcode = 0x100, Config = 0x200, Scrap = 0x400 } internal static class LGLog { private static readonly Dictionary<LogCat, LGLevel> Levels = new Dictionary<LogCat, LGLevel>(); private static LGLevel _defaultLevel = LGLevel.Warn; private static bool _enabled = true; private static bool _includeContext = true; private static int _repeatLimit = 20; private static readonly Dictionary<string, int> RepeatCounts = new Dictionary<string, int>(); private static readonly HashSet<string> FiredOnce = new HashSet<string>(); internal static void Configure(bool enabled, LGLevel defaultLevel, Dictionary<LogCat, LGLevel> perCategory, bool includeContext, int repeatLimit) { _enabled = enabled; _defaultLevel = defaultLevel; _includeContext = includeContext; _repeatLimit = repeatLimit; Levels.Clear(); foreach (KeyValuePair<LogCat, LGLevel> item in perCategory) { Levels[item.Key] = item.Value; } } internal static bool On(LogCat cat, LGLevel level) { if (!_enabled) { return false; } LGLevel value; LGLevel lGLevel = (Levels.TryGetValue(cat, out value) ? value : _defaultLevel); if (_defaultLevel > lGLevel) { lGLevel = _defaultLevel; } if (lGLevel != LGLevel.Off) { return level <= lGLevel; } return false; } internal static void Error(LogCat cat, string msg) { Write(cat, LGLevel.Error, msg); } internal static void Warn(LogCat cat, string msg) { Write(cat, LGLevel.Warn, msg); } internal static void Info(LogCat cat, string msg) { Write(cat, LGLevel.Info, msg); } internal static void Debug(LogCat cat, string msg) { Write(cat, LGLevel.Debug, msg); } internal static void Trace(LogCat cat, string msg) { Write(cat, LGLevel.Trace, msg); } internal static void Once(LogCat cat, LGLevel level, string key, string msg) { if (On(cat, level) && FiredOnce.Add(key)) { Write(cat, level, msg); } } internal static void Invariant(bool condition, LogCat cat, string key, string msg) { if (!condition && _enabled && FiredOnce.Add("INV:" + key)) { Emit(LGLevel.Error, Prefix(cat) + " INVARIANT VIOLATED: " + msg); } } private static void Write(LogCat cat, LGLevel level, string msg) { if (!On(cat, level)) { return; } if (_repeatLimit > 0) { string key = cat.ToString() + "|" + level.ToString() + "|" + msg; RepeatCounts.TryGetValue(key, out var value); if (value > _repeatLimit) { return; } RepeatCounts[key] = value + 1; if (value == _repeatLimit) { Emit(level, $"{Prefix(cat)} {msg} (repeated {_repeatLimit}x - suppressing further occurrences)"); return; } } Emit(level, Prefix(cat) + " " + msg); } private static string Prefix(LogCat cat) { if (!_includeContext) { return $"[{cat}]"; } char c = 'C'; NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { c = '?'; } else if (singleton.IsHost || singleton.IsServer) { c = 'H'; } return $"[{c}][{cat}]"; } private static void Emit(LGLevel level, string line) { switch (level) { case LGLevel.Error: Plugin.Logger.LogError((object)line); break; case LGLevel.Warn: Plugin.Logger.LogWarning((object)line); break; default: Plugin.Logger.LogInfo((object)line); break; } } internal static void ResetRound() { RepeatCounts.Clear(); FiredOnce.Clear(); } } } 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; } } public static class EnhancedMonstersCompatibilityLayer { [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static void RegisterCustomMonsterEnemyData() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) EnemyMetadata val = default(EnemyMetadata); ((EnemyMetadata)(ref val))..ctor(); val.MeshOffset = Vec3.op_Implicit(new Vector3(0f, 0f, 0f)); val.FloorRotation = Vec3.op_Implicit(new Vector3(0f, 90f, 0f)); val.HandRotation = Vec3.op_Implicit(new Vector3(0f, 90f, 0f)); val.AnimateOnDeath = false; EnemyMetadata val2 = val; EnemiesDataManager.RegisterEnemy("LethalGargoyle", true, 70, 90, 30f, "B", val2, false); } } } namespace LethalGargoyles.src.Scrap { internal class GargoyleStatue : GrabbableObject { private AudioSource? scrapAudio; private int lastTaunt; private float dogHearDistSqr; private float dogCooldown; private bool dogHear; private float lastDogCheck; private float lastDogTaunt; private static readonly Dictionary<ulong, float> s_playerTalkingUntil = new Dictionary<ulong, float>(); private float _nextTalkTime; [Conditional("DEBUG")] private void LogIfDebugBuild(string text) { Plugin.Logger.LogInfo((object)text); } public override void Start() { ((GrabbableObject)this).Start(); scrapAudio = ((Component)this).GetComponentInParent<AudioSource>(); dogHearDistSqr = Plugin.BoundConfig.dogHearDist.Value; dogCooldown = Plugin.BoundConfig.dogCooldown.Value; dogHear = Plugin.BoundConfig.dogHear.Value; dogHearDistSqr *= dogHearDistSqr; lastDogTaunt = Time.time - dogCooldown; } public override void Update() { ((GrabbableObject)this).Update(); if ((Object)(object)scrapAudio != (Object)null && ((NetworkBehaviour)this).IsServer && !base.isPocketed && dogHear && Time.time - lastDogTaunt > dogCooldown && Time.time - lastDogCheck > 1f && !scrapAudio.isPlaying) { lastDogCheck = Time.time; if (DogNearStatue() && LethalGargoylesAI.ChooseRandomClip("taunt_enemy_Mouthdog", "Enemy", out string audioClip) && audioClip != null && (Object)(object)((NetworkBehaviour)this).NetworkObject != (Object)null && ((NetworkBehaviour)this).NetworkObject.IsSpawned) { lastDogTaunt = Time.time; TauntClientRpc(audioClip, "enemy"); } } } private bool DogNearStatue() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_0035: Unknown result type (might be due to invalid IL or missing references) 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 > dogHearDistSqr) && string.Equals(spawnedEnemy.enemyType.enemyName, "MouthDog", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public override void ItemActivate(bool used, bool buttonDown = true) { ((GrabbableObject)this).ItemActivate(used, buttonDown); if (LGLog.On(LogCat.Scrap, LGLevel.Debug)) { LGLog.Debug(LogCat.Scrap, $"Statue#{((NetworkBehaviour)this).NetworkObjectId} ItemActivate(used={used}, down={buttonDown}) " + $"pocketed={base.isPocketed} held={base.isHeld} " + "heldBy=" + (((Object)(object)base.playerHeldBy != (Object)null) ? base.playerHeldBy.playerUsername : "null") + " " + $"server={((NetworkBehaviour)this).IsServer} owner={((NetworkBehaviour)this).IsOwner}"); } if (((NetworkBehaviour)this).IsServer && !base.isPocketed && (Object)(object)scrapAudio != (Object)null) { ItemActivateServerRpc(used, buttonDown); } } [ServerRpc(RequireOwnership = false)] private void ItemActivateServerRpc(bool used, bool buttonDown) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: 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) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsClient || networkManager.IsHost)) { ServerRpcParams val2 = default(ServerRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendServerRpc(3724832850u, val2, (RpcDelivery)0); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref used, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref buttonDown, default(ForPrimitives)); ((NetworkBehaviour)this).__endSendServerRpc(ref val, 3724832850u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsServer || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; LGLog.Debug(LogCat.Scrap, $"Statue#{((NetworkBehaviour)this).NetworkObjectId} ItemActivateServerRpc RECEIVED pocketed={base.isPocketed}"); if (!base.isPocketed) { GeneralTaunt(); } } } private bool TryReserveTalkSlot(float clipLength) { float time = Time.time; if (time < _nextTalkTime) { return false; } if ((Object)(object)base.playerHeldBy != (Object)null) { ulong actualClientId = base.playerHeldBy.actualClientId; if (s_playerTalkingUntil.TryGetValue(actualClientId, out var value) && time < value) { LGLog.Debug(LogCat.Scrap, $"Statue#{((NetworkBehaviour)this).NetworkObjectId} suppressed - {base.playerHeldBy.playerUsername} already has a statue talking for another {value - time:0.0}s"); return false; } s_playerTalkingUntil[actualClientId] = time + clipLength; } _nextTalkTime = time + clipLength; return true; } public void GeneralTaunt() { List<AudioClip> list = new List<AudioClip>(AudioManager.tauntClips); AudioClip val = null; if ((Object)(object)base.playerHeldBy != (Object)null) { val = LethalGargoylesAI.FindClip($"{base.playerHeldBy.playerSteamId}", AudioManager.playerClips); if ((Object)(object)val != (Object)null) { list.Add(val); } } if (list.Count > 0) { int num = Random.Range(0, list.Count); if (num == lastTaunt) { num = (num + 1) % list.Count; } AudioClip val2 = list[num]; if (TryReserveTalkSlot(val2.length)) { lastTaunt = num; string clipType = (((Object)(object)val != (Object)null && (Object)(object)val2 == (Object)(object)val) ? "steamids" : "general"); TauntClientRpc(((Object)val2).name, clipType); } } else { LGLog.Warn(LogCat.Scrap, "Gargoyle Statue was activated but the general taunt list is empty - nothing to play."); } } [ClientRpc] private void TauntClientRpc(string clipName, string clipType) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(3274531793u, val2, (RpcDelivery)0); bool flag = clipName != null; ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref flag, default(ForPrimitives)); if (flag) { ((FastBufferWriter)(ref val)).WriteValueSafe(clipName, false); } bool flag2 = clipType != null; ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref flag2, default(ForPrimitives)); if (flag2) { ((FastBufferWriter)(ref val)).WriteValueSafe(clipType, false); } ((NetworkBehaviour)this).__endSendClientRpc(ref val, 3274531793u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 || (!networkManager.IsClient && !networkManager.IsHost)) { return; } ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; List<AudioClip> list = new List<AudioClip>(); switch (clipType) { case "general": list = AudioManager.tauntClips; break; case "enemy": list = AudioManager.enemyClips; break; case "aggro": list = AudioManager.aggroClips; break; case "death": list = AudioManager.deathClips; break; case "attack": list = AudioManager.attackClips; break; case "hit": list = AudioManager.hitClips; break; case "priordeath": list = AudioManager.priorDeathClips; break; case "playerdeath": list = AudioManager.playerDeathClips; break; case "class": list = AudioManager.classClips; break; case "activity": list = AudioManager.activityClips; break; case "steamids": list = AudioManager.playerClips; break; } AudioClip val3 = LethalGargoylesAI.FindClip(clipName, list); if (list.Count > 0 && (Object)(object)val3 != (Object)null && (Object)(object)scrapAudio != (Object)null) { LGLog.Debug(LogCat.Scrap, $"Statue#{((NetworkBehaviour)this).NetworkObjectId} PLAYING {clipType}: {((Object)val3).name} (audio={((Object)scrapAudio).GetInstanceID()})"); scrapAudio.PlayOneShot(val3); if (dogHear) { ((MonoBehaviour)this).StartCoroutine(PlayNoiseWhileTalking()); } } else if ((Object)(object)scrapAudio == (Object)null) { LGLog.Warn(LogCat.Scrap, "Gargoyle Statue has no AudioSource - it cannot play anything. The prefab is likely missing its AudioSource component."); } else { LGLog.Warn(LogCat.Audio, $"Statue taunt '{clipName}' (type '{clipType}') did not resolve locally - this machine has {list.Count} clip(s) for that type. " + "The audio transfer for that category was incomplete."); } } private IEnumerator PlayNoiseWhileTalking() { while ((Object)(object)scrapAudio != (Object)null && scrapAudio.isPlaying) { RoundManager.Instance.PlayAudibleNoise(((Component)this).transform.position, scrapAudio.maxDistance / 1.2f, scrapAudio.volume, 0, false, 0); yield return (object)new WaitForSeconds(3f); } } protected override void __initializeVariables() { ((GrabbableObject)this).__initializeVariables(); } protected override void __initializeRpcs() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown ((NetworkBehaviour)this).__registerRpc(3724832850u, new RpcReceiveHandler(__rpc_handler_3724832850), "ItemActivateServerRpc"); ((NetworkBehaviour)this).__registerRpc(3274531793u, new RpcReceiveHandler(__rpc_handler_3274531793), "TauntClientRpc"); ((GrabbableObject)this).__initializeRpcs(); } private static void __rpc_handler_3724832850(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_004a: 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_005f: 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) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { bool used = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref used, default(ForPrimitives)); bool buttonDown = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref buttonDown, default(ForPrimitives)); target.__rpc_exec_stage = (__RpcExecStage)1; ((GargoyleStatue)(object)target).ItemActivateServerRpc(used, buttonDown); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_3274531793(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_0067: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref flag, default(ForPrimitives)); string clipName = null; if (flag) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref clipName, false); } bool flag2 = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref flag2, default(ForPrimitives)); string clipType = null; if (flag2) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref clipType, false); } target.__rpc_exec_stage = (__RpcExecStage)1; ((GargoyleStatue)(object)target).TauntClientRpc(clipName, clipType); target.__rpc_exec_stage = (__RpcExecStage)0; } } protected internal override string __getTypeName() { return "GargoyleStatue"; } } } 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)Unsafe.As<CauseOfDeath, CauseOfDeath>(ref val.causeOfDeath)/*cast due to .constrained prefix*/).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)Unsafe.As<CauseOfDeath, CauseOfDeath>(ref val.causeOfDeath)/*cast due to .constrained prefix*/).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 LethalGargoylesSFX : MonoBehaviour { public AudioSource? audioSource; public AudioClip? audioClip; public void PlayStep() { if (!((Object)(object)audioSource == (Object)null) && !((Object)(object)audioClip == (Object)null)) { audioSource.PlayOneShot(audioClip); } } } public class LethalGargoylesAI : EnemyAI, ISmartAI { private enum State { SearchingForPlayer, StealthyPursuit, GetOutOfSight, AggressivePursuit, Idle, PushTarget } public enum AnimState : byte { Idle, Walk, Chase, SwingAttack } public enum RelativeZone { Front, FrontRight, Right, BackRight, Back, BackLeft, Left, FrontLeft } 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 void ClearAllPlayerData() { playerActivities.Clear(); playerTauntTimers.Clear(); } 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 struct KillTriggerInfo { public Transform T; public BoxCollider C; } private static readonly int TrigIdle = Animator.StringToHash("startIdle"); private static readonly int TrigWalk = Animator.StringToHash("startWalk"); private static readonly int TrigChase = Animator.StringToHash("startChase"); private static readonly int TrigSwingAttack = Animator.StringToHash("swingAttack"); private const float DEST_EPSILON_SQR = 0.01f; private const float REPATH_INTERVAL = 0.75f; private const float DEST_CHANGE_SQR = 1f; private const float TELEPORT_COOLDOWN = 10f; private const float TELEPORT_MIN_DIST_SQR = 2025f; private const float TELEPORT_RANGE_MIN = 10f; private const float TELEPORT_RANGE_MAX = 18f; private const int TELEPORT_ATTEMPTS = 10; private const int PUSH_DAMAGE = 2; private const float SlowMs = 2f; private const float AGGRO_EVAL_INTERVAL = 0.2f; private const float HIDE_EVAL_INTERVAL = 0.35f; public const int DEFAULT_SIGHT_RANGE_SQR = 120; public static readonly HashSet<string> trackedItems = new HashSet<string> { "Key", "Apparatus", "Comedy", "Tragedy", "Maneater" }; 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<LethalGargoylesAI> activeGargoyles = new List<LethalGargoylesAI>(); private static readonly List<Transform> cachedRailings = new List<Transform>(); private static int s_cachedRailingsSceneHandle = -1; private static readonly List<KillTriggerInfo> cachedKillTriggerInfos = new List<KillTriggerInfo>(); private static readonly int RailingMask = 1 << LayerMask.NameToLayer("Railing"); private static readonly Collider[] _tmpRailingColliders = (Collider[])(object)new Collider[16]; 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 int s_nextGargoyleSerial; private static readonly object PlayerPushStatesLock = new object(); private static int lastGenTaunt = -1; private static int lastAgrTaunt = -1; private static int lastGargoyleToSwitch = 0; private static float lastNodeCheckTime = 0f; public Transform turnCompass; public Transform attackArea; private int _gargoyleSerial; public int myID; private bool _smartRegistered; private SmartPathTask? pathingTask; private SmartPathDestination? activeDestination; private Vector3 _lastActiveDestination = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); private Vector3 _lastRequestedDest; private float _nextPathRequestTime; private float pathDelayTimer; private SmartPathfindingLinkFlags _allowedLinks = (SmartPathfindingLinkFlags)1; private NavMeshPath? _zoneScratchPath; private float _corneredSince; private Vector3 _corneredAnchorPos; private float _corneredAggroUntil; private float corneredAggroDelay; private const float CORNERED_AGGRO_COMMIT = 8f; private const float CORNERED_PROGRESS_DIST = 8f; private bool _hideFoundCover = true; private bool _nodeChoiceFellBack; private float _nextHideTraceTime; private AnimatedObjectTrigger? currentDoorTrigger; private PlayerControllerB closestPlayer; private PlayerControllerB aggroPlayer; private PlayerControllerB? _lastTarget; private Vector3 cachedTargetPosition; private float distanceToPlayerSqr; private float distanceToClosestPlayerSqr; private bool isSeen; private bool canSeePlayer; private bool targetSeesGargoyle; private Transform? killTrigger; private float distToKillTriggerSqr; private float pushTimer; private int pushStage; private float targetTimer; private float _nextAggroEvalTime; private float playerCheckTimer; private int previousStateIndex; private float lastSeenCheckTime; private readonly Dictionary<RelativeZone, Vector3> RelativeZones = new Dictionary<RelativeZone, Vector3>(); private RelativeZone currentZone; private RelativeZone nextZoneRight; private RelativeZone nextZoneLeft; private float leftPathDist; private float rightPathDist; private float _nextZoneFailLogTime; private Vector3 lastCoverSearchPosition; private float coverSearchCooldown = 3f; private float lastCoverSearchTime = -10f; private List<Vector3> cachedCoverPoints = new List<Vector3>(); private float _nextHideEvalTime; public DoorLock? currentDoor; public float lastDoorCloseTime; private float baseSpeed; private float attackRangeSqr; private int attackDamage; private float aggroRangeSqr; private int minTaunt; private int maxTaunt; private float steamIDTauntCooldown = 90f; private float distWarnSqr; private float bufferDistSqr; private float awareDistSqr; private float idleDistanceSqr; private bool enablePush; private const float IDLE_HYSTERESIS = 1.2f; private const float AWARE_HYSTERESIS = 1.15f; private float idleKeepDistSqr; private float awareKeepDistSqr; private float randGenTauntTime; private float randAgrTauntTime; private float randEnemyTauntTime; private float lastGenTauntTime; private float lastAgrTauntTime; private float lastEnemyTauntTime; private float lastSteamIDTauntTime; private int genTauntCount; private string? lastEnemy; private string? _lastEnemyLogName; private float _nextEnemyLogTime; private float _nextActivityLogTime; private float _nextDoAiLogTime; private readonly float nodeCheckInterval = 5f; private float lastAttackTime; private readonly List<PlayerControllerB> validPlayers = new List<PlayerControllerB>(); private readonly List<LethalGargoylesAI> gargoyles = new List<LethalGargoylesAI>(); private readonly Dictionary<PlayerControllerB, string> playerClasses = new Dictionary<PlayerControllerB, string>(); private AnimState _lastAnim; private float _lastSfxVolume = -1f; private float _nextTeleportTime; private readonly List<GameObject> _nodeScratch = new List<GameObject>(); private readonly List<(float distSqr, Transform t)> _candidateScratch = new List<(float, Transform)>(); private readonly List<(float distSqr, Transform t)> _bestScratch = new List<(float, Transform)>(); private NavMeshPath? _scratchPath; public static LethalGargoylesAI? LGInstance { get; private set; } private string GargoyleTag => $"LG#{_gargoyleSerial}(agentId={myID}, netId={(((Object)(object)((NetworkBehaviour)this).NetworkObject != (Object)null) ? ((NetworkBehaviour)this).NetworkObject.NetworkObjectId : 0)})"; private static bool IsInvalidPos(Vector3 p) { return ((Vector3)(ref p)).sqrMagnitude < 0.0001f; } public override void Start() { //IL_0260: 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_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0298: 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_029f: Unknown result type (might be due to invalid IL or missing references) ((EnemyAI)this).Start(); _gargoyleSerial = Interlocked.Increment(ref s_nextGargoyleSerial); myID = ((Object)base.agent).GetInstanceID(); SmartPathfinding.RegisterSmartAgent(base.agent); _smartRegistered = true; LGInstance = this; SetAnim(AnimState.Walk); SwitchState(State.SearchingForPlayer); baseSpeed = Plugin.BoundConfig.baseSpeed.Value; attackDamage = Plugin.BoundConfig.attackDamage.Value; minTaunt = Plugin.BoundConfig.minTaunt.Value; maxTaunt = Plugin.BoundConfig.maxTaunt.Value; steamIDTauntCooldown = Plugin.BoundConfig.steamIDTauntCooldown.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; awareKeepDistSqr = awareDistSqr * 1.3225f; idleKeepDistSqr = idleDistanceSqr * 1.44f; enablePush = Plugin.BoundConfig.enablePush.Value; corneredAggroDelay = Plugin.BoundConfig.corneredAggroDelay.Value; float value = Plugin.BoundConfig.agentRadius.Value; if (value > 0f && !Mathf.Approximately(base.agent.radius, value)) { LGLog.Info(LogCat.Movement, $"{GargoyleTag} agent radius {base.agent.radius:0.00} -> {value:0.00} (config)"); base.agent.radius = value; } _allowedLinks = (SmartPathfindingLinkFlags)1; if (Plugin.BoundConfig.allowExitFacility.Value) { _allowedLinks = (SmartPathfindingLinkFlags)(_allowedLinks | 0xC); } if (Plugin.BoundConfig.allowElevators.Value) { _allowedLinks = (SmartPathfindingLinkFlags)(_allowedLinks | 2); } lastAttackTime = Time.time; pushTimer = Time.time; gargoyleTargets[myID] = base.targetPlayer; _lastTarget = base.targetPlayer; _nextAggroEvalTime = Time.time; AudioSource creatureVoice = base.creatureVoice; creatureVoice.maxDistance *= 3f; pathDelayTimer = Time.time; lastSteamIDTauntTime = Time.time - (steamIDTauntCooldown + 1f); cachedOutsideAINodes.Clear(); GameObject[] outsideAINodes = RoundManager.Instance.outsideAINodes; foreach (GameObject val in outsideAINodes) { if ((Object)(object)val != (Object)null) { cachedOutsideAINodes.Add(val); } } cachedInsideAINodes.Clear(); GameObject[] insideAINodes = RoundManager.Instance.insideAINodes; foreach (GameObject val2 in insideAINodes) { if ((Object)(object)val2 != (Object)null) { cachedInsideAINodes.Add(val2); } } 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) ((EnemyAI)this).Update(); cachedTargetPosition = (((Object)(object)base.targetPlayer != (Object)null) ? ((Component)base.targetPlayer).transform.position : ((Component)this).transform.position); if (base.isEnemyDead || StartOfRound.Instance.allPlayersDead) { return; } ApplyStateAudio(); if (!((Behaviour)base.agent).enabled || !base.agent.isOnNavMesh || !((NetworkBehaviour)this).IsOwner) { return; } if ((Object)(object)_lastTarget != (Object)(object)base.targetPlayer) { gargoyleTargets[myID] = base.targetPlayer; _lastTarget = base.targetPlayer; } if (Time.time - lastNodeCheckTime > nodeCheckInterval) { CheckAndRefreshAINodes(); lastNodeCheckTime = Time.time; } HandleTargetPlayer(); if (Time.time - lastSeenCheckTime > 0.33f) { closestPlayer = ((EnemyAI)this).GetClosestPlayer(false, false, false); float num; if (!((Object)(object)closestPlayer != (Object)null)) { num = float.MaxValue; } else { Vector3 val = ((Component)this).transform.position - ((Component)closestPlayer).transform.position; num = ((Vector3)(ref val)).sqrMagnitude; } distanceToClosestPlayerSqr = num; bool flag = GargoyleIsSeen(((Component)this).transform); if (flag && !isSeen) { pathDelayTimer = Time.time - 2f; _nextHideEvalTime = 0f; _nextPathRequestTime = 0f; LGLog.Debug(LogCat.Movement, GargoyleTag + " spotted - clearing hide commitment for an immediate re-evaluation"); _corneredSince = Time.time; _corneredAnchorPos = ((Component)this).transform.position; } else if (!flag && isSeen) { _corneredSince = 0f; _corneredAggroUntil = 0f; } isSeen = flag; UpdateCorneredTimer(); lastSeenCheckTime = Time.time; } HandlePushStage(); HandleBehaviorState(); if (base.currentBehaviourStateIndex == 0) { TryActivateSmartLink(); } else if (base.currentBehaviourStateIndex != 4) { FollowSmartPath(); } } [Conditional("DEBUG")] private void LogAiIntervalState() { //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_048c: Unknown result type (might be due to invalid IL or missing references) //IL_049b: Unknown result type (might be due to invalid IL or missing references) //IL_04aa: Unknown result type (might be due to invalid IL or missing references) //IL_04b9: Unknown result type (might be due to invalid IL or missing references) //IL_04c8: Unknown result type (might be due to invalid IL or missing references) //IL_04d7: Unknown result type (might be due to invalid IL or missing references) //IL_04e6: Unknown result type (might be due to invalid IL or missing references) //IL_04f5: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Unknown result type (might be due to invalid IL or missing references) //IL_05fe: Unknown result type (might be due to invalid IL or missing references) //IL_0603: Unknown result type (might be due to invalid IL or missing references) //IL_0607: Unknown result type (might be due to invalid IL or missing references) //IL_060c: Unknown result type (might be due to invalid IL or missing references) //IL_061d: Unknown result type (might be due to invalid IL or missing references) //IL_062a: Unknown result type (might be due to invalid IL or missing references) //IL_0639: Unknown result type (might be due to invalid IL or missing references) //IL_0648: Unknown result type (might be due to invalid IL or missing references) if (Time.time < _nextDoAiLogTime) { return; } _nextDoAiLogTime = Time.time + 3f; string text = StateToString(base.currentBehaviourStateIndex); string text2 = $"Owner={((NetworkBehaviour)this).IsOwner} Server={((NetworkBehaviour)this).IsServer} Spawned={(Object)(object)((NetworkBehaviour)this).NetworkObject != (Object)null && ((NetworkBehaviour)this).NetworkObject.IsSpawned}"; string text3 = (((Object)(object)base.targetPlayer == (Object)null) ? "Target=null" : $"Target={base.targetPlayer.playerUsername} id={base.targetPlayer.playerClientId} dead={base.targetPlayer.isPlayerDead} controlled={base.targetPlayer.isPlayerControlled} insideFactory={base.targetPlayer.isInsideFactory}"); string text4 = (((Object)(object)closestPlayer == (Object)null) ? "Closest=null" : $"Closest={closestPlayer.playerUsername} id={closestPlayer.playerClientId} dead={closestPlayer.isPlayerDead} insideFactory={closestPlayer.isInsideFactory}"); float num = (((Object)(object)base.targetPlayer != (Object)null) ? Mathf.Sqrt(distanceToPlayerSqr) : (-1f)); float num2 = (((Object)(object)closestPlayer != (Object)null) ? Mathf.Sqrt(distanceToClosestPlayerSqr) : (-1f)); string text5 = $"dist(target)={num:0.0}m dist(closest)={num2:0.0}m " + $"ranges: aware={Mathf.Sqrt(awareDistSqr):0.0}(keep {Mathf.Sqrt(awareKeepDistSqr):0.0}) aggro={Mathf.Sqrt(aggroRangeSqr):0.0} idle={Mathf.Sqrt(idleDistanceSqr):0.0}(keep {Mathf.Sqrt(idleKeepDistSqr):0.0}) atk={Mathf.Sqrt(attackRangeSqr):0.0} buffer={Mathf.Sqrt(bufferDistSqr):0.0}"; string text6 = $"seen={isSeen} targetSees={targetSeesGargoyle} canSee={canSeePlayer} " + $"push: enable={enablePush} stage={pushStage} timerIn={pushTimer - Time.time:0.0}s"; string text7 = string.Format("killTrigger={0} dist={1:0.0}m", ((Object)(object)killTrigger != (Object)null) ? ((Object)killTrigger).name : "null", (distToKillTriggerSqr == float.MaxValue) ? (-1f) : Mathf.Sqrt(distToKillTriggerSqr)); bool flag = false; if ((Object)(object)base.targetPlayer != (Object)null && playerPushStates.TryGetValue(base.targetPlayer, out ConcurrentDictionary<int, bool> value) && value.TryGetValue(myID, out var value2)) { flag = value2; } ConcurrentDictionary<int, bool> value3; string text8 = (((Object)(object)base.targetPlayer != (Object)null) ? $"pushMap: iAmPushing={flag} entries={(playerPushStates.TryGetValue(base.targetPlayer, out value3) ? value3.Count : 0)}" : "pushMap: n/a"); Vector3 position = ((Component)this).transform.position; Vector3 destination = base.agent.destination; Vector3 velocity = base.agent.velocity; string text9 = $"agent: enabled={((Behaviour)base.agent).enabled} onNavMesh={base.agent.isOnNavMesh} " + $"speed={base.agent.speed:0.00} angSpeed={base.agent.angularSpeed:0.0} stopDist={base.agent.stoppingDistance:0.00} " + $"hasPath={base.agent.hasPath} pending={base.agent.pathPending} status={base.agent.pathStatus} " + $"remDist={base.agent.remainingDistance:0.00} " + $"pos=({position.x:0.0},{position.y:0.0},{position.z:0.0}) dest=({destination.x:0.0},{destination.y:0.0},{destination.z:0.0}) vel=({velocity.x:0.0},{velocity.y:0.0},{velocity.z:0.0})"; if (pathingTask == null) { string text10 = "smartPath: task=null"; } else { bool isStarted = pathingTask.IsStarted; bool flag2 = isStarted && pathingTask.IsResultReady(0); string text11 = $"requested=({_lastRequestedDest.x:0.0},{_lastRequestedDest.y:0.0},{_lastRequestedDest.z:0.0})"; string text12 = $"lastActive=({_lastActiveDestination.x:0.0},{_lastActiveDestination.y:0.0},{_lastActiveDestination.z:0.0})"; string text13; if (!flag2) { text13 = "result=not-ready"; } else { SmartPathDestination? result = pathingTask.GetResult(0); if (!result.HasValue) { text13 = "result=null"; } else { SmartPathDestination value4 = result.Value; Vector3 position2 = ((SmartPathDestination)(ref value4)).Position; text13 = $"result: type={((SmartPathDestination)(ref value4)).Type} pos=({position2.x:0.0},{position2.y:0.0},{position2.z:0.0})"; } } string text10 = $"smartPath: started={isStarted} ready={flag2} {text11} {text12} {text13}"; } string text14 = (((Object)(object)currentDoor == (Object)null) ? "door=null" : $"door={((Object)currentDoor).name} locked={currentDoor.isLocked} trigCached={(Object)(object)currentDoorTrigger != (Object)null} trigBool={(Object)(object)currentDoorTrigger != (Object)null && currentDoorTrigger.boolValue} lastCloseAgo={Time.time - lastDoorCloseTime:0.00}s"); } public override void DoAIInterval() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) ((EnemyAI)this).DoAIInterval(); if (base.isEnemyDead || StartOfRound.Instance.allPlayersDead) { if (StartOfRound.Instance.allPlayersDead) { ClearAllVariables(); } return; } _ = base.currentBehaviourStateIndex; _ = previousStateIndex; previousStateIndex = base.currentBehaviourStateIndex; if ((Object)(object)base.targetPlayer != (Object)null) { killTrigger = FindNearestKillTrigger(cachedTargetPosition); if (Time.time - playerCheckTimer > 3f) { ChangeTarget(); playerCheckTimer = Time.time; } } if ((Object)(object)currentDoorTrigger == (Object)null && (Object)(object)currentDoor != (Object)null) { currentDoorTrigger = ((Component)currentDoor).gameObject.GetComponent<AnimatedObjectTrigger>(); } if (Time.time - lastDoorCloseTime >= 0.75f && (Object)(object)currentDoor != (Object)null && !currentDoor.isLocked && (Object)(object)currentDoorTrigger != (Object)null && currentDoorTrigger.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; currentDoorTrigger = null; } } switch (base.currentBehaviourStateIndex) { case 0: if (FoundClosestPlayerInRange()) { SwitchState(State.StealthyPursuit); } if (base.agent.hasPath) { SetAnim(base.agent.hasPath ? AnimState.Walk : AnimState.Idle); } else { SetAnim(AnimState.Idle); } break; case 1: case 2: if (base.agent.hasPath) { SetAnim(AnimState.Walk); } else { SetAnim(AnimState.Idle); } break; case 4: SetAnim(AnimState.Idle); break; case 3: if (base.agent.hasPath) { SetAnim(AnimState.Chase); } else { SetAnim(AnimState.Idle); } 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); SetSmartDestination(targetPosition); } else { SetSmartDestination(cachedTargetPosition); } targetTimer = Time.time; } if (base.agent.hasPath) { SetAnim(AnimState.Chase); } else { SetAnim(AnimState.Idle); } break; } } private void OnDisable() { CleanupSmartPathing(); } public override void OnNetworkDespawn() { RemoveSelfFromSharedState(); ((NetworkBehaviour)this).OnNetworkDespawn(); } public override void OnDestroy() { RemoveSelfFromSharedState(); CleanupSmartPathing(); ((EnemyAI)this).OnDestroy(); } private void RemoveSelfFromSharedState() { gargoyleTargets.TryRemove(myID, out PlayerControllerB _); foreach (KeyValuePair<PlayerControllerB, ConcurrentDictionary<int, bool>> playerPushState in playerPushStates) { playerPushState.Value.TryRemove(myID, out var _); } activeGargoyles.Remove(this); } private void CleanupSmartPathing() { SmartPathTask? obj = pathingTask; if (obj != null) { obj.Dispose(); } pathingTask = null; if (_smartRegistered) { if ((Object)(object)base.agent != (Object)null) { SmartPathfinding.UnregisterSmartAgent(base.agent); } _smartRegistered = false; } } private static void ClearAllVariables() { activeGargoyles.Clear(); gargoyl
plugins/LethalGargoyles/lib/NVorbis/NVorbis.dll
Decompiled a month 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;