Decompiled source of Sailwind Radio v1.0.1
BepInEx/plugins/SailwindRadio/SailwindRadio.dll
Decompiled 6 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Security; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using NLayer; using SailwindRadio.Acoustics; using SailwindRadio.Library; using SailwindRadio.Models; using SailwindRadio.Persistence; using SailwindRadio.Physical; using SailwindRadio.Playback; using SailwindRadio.Shops; using SailwindRadio.UI; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("SailwindRadio")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("SailwindRadio")] [assembly: AssemblyTitle("SailwindRadio")] [assembly: AssemblyVersion("1.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SailwindRadio { public sealed class RadioBassFilter : MonoBehaviour { private sealed class BassFilter { internal readonly int SampleRate; private readonly double[] b0 = new double[2]; private readonly double[] b1 = new double[2]; private readonly double[] b2 = new double[2]; private readonly double[] a1 = new double[2]; private readonly double[] a2 = new double[2]; private readonly double[] z1 = new double[16]; private readonly double[] z2 = new double[16]; internal BassFilter(int sampleRate) { SampleRate = sampleRate; double num = Math.PI * 280.0 / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); for (int i = 0; i < 2; i++) { double num4 = ((i == 0) ? 0.541196100146197 : 1.30656296487638); double num5 = num3 / (2.0 * num4); double num6 = 1.0 + num5; b0[i] = (1.0 - num2) / (2.0 * num6); b1[i] = (1.0 - num2) / num6; b2[i] = b0[i]; a1[i] = -2.0 * num2 / num6; a2[i] = (1.0 - num5) / num6; } } internal float Apply(float sample, int channel) { if (channel >= 8) { return 0f; } double num = (RadioSpeakerOutput.Finite(sample) ? sample : 0f); for (int i = 0; i < 2; i++) { int num2 = i * 8 + channel; double num3 = b0[i] * num + z1[num2]; z1[num2] = b1[i] * num - a1[i] * num3 + z2[num2]; z2[num2] = b2[i] * num - a2[i] * num3; num = num3; } return (float)(num * 2.0); } } private volatile BassFilter bass; internal void SetProfile(int sampleRate, bool woofer) { BassFilter bassFilter = bass; if (!woofer) { bass = null; return; } if (sampleRate < 8000 || sampleRate > 192000) { sampleRate = 48000; } if (bassFilter == null || bassFilter.SampleRate != sampleRate) { bass = new BassFilter(sampleRate); } } private void OnAudioFilterRead(float[] data, int channels) { Process(data, channels); } internal void Process(float[] data, int channels) { BassFilter bassFilter = bass; if (channels <= 0 || bassFilter == null) { return; } for (int i = 0; i < data.Length; i += channels) { for (int j = 0; j < channels && i + j < data.Length; j++) { float num = bassFilter.Apply(data[i + j], j); data[i + j] = (RadioSpeakerOutput.Finite(num) ? Math.Max(-1f, Math.Min(1f, num)) : 0f); } } } } public sealed class RadioPlayback : IDisposable { internal sealed class DecodedMp3 { internal readonly List<float[]> Blocks = new List<float[]>(); internal int Channels; internal int SampleRate; internal int SampleCount; } internal sealed class Mp3LoadResult { internal DecodedMp3 Decoded; internal StreamedMp3 Stream; } internal sealed class CancellableFileStream : Stream { private readonly FileStream input; private readonly CancellationToken token; public override bool CanRead => true; public override bool CanSeek => true; public override bool CanWrite => false; public override long Length => input.Length; public override long Position { get { return input.Position; } set { CancellationToken cancellationToken = token; cancellationToken.ThrowIfCancellationRequested(); input.Position = value; } } internal CancellableFileStream(string path, CancellationToken token) { this.token = token; input = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); } public override int Read(byte[] buffer, int offset, int count) { CancellationToken cancellationToken = token; cancellationToken.ThrowIfCancellationRequested(); return input.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { CancellationToken cancellationToken = token; cancellationToken.ThrowIfCancellationRequested(); return input.Seek(offset, origin); } public override void Flush() { } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } protected override void Dispose(bool disposing) { if (disposing) { input.Dispose(); } base.Dispose(disposing); } } private const double ScheduleLeadSeconds = 0.05; private const double LoadTimeoutSeconds = 60.0; private readonly RadioState state; private readonly Action<string> warning; private readonly MonoBehaviour host; private RadioAudioLifetime lifetime; private GameObject emitter; private AudioSource source; private RadioSpeakerOutput builtIn; private readonly Dictionary<int, RadioSpeakerOutput> endpoints = new Dictionary<int, RadioSpeakerOutput>(); private readonly List<int> lostEndpoints = new List<int>(); private Vector3 listenerPosition; private bool listenerKnown; private AudioClip clip; private UnityWebRequest request; private Task<Mp3LoadResult> mp3Task; private CancellationTokenSource mp3Cancellation; private StreamedMp3 streamedMp3; private string requestedPath; private string failure; private bool disposed; private bool running; private bool voicesPaused; private bool recoveryWarned; private double nextHealthCheck; private bool wasPowered; private volatile bool resetRequested; private double scheduledDsp; private double lastDsp; private int scheduledSample; private double loadStartedAt; private double streamWaitingSince = -1.0; internal const int MaximumMp3Samples = 67108864; internal const long MaximumMp3FileBytes = 134217728L; internal const long MaximumStreamedMp3FileBytes = 1073741824L; private static readonly SemaphoreSlim Mp3DecodeSlot = new SemaphoreSlim(1, 1); private string preloadPath; private bool preloadStarted; private bool preloadFailed; private AudioClip preloadClip; private UnityWebRequest preloadRequest; private Task<Mp3LoadResult> preloadTask; private CancellationTokenSource preloadCancellation; private double preloadStartedAt; public string Status { get; private set; } public string TrackLabel { get; private set; } public bool RepeatTrack { get; set; } = true; public bool TrackEnded { get; private set; } public bool LoadFailed => failure != null; public string FailedPath { get; private set; } public RadioPlayback(MonoBehaviour coroutineHost, RadioState state, Action<string> warning) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown if ((Object)(object)coroutineHost == (Object)null) { throw new ArgumentNullException("coroutineHost"); } if (state == null) { throw new ArgumentNullException("state"); } this.state = state; host = coroutineHost; this.warning = warning ?? ((Action<string>)delegate { }); builtIn = new RadioSpeakerOutput("Sailwind Radio audio", RadioSpeakerProfile.BuiltIn, this.warning); emitter = builtIn.Emitter; source = builtIn.Source; lifetime = emitter.AddComponent<RadioAudioLifetime>(); lifetime.Host = coroutineHost; lifetime.Playback = this; AudioSettings.OnAudioConfigurationChanged += new AudioConfigurationChangeHandler(OnAudioConfigurationChanged); Status = "Off"; TrackLabel = "No track"; lastDsp = AudioSettings.dspTime; } public void SetTrack(string path) { if (!disposed) { path = path ?? ""; if (!string.Equals(state.TrackPath, path, StringComparison.Ordinal)) { state.PositionSeconds = 0.0; } state.TrackPath = path; ReleaseTrack(); failure = null; FailedPath = null; TrackEnded = false; requestedPath = null; if (!AdoptPreload(path)) { ReleasePreload(); } } } public void SetCarried(bool carried) { if (!disposed) { builtIn.Carried = carried; } } public void BeginEndpoints() { if (disposed) { return; } foreach (RadioSpeakerOutput value in endpoints.Values) { value.Seen = false; } } public void SetEndpoint(int id, Vector3 position, bool carried, int kind, float localVolume, float bass, float obstruction) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (disposed) { return; } RadioSpeakerProfile radioSpeakerProfile = RadioSpeakerProfile.ForKind(kind); if (radioSpeakerProfile != null) { if (!endpoints.TryGetValue(id, out var value) || (Object)(object)value.Emitter == (Object)null || (Object)(object)value.Source == (Object)null || value.Profile != radioSpeakerProfile) { value?.Dispose(); value = new RadioSpeakerOutput("Sailwind Radio speaker " + id, radioSpeakerProfile, warning); endpoints[id] = value; } value.Profile = radioSpeakerProfile; value.Position = position; value.Carried = carried; value.LocalVolume = localVolume; value.Bass = bass; value.Obstruction = obstruction; value.Seen = true; } } public void EndEndpoints() { if (disposed) { return; } lostEndpoints.Clear(); foreach (KeyValuePair<int, RadioSpeakerOutput> endpoint in endpoints) { if (!endpoint.Value.Seen) { lostEndpoints.Add(endpoint.Key); } } foreach (int lostEndpoint in lostEndpoints) { endpoints[lostEndpoint].Dispose(); endpoints.Remove(lostEndpoint); } } public void SetAcoustics(Vector3 listenerPosition, bool listenerKnown, float obstruction) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!disposed) { this.listenerPosition = listenerPosition; this.listenerKnown = listenerKnown && IsFinite(listenerPosition.x) && IsFinite(listenerPosition.y) && IsFinite(listenerPosition.z); builtIn.Obstruction = obstruction; } } public void Tick(Vector3 worldPosition, bool suspended) { //IL_002a: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Invalid comparison between Unknown and I4 //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Invalid comparison between Unknown and I4 //IL_0427: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Invalid comparison between Unknown and I4 //IL_04b2: Unknown result type (might be due to invalid IL or missing references) //IL_04b8: Invalid comparison between Unknown and I4 if (disposed) { return; } if ((Object)(object)host == (Object)null) { Dispose(); return; } EnsureOwnedOutput(); builtIn.Position = worldPosition; builtIn.LocalVolume = state.LocalVolume; state.Volume = (float.IsNaN(state.Volume) ? 0.5f : Mathf.Clamp01(state.Volume)); builtIn.Update(state.Volume, listenerPosition, listenerKnown); source.loop = RepeatTrack; foreach (RadioSpeakerOutput value in endpoints.Values) { value.Update(state.Volume, listenerPosition, listenerKnown); if ((Object)(object)value.Source != (Object)null) { value.Source.loop = RepeatTrack; } } double dspTime = AudioSettings.dspTime; if (resetRequested || dspTime < lastDsp) { StopOutputs(); resetRequested = false; } lastDsp = dspTime; PollPreload(); if (!string.Equals(requestedPath, state.TrackPath ?? "", StringComparison.Ordinal)) { ReleaseTrack(); requestedPath = state.TrackPath ?? ""; failure = null; FailedPath = null; TrackEnded = false; TrackLabel = SafeLabel(requestedPath); if (!AdoptPreload(requestedPath)) { ReleasePreload(); if (state.Powered) { BeginLoad(); } } } else if (state.Powered && !wasPowered && (Object)(object)clip == (Object)null && request == null && mp3Task == null) { failure = null; FailedPath = null; BeginLoad(); } wasPowered = state.Powered; CompleteLoad(); CompleteMp3Load(); if (streamedMp3 != null) { if (streamedMp3.Fault != null) { Fail("Unable to decode music", streamedMp3.Fault.Message); } else { int num = SafeSavedFrame(); streamedMp3.Request(running ? SampleAt(dspTime) : num); } } if (request != null || mp3Task != null || ((Object)(object)clip != (Object)null && (int)clip.loadState != 2)) { if ((Object)(object)clip != (Object)null && (int)clip.loadState == 3) { Fail("Unable to decode music", "The decoder failed to load audio data"); } else if ((double)Time.realtimeSinceStartup - loadStartedAt >= 60.0) { Fail("Unable to load music", "Audio loading timed out after 60 seconds"); } } FinishIfEnded(); bool flag = state.Powered && !state.Paused && !suspended && !AudioListener.pause && !TrackEnded; int num2 = SafeSavedFrame(); bool flag2 = streamedMp3 == null || streamedMp3.Ready(running ? SampleAt(dspTime) : num2); if (streamedMp3 != null && flag && !flag2) { if (streamWaitingSince < 0.0) { streamWaitingSince = Time.realtimeSinceStartup; } else if ((double)Time.realtimeSinceStartup - streamWaitingSince >= 60.0) { Fail("Unable to decode music", "Streamed MP3 buffer did not recover"); flag = false; } } else { streamWaitingSince = -1.0; } if (!flag && running) { CapturePosition(); PauseOutputs(); } if (flag && (Object)(object)clip != (Object)null && (int)clip.loadState == 2 && !running && flag2) { StartAtSavedPosition(); } if (running) { CapturePosition(); } if (running) { JoinEndpoints(); } if (running) { CheckOutputHealth(); } Status = ((!state.Powered) ? "Off" : ((failure != null) ? failure : ((request != null || mp3Task != null || ((Object)(object)clip != (Object)null && (int)clip.loadState != 2) || (streamedMp3 != null && !running && !flag2)) ? "Loading" : (((Object)(object)clip == (Object)null) ? "No track" : (TrackEnded ? "Ended" : (state.Paused ? "Paused" : ((suspended || AudioListener.pause) ? "Suspended" : "Playing"))))))); } public void CapturePosition() { if (!disposed && running && !((Object)(object)clip == (Object)null) && !resetRequested && !(AudioSettings.dspTime < lastDsp) && !FinishIfEnded()) { state.PositionSeconds = (double)SampleAt(AudioSettings.dspTime) / (double)clip.frequency; } } private int SafeSavedFrame() { if ((Object)(object)clip == (Object)null || clip.samples <= 0 || clip.frequency <= 0) { return 0; } double positionSeconds = state.PositionSeconds; if (double.IsNaN(positionSeconds) || double.IsInfinity(positionSeconds) || positionSeconds < 0.0) { return 0; } return (int)Math.Min(clip.samples - 1, Math.Floor(positionSeconds * (double)clip.frequency)); } private int SampleAt(double dsp) { long num = scheduledSample + (long)Math.Round(Math.Max(0.0, dsp - scheduledDsp) * (double)clip.frequency); if (!RepeatTrack) { return (int)Math.Min(clip.samples, num); } return (int)(num % clip.samples); } internal static float DistanceGain(float distance) { return RadioSpeakerOutput.DistanceGain(distance, RadioSpeakerProfile.BuiltIn.Radius); } private static bool IsFinite(float value) { return RadioSpeakerOutput.Finite(value); } private void BeginLoad() { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Invalid comparison between Unknown and I4 //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Invalid comparison between Unknown and I4 //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(requestedPath)) { return; } try { if (!Path.IsPathRooted(requestedPath)) { throw new IOException("Choose an absolute local music file path"); } string fullPath = Path.GetFullPath(requestedPath); Uri uri = new Uri(fullPath); if (!uri.IsFile || uri.IsUnc) { throw new IOException("Choose a local music file"); } AudioType val = (AudioType)(Path.GetExtension(fullPath).ToLowerInvariant() switch { ".mp3" => 13, ".ogg" => 14, ".wav" => 20, _ => throw new IOException("Choose an MP3, OGG or WAV file"), }); if (!File.Exists(fullPath)) { throw new IOException("Music file is missing or inaccessible"); } if (new FileInfo(fullPath).Length > (((int)val == 13) ? 1073741824 : 134217728)) { throw new IOException("Audio file exceeds the size limit"); } loadStartedAt = Time.realtimeSinceStartup; if ((int)val == 13) { mp3Cancellation = new CancellationTokenSource(); mp3Cancellation.CancelAfter(60000); CancellationToken token = mp3Cancellation.Token; double saved = state.PositionSeconds; mp3Task = Task.Run(() => PrepareMp3(fullPath, saved, token), token); } else { request = UnityWebRequestMultimedia.GetAudioClip(uri.AbsoluteUri, val); ((DownloadHandlerAudioClip)request.downloadHandler).streamAudio = false; request.SendWebRequest(); } } catch (Exception ex) { Fail("Unable to load music", ex.Message); } } private void CompleteLoad() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Invalid comparison between Unknown and I4 if (request == null || !request.isDone) { return; } try { if (request.isNetworkError || request.isHttpError) { throw new IOException(request.error ?? "Audio request failed"); } clip = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)clip == (Object)null || (int)clip.loadState == 3 || clip.samples <= 0 || clip.frequency <= 0 || (long)clip.samples * (long)clip.channels > 67108864) { throw new IOException("The file could not be decoded as audio"); } source.clip = clip; request.Dispose(); request = null; } catch (Exception ex) { Fail("Unable to decode music", ex.Message); } } private void CompleteMp3Load() { if (mp3Task == null || !mp3Task.IsCompleted) { return; } try { if (mp3Task.IsCanceled) { throw new IOException("MP3 decoding timed out"); } if (mp3Task.IsFaulted) { throw new IOException("MP3 decoding failed: " + mp3Task.Exception.GetBaseException().Message); } Mp3LoadResult result = mp3Task.Result; mp3Task = null; mp3Cancellation.Dispose(); mp3Cancellation = null; if (result.Stream != null) { streamedMp3 = result.Stream; StreamedMp3.Reader reader = streamedMp3.CreateReader(); clip = streamedMp3.CreateClip(reader); if ((Object)(object)clip == (Object)null) { throw new IOException("Unable to create streamed audio clip"); } builtIn.StreamClip = clip; source.clip = clip; ReleasePreload(); return; } DecodedMp3 decoded = result.Decoded; clip = AudioClip.Create("Sailwind Radio MP3", decoded.SampleCount / decoded.Channels, decoded.Channels, decoded.SampleRate, false); if ((Object)(object)clip == (Object)null) { throw new IOException("Unable to allocate the decoded audio clip"); } int num = 0; foreach (float[] block in decoded.Blocks) { if (!clip.SetData(block, num)) { throw new IOException("Unable to upload decoded audio"); } num += block.Length / decoded.Channels; } source.clip = clip; } catch (Exception ex) { Fail("Unable to decode music", ex.Message); } } internal static Mp3LoadResult PrepareMp3(string path, double initialSeconds, CancellationToken token, int maximumSamples = 67108864) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown token.ThrowIfCancellationRequested(); using (CancellableFileStream cancellableFileStream = new CancellableFileStream(path, token)) { if (cancellableFileStream.Length > 1073741824) { throw new IOException("MP3 exceeds the streamed file size limit"); } MpegFile val = new MpegFile((Stream)cancellableFileStream); try { int channels = val.Channels; if (channels < 1 || channels > 2 || val.SampleRate < 8000 || val.SampleRate > 96000) { throw new IOException("Unsupported MP3 channel count or sample rate"); } if (((val.Length < 0) ? (-1) : (val.Length / 4)) > maximumSamples || cancellableFileStream.Length > 134217728) { if (val.Length % (channels * 4) != 0L) { throw new IOException("Invalid MP3 sample length"); } if (double.IsNaN(initialSeconds) || double.IsInfinity(initialSeconds) || initialSeconds < 0.0) { initialSeconds = 0.0; } int initialFrame = (int)Math.Min(2147483646.0, initialSeconds * (double)val.SampleRate); return new Mp3LoadResult { Stream = StreamedMp3.Open(path, initialFrame, token) }; } } finally { ((IDisposable)val)?.Dispose(); } } return new Mp3LoadResult { Decoded = DecodeMp3(path, token, maximumSamples, 134217728L) }; } internal static bool IsLongMp3(string path, CancellationToken token) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown token.ThrowIfCancellationRequested(); using CancellableFileStream cancellableFileStream = new CancellableFileStream(path, token); if (cancellableFileStream.Length > 134217728) { return true; } MpegFile val = new MpegFile((Stream)cancellableFileStream); try { return val.Length > 268435456; } finally { ((IDisposable)val)?.Dispose(); } } internal static DecodedMp3 DecodeMp3(string path, CancellationToken token, int maximumSamples = 67108864, long maximumFileBytes = 134217728L) { Mp3DecodeSlot.Wait(token); try { return DecodeMp3Core(path, token, maximumSamples, maximumFileBytes); } finally { Mp3DecodeSlot.Release(); } } private static DecodedMp3 DecodeMp3Core(string path, CancellationToken token, int maximumSamples, long maximumFileBytes) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown token.ThrowIfCancellationRequested(); using CancellableFileStream cancellableFileStream = new CancellableFileStream(path, token); if (cancellableFileStream.Length > maximumFileBytes) { throw new IOException("MP3 exceeds the 128 MiB file size limit"); } MpegFile val = new MpegFile((Stream)cancellableFileStream); try { DecodedMp3 decodedMp = new DecodedMp3 { Channels = val.Channels, SampleRate = val.SampleRate }; if (decodedMp.Channels < 1 || decodedMp.Channels > 2 || decodedMp.SampleRate < 8000 || decodedMp.SampleRate > 96000) { throw new IOException("Unsupported MP3 channel count or sample rate"); } while (true) { token.ThrowIfCancellationRequested(); float[] array = new float[16384]; int num = val.ReadSamples(array, 0, array.Length); if (num == 0) { break; } if (num < 0 || num % decodedMp.Channels != 0) { throw new IOException("MP3 decoder returned incomplete sample frames"); } if (num > maximumSamples - decodedMp.SampleCount) { throw new IOException("MP3 exceeds the 256 MiB decoded audio limit"); } if (num != array.Length) { Array.Resize(ref array, num); } for (int i = 0; i < array.Length; i++) { array[i] = (float.IsNaN(array[i]) ? 0f : Math.Max(-1f, Math.Min(1f, array[i]))); } decodedMp.Blocks.Add(array); decodedMp.SampleCount += num; } token.ThrowIfCancellationRequested(); if (decodedMp.SampleCount == 0) { throw new IOException("The MP3 contains no decodable audio"); } return decodedMp; } finally { ((IDisposable)val)?.Dispose(); } } private void StartAtSavedPosition() { double num = state.PositionSeconds; if (double.IsNaN(num) || double.IsInfinity(num) || num < 0.0) { num = 0.0; } double num2 = (double)clip.samples / (double)clip.frequency; if (!RepeatTrack && num >= num2) { state.PositionSeconds = num2; TrackEnded = true; return; } scheduledSample = (int)Math.Min(clip.samples - 1, Math.Floor(num % num2 * (double)clip.frequency)); double dspTime = AudioSettings.dspTime; scheduledDsp = dspTime + (voicesPaused ? 0.0 : 0.05); if (voicesPaused) { ResumeOutput(builtIn, dspTime); foreach (RadioSpeakerOutput value in endpoints.Values) { ResumeOutput(value, dspTime); } } else { ScheduleEndpoint(builtIn, scheduledDsp, scheduledSample); foreach (RadioSpeakerOutput value2 in endpoints.Values) { ScheduleEndpoint(value2, scheduledDsp, scheduledSample); } } running = true; voicesPaused = false; nextHealthCheck = dspTime + 0.5; } private void ResumeOutput(RadioSpeakerOutput output, double now) { if (output.Paused && (Object)(object)output.Source != (Object)null && (Object)(object)output.Source.clip == (Object)(object)(((Object)(object)output.StreamClip != (Object)null) ? output.StreamClip : clip)) { output.Resume(scheduledSample, now); } else { ScheduleEndpoint(output, now + 0.05, SampleAt(now + 0.05)); } } private double EndDspTime() { if (!((Object)(object)clip == (Object)null)) { return scheduledDsp + (double)(clip.samples - scheduledSample) / (double)clip.frequency; } return double.PositiveInfinity; } private bool FinishIfEnded() { if (!running || RepeatTrack || (Object)(object)clip == (Object)null || AudioSettings.dspTime < EndDspTime()) { return false; } state.PositionSeconds = (double)clip.samples / (double)clip.frequency; TrackEnded = true; StopOutputs(); return true; } private void JoinEndpoints() { double dspTime = AudioSettings.dspTime; bool waitingForStart = dspTime < scheduledDsp; JoinOutput(builtIn, dspTime, waitingForStart); foreach (RadioSpeakerOutput value in endpoints.Values) { JoinOutput(value, dspTime, waitingForStart); } } private void JoinOutput(RadioSpeakerOutput output, double now, bool waitingForStart) { if (!output.Running && !((Object)(object)output.Source == (Object)null)) { double num = (waitingForStart ? scheduledDsp : (now + 0.05)); if (RepeatTrack || !(num >= EndDspTime())) { ScheduleEndpoint(output, num, SampleAt(num)); } } } private void ScheduleEndpoint(RadioSpeakerOutput endpoint, double start, int sample) { if (!((Object)(object)endpoint.Source == (Object)null) && !((Object)(object)clip == (Object)null) && sample >= 0 && sample < clip.samples && (RepeatTrack || !(start >= EndDspTime()))) { if (streamedMp3 != null && (Object)(object)endpoint.StreamClip == (Object)null) { endpoint.StreamClip = streamedMp3.CreateClip(streamedMp3.CreateReader()); } endpoint.Source.clip = (((Object)(object)endpoint.StreamClip != (Object)null) ? endpoint.StreamClip : clip); endpoint.Source.loop = RepeatTrack; endpoint.Source.timeSamples = sample; endpoint.Source.PlayScheduled(start); endpoint.Running = true; endpoint.Paused = false; endpoint.SettlesAt = start + 0.3; endpoint.StartsAt = start; } } private void PauseOutputs() { if (AudioSettings.dspTime < scheduledDsp) { StopOutputs(); return; } running = false; voicesPaused = true; PauseOutput(builtIn); foreach (RadioSpeakerOutput value in endpoints.Values) { PauseOutput(value); } } private static void PauseOutput(RadioSpeakerOutput output) { if (AudioSettings.dspTime < output.StartsAt) { output.Stop(); } else { output.Pause(); } } private void StopOutputs(bool clearClip = false) { running = false; voicesPaused = false; if (builtIn != null) { builtIn.Stop(clearClip); } foreach (RadioSpeakerOutput value in endpoints.Values) { value.Stop(clearClip); } } private void EnsureOwnedOutput() { if ((Object)(object)emitter == (Object)null || (Object)(object)source == (Object)null) { RadioSpeakerOutput radioSpeakerOutput = builtIn; if ((Object)(object)lifetime != (Object)null) { lifetime.Playback = null; } builtIn = new RadioSpeakerOutput("Sailwind Radio audio", RadioSpeakerProfile.BuiltIn, warning); builtIn.Carried = radioSpeakerOutput.Carried; builtIn.Obstruction = radioSpeakerOutput.Obstruction; if ((Object)(object)radioSpeakerOutput.StreamClip == (Object)(object)clip) { radioSpeakerOutput.StreamClip = null; } radioSpeakerOutput.Dispose(); emitter = builtIn.Emitter; source = builtIn.Source; lifetime = emitter.AddComponent<RadioAudioLifetime>(); lifetime.Host = host; lifetime.Playback = this; WarnRecovery(); } EnableOwnedOutput(builtIn); foreach (RadioSpeakerOutput value in endpoints.Values) { EnableOwnedOutput(value); } } private void EnableOwnedOutput(RadioSpeakerOutput output) { if (!((Object)(object)output.Emitter == (Object)null) && !((Object)(object)output.Source == (Object)null)) { if (!output.Emitter.activeSelf) { output.Emitter.SetActive(true); WarnRecovery(); } if (!((Behaviour)output.Source).enabled) { ((Behaviour)output.Source).enabled = true; WarnRecovery(); } } } private void WarnRecovery() { if (!recoveryWarned) { recoveryWarned = true; warning("Recovered an unavailable radio audio output"); } } private void CheckOutputHealth() { double dspTime = AudioSettings.dspTime; if (dspTime < nextHealthCheck) { return; } nextHealthCheck = dspTime + 0.25; RepairOutput(builtIn, dspTime); foreach (RadioSpeakerOutput value in endpoints.Values) { RepairOutput(value, dspTime); } } private void RepairOutput(RadioSpeakerOutput output, double now) { if (!output.Running || (Object)(object)output.Source == (Object)null || now < output.SettlesAt) { return; } if (output.Source.isVirtual || output.Source.volume <= 0f) { output.DriftObservations = 0; return; } int num = SampleAt(now); int num2 = Math.Abs(output.Source.timeSamples - num); if (RepeatTrack) { num2 = Math.Min(num2, clip.samples - num2); } int num3 = default(int); int num4 = default(int); AudioSettings.GetDSPBufferSize(ref num3, ref num4); double num5 = Math.Max(0.05, 2.0 * (double)num3 / (double)Math.Max(8000, AudioSettings.outputSampleRate) + 0.01); if (output.Source.isPlaying) { if ((double)num2 <= (double)clip.frequency * num5) { output.DriftObservations = 0; return; } if (++output.DriftObservations < 2) { return; } } output.DriftObservations = 0; if (output.Source.isPlaying) { output.Pause(); output.Resume(num, now); } else { ScheduleEndpoint(output, now + 0.05, SampleAt(now + 0.05)); } } private void Fail(string message, string detail) { ReleaseTrack(); failure = message; FailedPath = requestedPath; warning(message + ": " + detail); } private static string SafeLabel(string path) { if (string.IsNullOrWhiteSpace(path)) { return "No track"; } try { return Path.GetFileNameWithoutExtension(path); } catch { return "Music file"; } } private void ReleaseTrack() { try { StopOutputs(clearClip: true); } finally { ReleaseTrackResources(); } } private void ReleaseTrackResources() { if (mp3Cancellation != null) { Task<Mp3LoadResult> task = mp3Task; CancellationTokenSource cancellation = mp3Cancellation; mp3Task = null; mp3Cancellation = null; cancellation.Cancel(); if (task == null) { cancellation.Dispose(); } else { task.ContinueWith(delegate(Task<Mp3LoadResult> completed) { if (completed.IsFaulted) { _ = completed.Exception; } if (completed.Status == TaskStatus.RanToCompletion && completed.Result.Stream != null) { completed.Result.Stream.Dispose(); } cancellation.Dispose(); }, TaskScheduler.Default); } } try { DiscardRequest(ref request, clip); } finally { foreach (RadioSpeakerOutput value in endpoints.Values) { value.ReleaseStreamClip(); } if (builtIn != null) { if ((Object)(object)builtIn.StreamClip == (Object)(object)clip) { builtIn.StreamClip = null; } else { builtIn.ReleaseStreamClip(); } } if ((Object)(object)clip != (Object)null) { Object.Destroy((Object)(object)clip); } clip = null; if (streamedMp3 != null) { streamedMp3.Dispose(); streamedMp3 = null; } streamWaitingSince = -1.0; } } private void OnAudioConfigurationChanged(bool deviceWasChanged) { resetRequested = true; } public void Dispose() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown if (disposed) { return; } try { CapturePosition(); } finally { disposed = true; AudioSettings.OnAudioConfigurationChanged -= new AudioConfigurationChangeHandler(OnAudioConfigurationChanged); try { try { ReleaseTrack(); } finally { ReleasePreload(); } } finally { foreach (RadioSpeakerOutput value in endpoints.Values) { value.Dispose(); } endpoints.Clear(); if (builtIn != null) { builtIn.Dispose(); } builtIn = null; emitter = null; source = null; } } } public void PreloadTrack(string path) { if (!disposed) { path = path ?? ""; if (string.Equals(path, state.TrackPath, StringComparison.OrdinalIgnoreCase)) { path = ""; } if (!string.Equals(path, preloadPath ?? "", StringComparison.OrdinalIgnoreCase)) { ReleasePreload(); preloadPath = path; } } } private void PollPreload() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Invalid comparison between Unknown and I4 //IL_0378: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Invalid comparison between Unknown and I4 //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(preloadPath) || preloadFailed) { return; } try { if (!preloadStarted) { if (!state.Powered || (Object)(object)clip == (Object)null || (int)clip.loadState != 2 || streamedMp3 != null) { return; } if (!Path.IsPathRooted(preloadPath)) { throw new IOException("Expected an absolute local path"); } string fullPath = Path.GetFullPath(preloadPath); Uri uri = new Uri(fullPath); if (!uri.IsFile || uri.IsUnc || !File.Exists(fullPath)) { throw new IOException("Upcoming track unavailable"); } if (new FileInfo(fullPath).Length > 134217728) { throw new IOException("Upcoming file exceeds the size limit"); } string text = Path.GetExtension(fullPath).ToLowerInvariant(); preloadStartedAt = Time.realtimeSinceStartup; preloadStarted = true; int num; AudioType val; switch (text) { case ".mp3": { preloadCancellation = new CancellationTokenSource(); preloadCancellation.CancelAfter(60000); CancellationToken token = preloadCancellation.Token; preloadTask = Task.Run(delegate { if (IsLongMp3(fullPath, token)) { throw new IOException("Long MP3 starts streaming only when selected"); } return new Mp3LoadResult { Decoded = DecodeMp3(fullPath, token, 67108864, 134217728L) }; }, token); break; } default: num = 0; goto IL_017d; case ".wav": num = 20; goto IL_017d; case ".ogg": { num = 14; goto IL_017d; } IL_017d: val = (AudioType)num; if ((int)val == 0) { throw new IOException("Unsupported upcoming format"); } preloadRequest = UnityWebRequestMultimedia.GetAudioClip(uri.AbsoluteUri, val); ((DownloadHandlerAudioClip)preloadRequest.downloadHandler).streamAudio = false; preloadRequest.SendWebRequest(); break; } } if (preloadRequest != null && preloadRequest.isDone) { if (preloadRequest.isNetworkError || preloadRequest.isHttpError) { throw new IOException("Upcoming decode failed"); } preloadClip = DownloadHandlerAudioClip.GetContent(preloadRequest); preloadRequest.Dispose(); preloadRequest = null; ValidatePreloadClip(); } if (preloadTask != null && preloadTask.IsCompleted) { if (preloadTask.IsCanceled || preloadTask.IsFaulted) { throw new IOException("Upcoming MP3 decode failed"); } DecodedMp3 decoded = preloadTask.Result.Decoded; preloadTask = null; preloadCancellation.Dispose(); preloadCancellation = null; preloadClip = AudioClip.Create("Sailwind Radio next MP3", decoded.SampleCount / decoded.Channels, decoded.Channels, decoded.SampleRate, false); if ((Object)(object)preloadClip == (Object)null) { throw new IOException("Upcoming clip allocation failed"); } int num2 = 0; foreach (float[] block in decoded.Blocks) { if (!preloadClip.SetData(block, num2)) { throw new IOException("Upcoming clip upload failed"); } num2 += block.Length / decoded.Channels; } ValidatePreloadClip(); } if ((Object)(object)preloadClip != (Object)null && (int)preloadClip.loadState == 3) { throw new IOException("Upcoming audio data failed"); } if (((Object)(object)preloadClip == (Object)null || (int)preloadClip.loadState != 2) && (double)Time.realtimeSinceStartup - preloadStartedAt >= 60.0) { throw new IOException("Upcoming load timed out"); } } catch { string text2 = preloadPath; ReleasePreload(); preloadPath = text2; preloadFailed = true; } } private void ValidatePreloadClip() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 if ((Object)(object)preloadClip == (Object)null || preloadClip.samples <= 0 || preloadClip.frequency <= 0 || (int)preloadClip.loadState == 3 || (long)preloadClip.samples * (long)preloadClip.channels > 67108864) { throw new IOException("Upcoming audio exceeds the decoded limit or is invalid"); } } private bool AdoptPreload(string path) { if (!preloadStarted || preloadFailed || !string.Equals(path, preloadPath, StringComparison.OrdinalIgnoreCase)) { return false; } requestedPath = path; TrackLabel = SafeLabel(path); clip = preloadClip; request = preloadRequest; mp3Task = preloadTask; mp3Cancellation = preloadCancellation; loadStartedAt = preloadStartedAt; if ((Object)(object)source != (Object)null) { source.clip = clip; } preloadClip = null; preloadRequest = null; preloadTask = null; preloadCancellation = null; preloadPath = null; preloadStarted = (preloadFailed = false); return true; } private void ReleasePreload() { if (preloadCancellation != null) { Task<Mp3LoadResult> task = preloadTask; CancellationTokenSource cancellation = preloadCancellation; cancellation.Cancel(); if (task == null) { cancellation.Dispose(); } else { task.ContinueWith(delegate(Task<Mp3LoadResult> completed) { if (completed.IsFaulted) { _ = completed.Exception; } cancellation.Dispose(); }, TaskScheduler.Default); } } try { DiscardRequest(ref preloadRequest, preloadClip); } finally { if ((Object)(object)preloadClip != (Object)null) { Object.Destroy((Object)(object)preloadClip); } preloadPath = null; preloadClip = null; preloadTask = null; preloadCancellation = null; preloadStarted = (preloadFailed = false); } } private static void DiscardRequest(ref UnityWebRequest owned, AudioClip retained) { UnityWebRequest val = owned; owned = null; if (val == null) { return; } try { if (!val.isDone) { val.Abort(); } else if (!val.isNetworkError && !val.isHttpError) { AudioClip content = DownloadHandlerAudioClip.GetContent(val); if ((Object)(object)content != (Object)null && (Object)(object)content != (Object)(object)retained) { Object.Destroy((Object)(object)content); } } } catch { } finally { val.Dispose(); } } } public sealed class RadioAudioLifetime : MonoBehaviour { internal MonoBehaviour Host; internal RadioPlayback Playback; private void Update() { if ((Object)(object)Host == (Object)null && Playback != null) { Playback.Dispose(); } } private void OnDestroy() { if ((Object)(object)Host == (Object)null && Playback != null) { Playback.Dispose(); } } } internal sealed class RadioSpeakerProfile { internal readonly float Gain; internal readonly float Radius; internal readonly float HighPass; internal readonly float LowPass; internal readonly bool IsWoofer; internal static readonly RadioSpeakerProfile BuiltIn = new RadioSpeakerProfile(0.25f, 12f, 900f, 5000f); internal static readonly RadioSpeakerProfile Small = new RadioSpeakerProfile(0.35f, 15f, 600f, 9000f); internal static readonly RadioSpeakerProfile Normal = new RadioSpeakerProfile(0.65f, 20f, 160f, 18000f); internal static readonly RadioSpeakerProfile TurboWoofer = new RadioSpeakerProfile(1f, 20f, 20f, 140f, woofer: true); private RadioSpeakerProfile(float gain, float radius, float highPass, float lowPass, bool woofer = false) { Gain = gain; Radius = radius; HighPass = highPass; LowPass = lowPass; IsWoofer = woofer; } internal static RadioSpeakerProfile ForKind(int kind) { return kind switch { 3 => TurboWoofer, 2 => Normal, 1 => Small, _ => null, }; } } internal sealed class RadioSpeakerOutput : IDisposable { internal readonly GameObject Emitter; internal readonly AudioSource Source; internal AudioClip StreamClip; internal RadioSpeakerProfile Profile; internal Vector3 Position; internal bool Carried; internal bool Seen; internal bool Running; internal bool Paused; internal double SettlesAt; internal double StartsAt; internal int DriftObservations; internal float LocalVolume = 1f; internal float Bass = 1f; internal float Obstruction; private AudioHighPassFilter highPass; private AudioLowPassFilter lowPass; private RadioBassFilter bassFilter; private float smoothedObstruction; private bool initialized; internal RadioSpeakerOutput(string name, RadioSpeakerProfile profile, Action<string> warning) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown Profile = profile; Emitter = new GameObject(name); Object.DontDestroyOnLoad((Object)(object)Emitter); Source = Emitter.AddComponent<AudioSource>(); Source.playOnAwake = false; Source.loop = true; Source.spatialBlend = 1f; AnimationCurve val = AnimationCurve.Constant(0f, 1f, 1f); val.preWrapMode = (WrapMode)8; val.postWrapMode = (WrapMode)8; Source.SetCustomCurve((AudioSourceCurveType)0, val); Source.rolloffMode = (AudioRolloffMode)2; Source.minDistance = 1f; Source.maxDistance = profile.Radius; Source.volume = 0f; Source.pitch = 1f; Source.dopplerLevel = 0f; Source.ignoreListenerPause = false; Source.bypassReverbZones = true; try { highPass = Emitter.AddComponent<AudioHighPassFilter>(); if ((Object)(object)highPass == (Object)null) { throw new InvalidOperationException("High-pass component unavailable"); } highPass.cutoffFrequency = profile.HighPass; highPass.highpassResonanceQ = 1f; } catch (Exception ex) { if ((Object)(object)highPass != (Object)null) { Object.Destroy((Object)(object)highPass); } highPass = null; warning("Radio high-pass filter unavailable: " + ex.Message); } try { lowPass = Emitter.AddComponent<AudioLowPassFilter>(); if ((Object)(object)lowPass == (Object)null) { throw new InvalidOperationException("Low-pass component unavailable"); } lowPass.cutoffFrequency = profile.LowPass; lowPass.lowpassResonanceQ = 1f; } catch (Exception ex2) { if ((Object)(object)lowPass != (Object)null) { Object.Destroy((Object)(object)lowPass); } lowPass = null; warning("Radio low-pass filter unavailable: " + ex2.Message); } if (!profile.IsWoofer) { return; } try { bassFilter = Emitter.AddComponent<RadioBassFilter>(); if ((Object)(object)bassFilter == (Object)null) { throw new InvalidOperationException("Bass component unavailable"); } } catch (Exception ex3) { if ((Object)(object)bassFilter != (Object)null) { Object.Destroy((Object)(object)bassFilter); } bassFilter = null; warning("Radio bass filter unavailable: " + ex3.Message); } } internal void Update(float masterVolume, Vector3 listener, bool listenerKnown) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Emitter == (Object)null) && !((Object)(object)Source == (Object)null)) { Emitter.transform.position = Position; Source.spatialBlend = (Carried ? 0f : 1f); Source.maxDistance = Profile.Radius; float num = (Carried ? 0f : Clamp(Obstruction)); if (!initialized) { smoothedObstruction = num; initialized = true; } float num2 = Time.unscaledDeltaTime; if (!Finite(num2) || num2 < 0f) { num2 = 0f; } smoothedObstruction += (num - smoothedObstruction) * (float)(1.0 - Math.Exp((double)(0f - num2) / 0.25)); float num3 = (Carried ? 0f : smoothedObstruction); if ((Object)(object)highPass != (Object)null) { highPass.cutoffFrequency = Profile.HighPass; } float num4 = Math.Min(1500f, Profile.LowPass); if ((Object)(object)lowPass != (Object)null) { lowPass.cutoffFrequency = Profile.LowPass + (num4 - Profile.LowPass) * num3; } float num5 = ((!listenerKnown) ? 0f : (Carried ? 1f : DistanceGain(Vector3.Distance(Position, listener), Profile.Radius))); float num6 = (Profile.IsWoofer ? 1f : Clamp(LocalVolume)); float num7 = (Profile.IsWoofer ? ((float)Math.Sqrt(Clamp(Bass))) : 1f); Source.volume = Profile.Gain * masterVolume * masterVolume * num6 * num6 * num7 * num5 * (1f - (Profile.IsWoofer ? 0.4f : 0.65f) * num3); if ((Object)(object)bassFilter != (Object)null) { bassFilter.SetProfile(AudioSettings.outputSampleRate, Profile.IsWoofer); } } } internal void Stop(bool clearClip = false) { Running = (Paused = false); if (!((Object)(object)Source == (Object)null)) { Source.Stop(); if (clearClip) { Source.clip = null; } } } internal void Pause() { if (Running && !((Object)(object)Source == (Object)null)) { Source.Pause(); Running = false; Paused = true; } } internal void Resume(int sample, double now) { if (!((Object)(object)Source == (Object)null)) { Source.timeSamples = sample; Source.UnPause(); Running = true; Paused = false; SettlesAt = now + 0.3; StartsAt = now; } } internal static float DistanceGain(float distance, float radius) { if (!Finite(distance)) { return 0f; } float num = 1f - Mathf.Clamp01((distance - 1f) / (radius - 1f)); return num * num; } internal static float Clamp(float value) { if (!float.IsNaN(value)) { return Mathf.Clamp01(value); } return 0f; } internal static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } public void Dispose() { try { Stop(clearClip: true); } finally { ReleaseStreamClip(); if ((Object)(object)Emitter != (Object)null) { Object.Destroy((Object)(object)Emitter); } highPass = null; lowPass = null; bassFilter = null; } } internal void ReleaseStreamClip() { if ((Object)(object)StreamClip != (Object)null) { Object.Destroy((Object)(object)StreamClip); } StreamClip = null; } } internal sealed class StreamedMp3 : IDisposable { private sealed class Chunk { internal int State; internal int Start; internal int Count; internal readonly float[] Samples; internal Chunk(int channels) { Samples = new float[4096 * channels]; } } internal sealed class Reader { private readonly StreamedMp3 owner; private int cursor; private int generation; internal int Cursor => Volatile.Read(in cursor); internal int Generation => Volatile.Read(in generation); internal Reader(StreamedMp3 owner) { this.owner = owner; } internal void SetPosition(int position) { position = Math.Max(0, Math.Min(owner.Frames - 1, position)); int num = Volatile.Read(in cursor); Interlocked.Increment(ref generation); Volatile.Write(ref cursor, position); if (position != num) { owner.Request(position); } } internal void Read(float[] data) { Array.Clear(data, 0, data.Length); if (!owner.disposed) { int observedGeneration = Volatile.Read(in generation); int num = Volatile.Read(in cursor); int num2 = data.Length / owner.Channels; owner.Copy(num, data, num2); int advanced = ((num > owner.Frames - num2) ? owner.Frames : (num + num2)); CommitRead(observedGeneration, num, advanced); } } internal void CommitRead(int observedGeneration, int start, int advanced) { if (Volatile.Read(in generation) == observedGeneration) { Interlocked.CompareExchange(ref cursor, advanced, start); } } } private const int ChunkFrames = 4096; private const int WindowSeconds = 20; private const int AheadSeconds = 10; private const int SeekPrerollSeconds = 5; private MpegFile decoder; private Stream input; private readonly string path; private readonly CancellationTokenSource cancellation; private readonly Chunk[] chunks; private readonly Task worker; private long requestedFrame; private Exception fault; private volatile bool disposed; internal readonly int Channels; internal readonly int SampleRate; internal readonly int Frames; internal int BufferSamples => chunks.Length * 4096 * Channels; internal bool WorkerCompleted => worker.IsCompleted; internal Exception Fault => Volatile.Read(in fault); internal static StreamedMp3 Open(string path, int initialFrame, CancellationToken token) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown token.ThrowIfCancellationRequested(); CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(new CancellationToken[1] { token }); RadioPlayback.CancellableFileStream cancellableFileStream = null; try { cancellableFileStream = new RadioPlayback.CancellableFileStream(path, cancellationTokenSource.Token); if (cancellableFileStream.Length > 1073741824) { throw new IOException("MP3 exceeds the streamed file size limit"); } MpegFile val = new MpegFile((Stream)cancellableFileStream); try { int channels = val.Channels; int sampleRate = val.SampleRate; long length = val.Length; if (!val.CanSeek || channels < 1 || channels > 2 || sampleRate < 8000 || sampleRate > 96000 || length <= 0 || length % (channels * 4) != 0L) { throw new IOException("MP3 cannot be streamed with a known seekable duration"); } long num = length / (channels * 4); if (num > int.MaxValue - (long)sampleRate * 2L) { throw new IOException("MP3 duration exceeds Unity's sample-position limit"); } return new StreamedMp3(path, cancellableFileStream, val, cancellationTokenSource, channels, sampleRate, (int)num, initialFrame); } catch { val.Dispose(); throw; } } catch { cancellableFileStream?.Dispose(); cancellationTokenSource.Dispose(); throw; } } private StreamedMp3(string path, Stream input, MpegFile decoder, CancellationTokenSource cancellation, int channels, int rate, int frames, int initialFrame) { this.path = path; this.input = input; this.decoder = decoder; this.cancellation = cancellation; Channels = channels; SampleRate = rate; Frames = frames; chunks = new Chunk[Math.Max(2, (20 * rate + 4096 - 1) / 4096)]; for (int i = 0; i < chunks.Length; i++) { chunks[i] = new Chunk(channels); } requestedFrame = Math.Max(0, Math.Min(frames - 1, initialFrame)); worker = Task.Run((Action)Run); } internal Reader CreateReader() { return new Reader(this); } internal AudioClip CreateClip(Reader reader) { //IL_0034: 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_004a: Expected O, but got Unknown //IL_004a: Expected O, but got Unknown if (disposed) { throw new ObjectDisposedException("StreamedMp3"); } return AudioClip.Create("Sailwind Radio streamed MP3", Frames, Channels, SampleRate, true, new PCMReaderCallback(reader.Read), new PCMSetPositionCallback(reader.SetPosition)); } internal void Request(int frame) { Interlocked.Exchange(ref requestedFrame, Math.Max(0, Math.Min(Frames - 1, frame))); } internal bool Ready(int frame) { if (disposed) { return false; } frame = Math.Max(0, Math.Min(Frames - 1, frame)); int num = (int)Math.Min(Frames, (long)frame + (long)Math.Max(1, SampleRate / 2)); for (int num2 = frame; num2 < num; num2 = (num2 / 4096 + 1) * 4096) { Chunk chunk = chunks[num2 / 4096 % chunks.Length]; if (Volatile.Read(in chunk.State) < 0 || num2 < chunk.Start || num2 >= chunk.Start + chunk.Count) { return false; } } return true; } private void Copy(int frame, float[] destination, int frames) { if (frame < 0 || frame >= Frames) { return; } int i = Math.Max(0, frame); int num3; for (int num = (int)Math.Min(Frames, (long)Math.Max(0, frame) + (long)frames); i < num; i += num3) { int num2 = i / 4096 % chunks.Length; Chunk chunk = chunks[num2]; num3 = Math.Min(num - i, 4096 - i % 4096); int num4; do { num4 = Volatile.Read(in chunk.State); } while (num4 >= 0 && Interlocked.CompareExchange(ref chunk.State, num4 + 1, num4) != num4); if (num4 < 0) { continue; } try { if (i >= chunk.Start && i + num3 <= chunk.Start + chunk.Count) { Array.Copy(chunk.Samples, (i - chunk.Start) * Channels, destination, (i - frame) * Channels, num3 * Channels); } } finally { Interlocked.Decrement(ref chunk.State); } } } private void Run() { int num = -1; try { while (!cancellation.IsCancellationRequested) { int num2 = (int)Interlocked.Read(in requestedFrame); int num3 = (int)Math.Min(Frames, (long)num2 + (long)(10 * SampleRate)); if (num < num2 - SampleRate * 2 || num > num3 || num < 0 || (num >= num3 && !Ready(num2))) { bool num4 = num >= 0; num = num2 / 4096 * 4096; if (num4) { Reopen(); } int num5 = Math.Max(0, num - 5 * SampleRate); if (num5 > 0) { decoder.Position = (long)num5 * (long)Channels * 4; } long num6 = decoder.Position / (Channels * 4); if (num5 > 0) { num6 += ((SampleRate >= 32000) ? 1152 : 576); } if (num6 > num) { throw new IOException("MP3 decoder seek passed the requested sample"); } float[] array = new float[4096 * Channels]; int num8; for (; num6 < num; num6 += num8 / Channels) { cancellation.Token.ThrowIfCancellationRequested(); int num7 = (int)Math.Min(num - num6, 4096L) * Channels; num8 = decoder.ReadSamples(array, 0, num7); if (num8 <= 0 || num8 % Channels != 0) { throw new IOException("MP3 decoder could not finish seek"); } } } if (num >= num3 || num >= Frames) { cancellation.Token.WaitHandle.WaitOne(20); continue; } Chunk chunk = chunks[num / 4096 % chunks.Length]; while (Interlocked.CompareExchange(ref chunk.State, -1, 0) != 0) { cancellation.Token.ThrowIfCancellationRequested(); cancellation.Token.WaitHandle.WaitOne(1); } int num9 = Math.Min(4096, Frames - num) * Channels; int i = 0; try { int num10; for (; i < num9; i += num10) { cancellation.Token.ThrowIfCancellationRequested(); num10 = decoder.ReadSamples(chunk.Samples, i, num9 - i); if (num10 <= 0) { break; } } if (i == 0) { throw new IOException("MP3 ended before its reported duration"); } if (i % Channels != 0) { throw new IOException("MP3 decoder returned an incomplete frame"); } for (int j = 0; j < i; j++) { if (float.IsNaN(chunk.Samples[j]) || float.IsInfinity(chunk.Samples[j])) { chunk.Samples[j] = 0f; } else { chunk.Samples[j] = Math.Max(-1f, Math.Min(1f, chunk.Samples[j])); } } chunk.Start = num; chunk.Count = i / Channels; } finally { Volatile.Write(ref chunk.State, 0); } num += i / Channels; } } catch (OperationCanceledException) { } catch (Exception value) { Volatile.Write(ref fault, value); } finally { decoder.Dispose(); input.Dispose(); } } private void Reopen() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown decoder.Dispose(); input.Dispose(); input = new RadioPlayback.CancellableFileStream(path, cancellation.Token); decoder = new MpegFile(input); } public void Dispose() { if (disposed) { return; } disposed = true; cancellation.Cancel(); worker.ContinueWith(delegate(Task done) { if (done.IsFaulted) { _ = done.Exception; } cancellation.Dispose(); }, TaskScheduler.Default); } } [BepInPlugin("local.sailwind.radio", "Sailwind Radio", "1.0.1")] [BepInProcess("Sailwind.exe")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "local.sailwind.radio"; public const string Version = "1.0.1"; private static Plugin instance; private Harmony harmony; private RadioWorldService world; private RadioAcousticsService acoustics; private RadioShopService shops; private ConfigEntry<string> musicFolders; private ConfigEntry<bool> continueWhileSleeping; private LibraryScanner library; private RadioMenus menus; private bool libraryReady; private readonly Dictionary<RadioItemController, RadioPlayback> playback = new Dictionary<RadioItemController, RadioPlayback>(); private readonly Dictionary<RadioItemController, RadioQueue> queues = new Dictionary<RadioItemController, RadioQueue>(); private readonly HashSet<RadioItemController> seen = new HashSet<RadioItemController>(); private readonly List<RadioItemController> removed = new List<RadioItemController>(); private bool ready; private bool applicationPaused; private bool focused = true; private bool Suspended { get { if (GameState.playing && !GameState.currentlyLoading && GameState.loadingScenes <= 0 && (!GameState.sleeping || continueWhileSleeping.Value) && (!(Time.timeScale <= 0f) || (GameState.sleeping && continueWhileSleeping.Value)) && !AudioListener.pause && !applicationPaused) { if (!focused) { return !Application.runInBackground; } return false; } return true; } } private void Awake() { //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Expected O, but got Unknown instance = this; try { musicFolders = ((BaseUnityPlugin)this).Config.Bind<string>("Music", "MusicFolders", "", "Music folders separated by |. Each folder is one collection including all its subfolders. Example: D:\\Music | E:\\Sailing Music"); continueWhileSleeping = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "ContinueWhileSleeping", false, "Keep music playing while the player sleeps. Loading still suspends playback."); ConfigEntry<string> val = ((BaseUnityPlugin)this).Config.Bind<string>("Development", "SpawnRadio", "", "Retired developer spawn shortcut."); ((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val).Definition); ConfigEntry<int> val2 = ((BaseUnityPlugin)this).Config.Bind<int>("Internal", "SpawnDefaultRevision", 0, "Retired spawn shortcut migration marker."); ((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val2).Definition); ConfigEntry<string> val3 = ((BaseUnityPlugin)this).Config.Bind<string>("Music", "MusicFile", "", "Retired single-file music setting."); bool num = !string.IsNullOrWhiteSpace(val3.Value) && string.IsNullOrWhiteSpace(musicFolders.Value); ((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val3).Definition); ConfigEntry<bool> val4 = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "StormInterferenceEnabled", true, "Retired interference setting."); ((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val4).Definition); ConfigEntry<float> val5 = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "StormInterferenceStrength", 0.35f, "Retired interference setting."); ((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val5).Definition); ConfigEntry<bool> val6 = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "ContinueWhilePaused", false, "Retired pause setting."); ((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val6).Definition); ((BaseUnityPlugin)this).Config.Save(); if (num) { Warn("MusicFile has been retired. Set MusicFolders to a folder containing your music."); } harmony = new Harmony("local.sailwind.radio"); acoustics = new RadioAcousticsService(); library = new LibraryScanner(); menus = new RadioMenus(); world = new RadioWorldService(harmony, Warn); world.BeforeSave += CapturePositions; world.ActionRequested += HandleAction; shops = new RadioShopService(world, Warn); menus.CollectionsChanged += SelectCollections; library.RequestScan(musicFolders.Value); InstallSuspendBoundary(typeof(StartMenu), "GameToSettings"); InstallSuspendBoundary(typeof(Sleep), "FallAsleep"); ready = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Sailwind Radio 1.0.1 ready. Configure MusicFolders to load music."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Radio initialization failed: " + ex)); Shutdown(); ((Behaviour)this).enabled = false; } } private void InstallSuspendBoundary(Type type, string name) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_0087: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.DeclaredMethod(type, name, Type.EmptyTypes, (Type[])null); if (methodInfo == null) { Warn("Immediate radio pause hook unavailable for " + type.Name + "." + name + ". Game state pause checks remain active."); } else { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "ReleaseControlBoundary", (Type[])null), new HarmonyMethod(typeof(Plugin), "SuspendBoundary", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void ReleaseControlBoundary() { if (Object.op_Implicit((Object)(object)instance) && instance.ready) { instance.ReleaseRadioControls(); } } private static void SuspendBoundary() { if (Object.op_Implicit((Object)(object)instance) && instance.ready && instance.Suspended) { instance.SuspendNow(); } } private void Update() { if (!ready) { return; } try { acoustics.Tick(); world.Tick(); TickShops(); SynchronizePlayback(); if (library.Poll()) { libraryReady = true; foreach (string warning in library.Snapshot.Warnings) { Warn(warning); } foreach (KeyValuePair<RadioItemController, RadioQueue> queue in queues) { if (queue.Value.ApplySnapshot(library.Snapshot)) { playback[queue.Key].SetTrack(queue.Key.State.TrackPath); } } if (Object.op_Implicit((Object)(object)menus.CollectionRadio)) { ShowCollections(menus.CollectionRadio); } } menus.Tick(); bool suspended = Suspended; PlaybackOrder.Tick(playback, world.ActiveRadioId, (KeyValuePair<RadioItemController, RadioPlayback> pair) => Object.op_Implicit((Object)(object)pair.Key) ? pair.Key.InstanceId : 0, TickPlayback, suspended); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Radio stopped after a runtime error: " + ex)); Shutdown(); ((Behaviour)this).enabled = false; } } private void SynchronizePlayback() { seen.Clear(); IReadOnlyList<RadioItemController> items = world.Items; for (int i = 0; i < items.Count; i++) { RadioItemController item = items[i]; if (!Object.op_Implicit((Object)(object)item) || item.State.Kind != 0) { continue; } seen.Add(item); if (!playback.ContainsKey(item)) { RadioQueue radioQueue = new RadioQueue(item.State); if (libraryReady) { radioQueue.ApplySnapshot(library.Snapshot); } queues.Add(item, radioQueue); RadioPlayback engine = new RadioPlayback((MonoBehaviour)(object)this, item.State, Warn) { RepeatTrack = false }; playback.Add(item, engine); item.CapturePosition = engine.CapturePosition; item.SuspendPlayback = delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) engine.Tick(item.VisualAudioPosition, suspended: true); }; } } removed.Clear(); foreach (KeyValuePair<RadioItemController, RadioPlayback> item2 in playback) { if (!Object.op_Implicit((Object)(object)item2.Key) || !seen.Contains(item2.Key)) { item2.Value.Dispose(); if (Object.op_Implicit((Object)(object)item2.Key)) { item2.Key.CapturePosition = null; item2.Key.SuspendPlayback = null; } removed.Add(item2.Key); } } foreach (RadioItemController item3 in removed) { playback.Remove(item3); queues.Remove(item3); } } private void TickShops() { if (shops == null) { return; } try { shops.Tick(); } catch (Exception ex) { Warn("Radio shops stopped after an error: " + ex.Message); RadioShopService radioShopService = shops; shops = null; try { radioShopService.Dispose(); } catch (Exception ex2) { Warn("Could not release radio shops: " + ex2.Message); } } } private void TickPlayback(KeyValuePair<RadioItemController, RadioPlayback> pair, bool suspended) { //IL_0016: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)pair.Key)) { return; } Vector3 visualAudioPosition = pair.Key.VisualAudioPosition; Vector3 val = pair.Key.SoundPosition; bool usesPlayerPosition = pair.Key.UsesPlayerPosition; if (usesPlayerPosition && acoustics.ListenerKnown) { val = acoustics.ListenerPosition; } float obstruction = (pair.Key.State.Powered ? acoustics.ObstructionAt(val, usesPlayerPosition) : 0f); pair.Value.SetAcoustics(acoustics.ListenerPosition, acoustics.ListenerKnown, obstruction); pair.Value.SetCarried(usesPlayerPosition); pair.Value.BeginEndpoints(); if (pair.Key.InstanceId == world.ActiveRadioId && pair.Key.State.Powered) { DeviceVessel vessel = pair.Key.Vessel; foreach (RadioItemController item in world.Items) { if (Object.op_Implicit((Object)(object)item) && item.State.Kind != 0 && item.State.SpeakerEnabled) { DeviceVessel vessel2 = item.Vessel; bool usesPlayerPosition2 = item.UsesPlayerPosition; Vector3 visualAudioPosition2 = item.VisualAudioPosition; if (DeviceVessels.CanConnect(vessel, vessel2, visualAudioPosition, visualAudioPosition2)) { Vector3 val2 = ((usesPlayerPosition2 && acoustics.ListenerKnown) ? acoustics.ListenerPosition : item.SoundPosition); pair.Value.SetEndpoint(item.InstanceId, val2, usesPlayerPosition2, item.State.Kind, (item.State.Kind == 3) ? 1f : item.State.Volume, item.State.Bass, acoustics.ObstructionAt(val2, usesPlayerPosition2)); } } } } pair.Value.EndEndpoints(); pair.Value.Tick(val, suspended); if (!suspended && pair.Key.State.Powered && !pair.Key.State.Paused) { RadioQueue radioQueue = queues[pair.Key]; bool num; if (!pair.Value.LoadFailed) { if (!pair.Value.TrackEnded) { goto IL_02c8; } num = radioQueue.TrackEnded(); } else { num = radioQueue.TrackFailed(pair.Value.FailedPath); } if (num) { pair.Value.SetTrack(pair.Key.State.TrackPath); } } goto IL_02c8; IL_02c8: pair.Value.PreloadTrack((pair.Key.InstanceId == world.ActiveRadioId && pair.Key.State.Powered) ? queues[pair.Key].PeekNext() : ""); TrackInfo trackInfo = library.Snapshot.GetTrackInfo(pair.Key.State.TrackPath); pair.Key.SetTrackInfo(trackInfo.Title, trackInfo.Artist, trackInfo.Album); } private void HandleAction(RadioItemController item, RadioAction action) { if (!ready || !Object.op_Implicit((Object)(object)item) || item.State.Kind != 0) { return; } if (action == RadioAction.Power) { if (item.State.Powered) { library.RequestScan(musicFolders.Value); } } else { if (!playback.TryGetValue(item, out var value) || !queues.TryGetValue(item, out var value2)) { return; } bool flag = false; switch (action) { case RadioAction.PlayPause: if (!item.State.Powered) { item.TogglePower(); break; } value.CapturePosition(); item.State.Paused = !item.State.Paused; if (item.State.Paused) { item.SuspendPlayback?.Invoke(); } break; case RadioAction.Previous: flag = value2.Previous(); break; case RadioAction.Next: flag = value2.Next(); break; case RadioAction.Shuffle: flag = value2.SetShuffle(!item.State.Shuffle); break; case RadioAction.Collections: ShowCollections(item); break; } if (flag) { value.SetTrack(item.State.TrackPath); } } } private void ShowCollections(RadioItemController item) { if (!Object.op_Implicit((Object)(object)item)) { return; } List<CollectionChoice> list = new List<CollectionChoice>(); HashSet<string> hashSet = new HashSet<string>(item.State.SelectedCollections ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase); foreach (MusicCollection collection in library.Snapshot.Collections) { list.Add(new CollectionChoice(collection.Id, collection.Label, hashSet.Contains(collection.Id))); } menus.ShowCollections(item, list); } private void SelectCollections(RadioItemController item, string[] selected) { if (Object.op_Implicit((Object)(object)item) && queues.TryGetValue(item, out var value) && value.SetCollections(library.Snapshot, selected)) { playback[item].SetTrack(item.State.TrackPath); } } private void OnGUI() { if (ready) { menus?.Draw(); } } private void CapturePositions() { foreach (RadioPlayback value in playback.Values) { try { value.CapturePosition(); } catch (Exception ex) { Warn("Could not capture a radio position: " + ex.Message); } } } private void SuspendNow() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<RadioItemController, RadioPlayback> item in playback) { if (Object.op_Implicit((Object)(object)item.Key)) { try { item.Value.Tick(item.Key.VisualAudioPosition, suspended: true); } catch (Exception ex) { Warn("Could not suspend a radio: " + ex.Message); } } } } private void ReleaseRadioControls() { menus?.Close(); foreach (RadioItemController item in world?.Items ?? Array.Empty<RadioItemController>()) { if (Object.op_Implicit((Object)(object)item)) { try { item.ReleaseControls(); } catch (Exception ex) { Warn("Could not release radio controls: " + ex.Message); } } } } private void OnApplicationPause(bool pause) { applicationPaused = pause; if (pause) { ReleaseRadioControls(); } if (pause && ready) { SuspendNow(); } } private void OnApplicationFocus(bool focus) { focused = focus; acoustics?.Invalidate(); if (!focus) { ReleaseRadioControls(); } if (!focus && !Application.runInBackground && ready) { SuspendNow(); } } private void Warn(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); } private void OnDisable() { Shutdown(); } private void OnDestroy() { Shutdown(); } private void Shutdown() { ready = false; ReleaseRadioControls(); CapturePositions(); foreach (KeyValuePair<RadioItemController, RadioPlayback> item in playback) { if (Object.op_Implicit((Object)(object)item.Key)) { item.Key.CapturePosition = null; item.Key.SuspendPlayback = null; } try { item.Value.Dispose(); } catch (Exception ex) { Warn("Could not release a radio player: " + ex.Message); } } playback.Clear(); queues.Clear(); library?.Dispose(); library = null; menus?.Dispose(); menus = null; try { shops?.Dispose(); } catch (Exception ex2) { Warn("Could not release radio shops: " + ex2.Message); } shops = null; try { world?.Dispose(); } catch (Exception ex3) { Warn("Could not release radio item hooks: " + ex3.Message); } world = null; acoustics?.Dispose(); acoustics = null; try { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } catch (Exception ex4) { Warn("Could not remove radio hooks: " + ex4.Message); } harmony = null; if ((Object)(object)instance == (Object)(object)this) { instance = null; } } } [Serializable] public sealed class RadioState { public int Kind; public float Bass = 0.5f; public float LocalVolume = 1f; public bool SpeakerEnabled = true; public bool Shuffle; public bool CollectionsInitialized; public string[] SelectedCollections = Array.Empty<string>(); public string TrackPath = ""; public double PositionSeconds; public bool Powered; public bool Paused; public float Volume = 0.5f; } } namespace SailwindRadio.UI { internal sealed class MenuInputLease { private Object character; private Object controller; private Object crosshair; private bool crosshairActive; private bool mouseLook; private bool cursorVisible; private CursorLockMode cursorLock; internal bool Owned { get; private set; } internal static bool GameplayAvailable { get { if (GameState.playing && !GameState.currentlyLoading && GameState.loadingScenes == 0 && !GameState.justStarted && !GameState.sleeping && !Object.op_Implicit((Object)(object)GameState.inBed) && !BoatCamera.on) { return Time.timeScale > 0f; } return false; } } internal bool Acquire() { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) if (Owned) { return true; } if (!GameplayAvailable || !Application.isFocused || GameState.inCursorMenu || !Object.op_Implicit((Object)(object)Refs.charController) || !Object.op_Implicit((Object)(object)Refs.ovrController) || !Object.op_Implicit((Object)(object)Refs.mouseCrosshair) || !((Collider)Refs.charController).enabled || !((Behaviour)Refs.ovrController).enabled) { return false; } character = (Object)(object)Refs.charController; controller = (Object)(object)Refs.ovrController; crosshair = (Object)(object)Refs.mouseCrosshair; crosshairActive = Refs.mouseCrosshair.activeSelf; mouseLook = MouseLook.MouseLookIsEnabled(); cursorVisible = Cursor.visible; cursorLock = Cursor.lockState; Owned = true; MouseLook.ToggleMouseLook(false); MouseLook.ToggleMouseLookAndCursor(false); ((Collider)Refs.charController).enabled = false; ((Behaviour)Refs.ovrController).enabled = false; Refs.mouseCrosshair.SetActive(false); return true; } internal void Release() { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) if (!Owned) { return; } Owned = false; if (!GameState.currentlyLoading && GameState.playing && !GameState.sleeping && !Object.op_Implicit((Object)(object)GameState.inBed)) { if (Object.op_Implicit((Object)(object)Refs.charController) && (Object)(object)Refs.charController == character) { ((Collider)Refs.charController).enabled = true; } if (Object.op_Implicit((Object)(object)Refs.ovrController) && (Object)(object)Refs.ovrController == controller) { ((Behaviour)Refs.ovrController).enabled = true; } if ((Object)(object)Refs.charController == character && (Object)(object)Refs.ovrController == controller) { MouseLook.ToggleMouseLook(mouseLook); MouseLook.ToggleMouseLookAndCursor(true); Cursor.visible = cursorVisible; Cursor.lockState = cursorLock; if (!BoatCamera.on && Object.op_Implicit((Object)(object)Refs.mouseCrosshair) && (Object)(object)Refs.mouseCrosshair == crosshair) { Refs.mouseCrosshair.SetActive(crosshairActive); } } } character = (controller = (crosshair = null)); } } public sealed class CollectionChoice { public readonly string Id; public readonly string Label; public readonly bool Selected; public CollectionChoice(string id, string label, bool selected) { Id = id; Label = label; Selected = selected; } } public sealed class RadioMenus : IDisposable { private readonly MenuInputLease lease = new MenuInputLease(); private readonly List<CollectionChoice> choices = new List<CollectionChoice>(); private readonly Dictionary<string, bool> selected = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private Vector2 scroll; private bool disposed; public bool IsOpen => lease.Owned; public RadioItemController CollectionRadio { get; private set; } public event Action<RadioItemController, string[]> CollectionsChanged; public void ShowCollections(RadioItemController radio, IReadOnlyList<CollectionChoice> available) { //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) if (disposed || !Object.op_Implicit((Object)(object)radio) || radio.State.Kind != 0) { return; } if (!IsOpen || !((Object)(object)CollectionRadio == (Object)(object)radio)) { Close(); radio.ReleaseControls(); if (!lease.Acquire()) { return; } selected.Clear(); scroll = Vector2.zero; } CollectionRadio = radio; choices.Clear(); if (available == null) { return; } foreach (CollectionChoice item in available) { if (item != null && !string.IsNullOrEmpty(item.Id)) { choices.Add(item); if (!selected.ContainsKey(item.Id)) { selected.Add(item.Id, item.Selected); } } } } public void Tick() { if (IsOpen) { if (!MenuInputLease.GameplayAvailable || !Application.isFocused || !GameState.inCursorMenu || Input.GetKeyDown((KeyCode)27)) { Close(); } else if (Object.op_Implicit((Object)(object)CollectionRadio) && (!CollectionRadio.IsPlacedForControls || !CollectionRadio.WithinMenuReach)) { Close(); } else if (!Object.op_Implicit((Object)(object)CollectionRadio)) { Close(); } } } public void Draw() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { float num = Mathf.Min(440, Screen.width - 24); float num2 = Mathf.Min(430, Screen.height - 24); GUILayout.Window(194043, new Rect(((float)Screen.width - num) * 0.5f, ((float)Screen.height - num2) * 0.5f, num, num2), new WindowFunction(DrawWindow), "Radio collections", Array.Empty<GUILayoutOption>()); } } private void DrawWindow(int id) { //IL_0021: 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_004f: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)CollectionRadio)) { GUILayout.Label("Choose which music collections this radio plays", Array.Empty<GUILayoutOption>()); scroll = GUILayout.BeginScrollView(scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height((float)Mathf.Min(280, Screen.height - 160)) }); if (choices.Count == 0) { GUILayout.Label("No collections available yet", Array.Empty<GUILayoutOption>()); } foreach (CollectionChoice choice in choices) { selected[choice.Id] = GUILayout.Toggle(selected[choice.Id], choice.Label ?? choice.Id, Array.Empty<GUILayoutOption>()); } GUILayout.EndScrollView(); if (GUILayout.Button("Apply", Array.Empty<GUILayoutOption>())) { RadioItemController collectionRadio = CollectionRadio; List<string> list = new List<string>(); foreach (CollectionChoice choice2 in choices) { if (selected[choice2.Id] && !list.Contains(choice2.Id)) { list.Add(choice2.Id); } } Close(); this.CollectionsChanged?.Invoke(collectionRadio, list.ToArray()); } } if (GUILayout.Button("Close", Array.Empty<GUILayoutOption>())) { Close(); } } public void Close() { lease.Release(); CollectionRadio = null; choices.Clear(); selected.Clear(); } public void Dispose() { if (!disposed) { Close(); disposed = true; } } } } namespace SailwindRadio.Shops { internal static class NativeShopContract { private sealed class Instruction { internal OpCode Code; internal MemberInfo Member; } internal static bool KeeperReady(ShopArea area) { Shopkeeper shopkeeper = area.GetShopkeeper(); if (!Object.op_Implicit((Object)(object)shopkeeper)) { return false; } FieldInfo? field = typeof(Shopkeeper).GetField("parentRegion", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = typeof(Shopkeeper).GetField("shop", BindingFlags.Instance | BindingFlags.NonPublic); if (field?.GetValue(shopkeeper) is Region) { return field2?.GetValue(shopkeeper) == area; } return false; } internal static bool Supports(MethodInfo sale, MethodInfo sell) { try { List<Instruction> list = Read(sell); int num = list.FindIndex((Instruction i) => i.Code == OpCodes.Stfld && i.Member?.Name == "sold" && i.Member.DeclaringType == typeof(ShipItem)); int num2 = list.FindIndex((Instruction i) => i.Member is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(SaveablePrefab) && methodInfo.Name == "RegisterToSave"); List<Instruction> list2 = Read(sale); int num3 = list2.FindIndex((Instruction i) => object.Equals(i.Member, sell)); int num4 = list2.FindIndex((Instruction i) => i.Member is FieldInfo fieldInfo && fieldInfo.DeclaringType == typeof(PlayerGold) && fieldInfo.Name == "currency"); int num5 = list2.FindIndex((Instruction i) => i.Code == OpCodes.Stelem_I4 || i.Code == OpCodes.Stind_I4); return num >= 0 && num2 > num && num3 >= 0 && num4 > num3 && num5 > num4; } catch { return false; } } private static List<Instruction> Read(MethodInfo method) { byte[] array = method?.GetMethodBody()?.GetILAsByteArray(); if (array == null) { throw new InvalidOperationException("Native method body unavailable"); } Dictionary<short, OpCode> dictionary = new Dictionary<short, OpCode>(); FieldInfo[] fields = typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(OpCode)) { OpCode value = (OpCode)fieldInfo.GetValue(null); dictionary[value.Value] = value; } } List<Instruction> list = new List<Instruction>(); int num = 0; while (num < array.Length) { short num2 = array[num++]; if (num2 == 254) { num2 = (short)(0xFE00 | array[num++]); } OpCode code = dictionary[num2]; Instruction instruction = new Instruction { Code = code }; int num3; switch (code.OperandType) { case OperandType.InlineNone: num3 = 0; break; case OperandType.ShortInlineBrTarget: case OperandType.ShortInlineI: case OperandType.ShortInlineVar: num3 = 1; break; case OperandType.InlineVar: num3 = 2; break; case OperandType.InlineI8: case OperandType.InlineR: num3 = 8; break; case OperandType.InlineSwitch: num3 = 4 + 4 * BitConverter.ToInt32(array, num); break; default: num3 = 4; break; } if (code.OperandType == OperandType.InlineMethod || code.OperandType == OperandType.InlineField) { instruction.Member = method.Module.ResolveMember(BitConverter.ToInt32(array, num)); } num += num3; list.Add(instruction); } return list; } } internal static class RadioShopCatalog { internal static readonly int[] StockKinds = new int[7] { 3, 2, 2, 0, 0, 1, 1 }; internal static int BasePrice(int kind) { return kind switch { 3 => 2632, 2 => 1579, 1 => 526, 0 => 789, _ => 0, }; } } internal static class RadioShopPlacement { internal static bool TryStand(IslandSceneryScene scenery, out Vector3 position, out Quaternion rotation, out string reason) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_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_0067: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) position = default(Vector3); rotation = Quaternion.identity; reason = "not an authored capital scene"; if (!Object.op_Implicit((Object)(object)scenery) || !RadioStandLayout.TryAnchor(scenery.parentIslandIndex, out var sceneryLocalPosition, out var yaw)) { return false; } Vector3 val = ((Component)scenery).transform.TransformPoint(sceneryLocalPosition); rotation = ((Component)scenery).transform.rotation * Quaternion.Euler(0f, yaw, 0f); float height; bool flag = Ground(val, out height); bool flag2 = (scenery.parentIslandIndex == 1 || scenery.parentIslandIndex == 15) && flag && height <= val.y + 0.02f && height >= val.y - 0.3f; float num = ((scenery.parentIslandIndex == 1) ? (-0.1f) : 0f); position = new Vector3(val.x, (flag2 ? height : (val.y + 0.02f)) + num, val.z); string text = (flag ? "" : "no level static ground at the authored anchor"); if (flag && !flag2 && Mathf.Abs(height - val.y) > 0.15f) { text = Append(text, "nearby support height differs from authored height by " + (height - val.y)); } bool flag3 = false; float[] array = new float[4] { -1.1f, 1.1f, 1.39f, 2.21f }; foreach (float num2 in array) { float[] array2 = new float[2] { -0.39f, 0.39f }; foreach (float num3 in array2) { if (!Ground(position + rotation * new Vector3(num2, 0f, num3), out var height2) || Mathf.Abs(height2 - position.y) > 0.045f) { flag3 = true; } } } if (flag3) { text = Append(text, "uneven or missing ground beneath the display"); } Vector3 envelopeHalf = RadioStandLayout.EnvelopeHalf; if (scenery.parentIslandIndex == 1) { envelopeHalf.x += 0.15f; } string text2 = Blocker(position + rotation * RadioStandLayout.EnvelopeCenter, envelopeHalf, rotation, null); if (text2 != null) { text = Append(text, "display footprint overlaps " + text2); } text2 = Blocker(position + rotation * new Vector3(0.55f, 0.91f, -1.02f), new Vector3(1.8f, 0.85f, 0.38f), rotation, null); if (text2 != null) { text = Append(text, "customer approach overlaps " + text2); } reason = text; return true; } internal static bool SlotClear(RadioShopStand stand, int index, int kind, out string reason) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) IslandSceneryScene componentInParent = ((Component)stand).GetComponentInParent<IslandSceneryScene>(); Vector3 val = ((Component)stand).transform.TransformPoint(RadioStandLayout.Slot(Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.parentIslandIndex : (-1), index, stand.UsesNativeGoldRockCounter)); Vector3 half = RadioDevice.Size(kind) * 0.5f + new Vector3(0.02f, 0.008f, 0.025f); Quaternion val2 = ((Component)stand).transform.rotation * RadioStandLayout.SlotRotation(kind); reason = Blocker(val + val2 * RadioDevice.Center(kind), half, val2, ((Component)stand).transform); return true; } private static string Append(string previous, string message) { if (previous.Length != 0) { return previous + "; " + message; } return message; } private static bool Ground(Vector3 expected, out float height) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (mi
BepInEx/plugins/SailwindRadio/NLayer.dll
Decompiled 6 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; 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.Threading; using System.Threading.Tasks; using NLayer.Decoder; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Mark Heath, Andrew Ward")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Fully Managed MPEG 1 & 2 Decoder for Layers 1, 2, & 3")] [assembly: AssemblyFileVersion("1.16.0.0")] [assembly: AssemblyInformationalVersion("1.16.0+c88b0d8abfa15c1e506106931ea8410170dc6326")] [assembly: AssemblyProduct("NLayer")] [assembly: AssemblyTitle("NLayer")] [assembly: AssemblyVersion("1.16.0.0")] namespace NLayer { public enum MpegVersion { Unknown = 0, Version1 = 10, Version2 = 20, Version25 = 25 } public enum MpegLayer { Unknown, LayerI, LayerII, LayerIII } public enum MpegChannelMode { Stereo, JointStereo, DualChannel, Mono } public enum StereoMode { Both, LeftOnly, RightOnly, DownmixToMono } public interface IMpegFrame { int SampleRate { get; } int SampleRateIndex { get; } int FrameLength { get; } int BitRate { get; } MpegVersion Version { get; } MpegLayer Layer { get; } MpegChannelMode ChannelMode { get; } int ChannelModeExtension { get; } int SampleCount { get; } int BitRateIndex { get; } bool IsCopyrighted { get; } bool HasCrc { get; } bool IsCorrupted { get; } void Reset(); int ReadBits(int bitCount); } public class MpegFile : IDisposable { private Stream _stream; private bool _closeStream; private bool _eofFound; private MpegStreamReader _reader; private MpegFrameDecoder _decoder; private object _seekLock = new object(); private long _position; private float[] _readBuf = new float[2304]; private int _readBufLen; private int _readBufOfs; public int SampleRate => _reader.SampleRate; public int Channels => _reader.Channels; public bool CanSeek => _reader.CanSeek; public long Length => _reader.SampleCount * _reader.Channels * 4; public TimeSpan Duration { get { long sampleCount = _reader.SampleCount; if (sampleCount == -1) { return TimeSpan.Zero; } return TimeSpan.FromSeconds((double)sampleCount / (double)_reader.SampleRate); } } public long Position { get { return _position; } set { if (!_reader.CanSeek) { throw new InvalidOperationException("Cannot Seek!"); } if (value < 0) { throw new ArgumentOutOfRangeException("value"); } long num = value / 4 / _reader.Channels; int num2 = 0; if (num >= _reader.FirstFrameSampleCount) { num2 = _reader.FirstFrameSampleCount; num -= num2; } lock (_seekLock) { long num3 = _reader.SeekTo(num); if (num3 == -1) { throw new ArgumentOutOfRangeException("value"); } _decoder.Reset(); if (num2 != 0) { _decoder.DecodeFrame(_reader.NextFrame(), _readBuf, 0); num3 += num2; } _position = num3 * 4 * _reader.Channels; _eofFound = false; _readBufOfs = (_readBufLen = 0); } } } public TimeSpan Time { get { return TimeSpan.FromSeconds((double)_position / 4.0 / (double)_reader.Channels / (double)_reader.SampleRate); } set { Position = (long)(value.TotalSeconds * (double)_reader.SampleRate * (double)_reader.Channels * 4.0); } } public StereoMode StereoMode { get { return _decoder.StereoMode; } set { _decoder.StereoMode = value; } } public MpegFile(string fileName) { Init(File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read), closeStream: true); } public MpegFile(Stream stream) { Init(stream, closeStream: false); } private void Init(Stream stream, bool closeStream) { _stream = stream; _closeStream = closeStream; _reader = new MpegStreamReader(_stream); _decoder = new MpegFrameDecoder(); } public void Dispose() { if (_closeStream) { _stream.Dispose(); _closeStream = false; } } public void SetEQ(float[] eq) { _decoder.SetEQ(eq); } public int ReadSamples(byte[] buffer, int index, int count) { if (index < 0 || index + count > buffer.Length) { throw new ArgumentOutOfRangeException("index"); } count -= count % 4; return ReadSamplesImpl(buffer, index, count, 32); } public int ReadSamples(float[] buffer, int index, int count) { if (index < 0 || index + count > buffer.Length) { throw new ArgumentOutOfRangeException("index"); } return ReadSamplesImpl(buffer, index * 4, count * 4, 32) / 4; } public int ReadSamplesInt16(byte[] buffer, int index, int count) { if (index < 0 || index + count > buffer.Length * 2) { throw new ArgumentOutOfRangeException("index"); } return ReadSamplesImpl(buffer, index, count, 16) * 2 / 4; } public int ReadSamplesInt8(byte[] buffer, int index, int count) { if (index < 0 || index + count > buffer.Length * 4) { throw new ArgumentOutOfRangeException("index"); } return ReadSamplesImpl(buffer, index, count, 8) / 4; } private int ReadSamplesImpl(Array buffer, int index, int count, int bitDepth) { int num = 0; lock (_seekLock) { while (count > 0) { if (_readBufLen > _readBufOfs) { int num2 = _readBufLen - _readBufOfs; if (num2 > count) { num2 = count; } if (bitDepth == 32) { Buffer.BlockCopy(_readBuf, _readBufOfs, buffer, index, num2); } else { for (int i = 0; i < num2 / 4; i++) { switch (bitDepth) { case 8: buffer.SetValue((byte)Math.Round(127.5f * _readBuf[_readBufOfs / 4 + i] + 127.5f), index / 4 + i); break; case 16: { int num3 = (int)Math.Round(32767.5f * _readBuf[_readBufOfs / 4 + i] - 0.5f); if (num3 < 0) { num3 += 65536; } buffer.SetValue((byte)(num3 % 256), 2 * (index / 4 + i)); buffer.SetValue((byte)(num3 / 256), 2 * (index / 4 + i) + 1); break; } } } } num += num2; count -= num2; index += num2; _position += num2; _readBufOfs += num2; if (_readBufOfs == _readBufLen) { _readBufLen = 0; } } if (_readBufLen != 0) { continue; } if (_eofFound) { break; } MpegFrame mpegFrame = _reader.NextFrame(); if (mpegFrame == null) { _eofFound = true; break; } try { _readBufLen = _decoder.DecodeFrame(mpegFrame, _readBuf, 0) * 4; _readBufOfs = 0; } catch (InvalidDataException) { _decoder.Reset(); _readBufOfs = (_readBufLen = 0); } catch (EndOfStreamException) { _eofFound = true; break; } finally { mpegFrame.ClearBuffer(); } } } return num; } } public class MpegFrameDecoder { private LayerIDecoder _layerIDecoder; private LayerIIDecoder _layerIIDecoder; private LayerIIIDecoder _layerIIIDecoder; private float[] _eqFactors; private float[] _ch0; private float[] _ch1; public StereoMode StereoMode { get; set; } public MpegFrameDecoder() { _ch0 = new float[1152]; _ch1 = new float[1152]; } public void SetEQ(float[] eq) { if (eq != null) { float[] array = new float[32]; for (int i = 0; i < eq.Length; i++) { array[i] = (float)Math.Pow(2.0, eq[i] / 6f); } _eqFactors = array; } else { _eqFactors = null; } } public int DecodeFrame(IMpegFrame frame, byte[] dest, int destOffset) { if (frame == null) { throw new ArgumentNullException("frame"); } if (dest == null) { throw new ArgumentNullException("dest"); } if (destOffset % 4 != 0) { throw new ArgumentException("Must be an even multiple of 4", "destOffset"); } if ((dest.Length - destOffset) / 4 < ((frame.ChannelMode == MpegChannelMode.Mono) ? 1 : 2) * frame.SampleCount) { throw new ArgumentException("Buffer not large enough! Must be big enough to hold the frame's entire output. This is up to 9,216 bytes.", "dest"); } return DecodeFrameImpl(frame, dest, destOffset / 4) * 4; } public int DecodeFrame(IMpegFrame frame, float[] dest, int destOffset) { if (frame == null) { throw new ArgumentNullException("frame"); } if (dest == null) { throw new ArgumentNullException("dest"); } if (dest.Length - destOffset < ((frame.ChannelMode == MpegChannelMode.Mono) ? 1 : 2) * frame.SampleCount) { throw new ArgumentException("Buffer not large enough! Must be big enough to hold the frame's entire output. This is up to 2,304 elements.", "dest"); } return DecodeFrameImpl(frame, dest, destOffset); } private int DecodeFrameImpl(IMpegFrame frame, Array dest, int destOffset) { frame.Reset(); LayerDecoderBase layerDecoderBase = null; switch (frame.Layer) { case MpegLayer.LayerI: if (_layerIDecoder == null) { _layerIDecoder = new LayerIDecoder(); } layerDecoderBase = _layerIDecoder; break; case MpegLayer.LayerII: if (_layerIIDecoder == null) { _layerIIDecoder = new LayerIIDecoder(); } layerDecoderBase = _layerIIDecoder; break; case MpegLayer.LayerIII: if (_layerIIIDecoder == null) { _layerIIIDecoder = new LayerIIIDecoder(); } layerDecoderBase = _layerIIIDecoder; break; } if (layerDecoderBase != null) { layerDecoderBase.SetEQ(_eqFactors); layerDecoderBase.StereoMode = StereoMode; int num = layerDecoderBase.DecodeFrame(frame, _ch0, _ch1); if (frame.ChannelMode == MpegChannelMode.Mono) { Buffer.BlockCopy(_ch0, 0, dest, destOffset * 4, num * 4); } else { for (int i = 0; i < num; i++) { Buffer.BlockCopy(_ch0, i * 4, dest, destOffset * 4, 4); destOffset++; Buffer.BlockCopy(_ch1, i * 4, dest, destOffset * 4, 4); destOffset++; } num *= 2; } return num; } return 0; } public void Reset() { if (_layerIDecoder != null) { _layerIDecoder.ResetForSeek(); } if (_layerIIDecoder != null) { _layerIIDecoder.ResetForSeek(); } if (_layerIIIDecoder != null) { _layerIIIDecoder.ResetForSeek(); } } } } namespace NLayer.Decoder { internal class BitReservoir { private byte[] _buf = new byte[8192]; private int _start; private int _end = -1; private int _bitsLeft; private long _bitsRead; public int BitsAvailable { get { if (_bitsLeft > 0) { return (_end + _buf.Length - _start) % _buf.Length * 8 + _bitsLeft; } return 0; } } public long BitsRead => _bitsRead; private static int GetSlots(IMpegFrame frame) { int num = frame.FrameLength - 4; if (frame.HasCrc) { num -= 2; } if (frame.Version == MpegVersion.Version1 && frame.ChannelMode != MpegChannelMode.Mono) { return num - 32; } if (frame.Version > MpegVersion.Version1 && frame.ChannelMode == MpegChannelMode.Mono) { return num - 9; } return num - 17; } public bool AddBits(IMpegFrame frame, int overlap) { int end = _end; int num = GetSlots(frame); while (--num >= 0) { int num2 = frame.ReadBits(8); if (num2 == -1) { throw new InvalidDataException("Frame did not have enough bytes!"); } _buf[++_end] = (byte)num2; if (_end == _buf.Length - 1) { _end = -1; } } _bitsLeft = 8; if (end == -1) { return overlap == 0; } if ((end + 1 - _start + _buf.Length) % _buf.Length >= overlap) { _start = (end + 1 - overlap + _buf.Length) % _buf.Length; return true; } _start = end + overlap; return false; } public int GetBits(int count) { int readCount; int result = TryPeekBits(count, out readCount); if (readCount < count) { throw new InvalidDataException("Reservoir did not have enough bytes!"); } SkipBits(count); return result; } public int Get1Bit() { if (_bitsLeft == 0) { throw new InvalidDataException("Reservoir did not have enough bytes!"); } _bitsLeft--; _bitsRead++; int result = (_buf[_start] >> _bitsLeft) & 1; if (_bitsLeft == 0 && (_start = (_start + 1) % _buf.Length) != _end + 1) { _bitsLeft = 8; } return result; } public int TryPeekBits(int count, out int readCount) { if (count < 0 || count > 32) { throw new ArgumentOutOfRangeException("count", "Must return between 0 and 32 bits!"); } if (_bitsLeft == 0 || count == 0) { readCount = 0; return 0; } int num = _buf[_start]; if (count < _bitsLeft) { num >>= _bitsLeft - count; num &= (1 << count) - 1; readCount = count; return num; } num &= (1 << _bitsLeft) - 1; count -= _bitsLeft; readCount = _bitsLeft; int num2 = _start; while (count > 0 && (num2 = (num2 + 1) % _buf.Length) != _end + 1) { int num3 = Math.Min(count, 8); num <<= num3; num |= _buf[num2] >> (8 - num3) % 8; count -= num3; readCount += num3; } return num; } public void SkipBits(int count) { if (count > 0) { if (count > BitsAvailable) { throw new ArgumentOutOfRangeException("count"); } int num = 8 - _bitsLeft + count; _start = (num / 8 + _start) % _buf.Length; _bitsLeft = 8 - num % 8; _bitsRead += count; } } public void RewindBits(int count) { _bitsLeft += count; _bitsRead -= count; while (_bitsLeft > 8) { _start--; _bitsLeft -= 8; } while (_start < 0) { _start += _buf.Length; } } public void FlushBits() { if (_bitsLeft < 8) { SkipBits(_bitsLeft); } } public void Reset() { _start = 0; _end = -1; _bitsLeft = 0; } } internal abstract class FrameBase { private static int _totalAllocation; private MpegStreamReader _reader; private byte[] _savedBuffer; internal static int TotalAllocation => Interlocked.CompareExchange(ref _totalAllocation, 0, 0); internal long Offset { get; private set; } internal int Length { get; set; } internal bool Validate(long offset, MpegStreamReader reader) { Offset = offset; _reader = reader; int num = Validate(); if (num > 0) { Length = num; return true; } return false; } protected int Read(int offset, byte[] buffer) { return Read(offset, buffer, 0, buffer.Length); } protected int Read(int offset, byte[] buffer, int index, int count) { if (_savedBuffer != null) { if (index < 0 || index + count > buffer.Length) { return 0; } if (offset < 0 || offset >= _savedBuffer.Length) { return 0; } if (offset + count > _savedBuffer.Length) { count = _savedBuffer.Length - index; } Array.Copy(_savedBuffer, offset, buffer, index, count); return count; } return _reader.Read(Offset + offset, buffer, index, count); } protected int ReadByte(int offset) { if (_savedBuffer != null) { if (offset < 0) { throw new ArgumentOutOfRangeException(); } if (offset >= _savedBuffer.Length) { return -1; } return _savedBuffer[offset]; } return _reader.ReadByte(Offset + offset); } protected abstract int Validate(); internal void SaveBuffer() { _savedBuffer = new byte[Length]; _reader.Read(Offset, _savedBuffer, 0, Length); Interlocked.Add(ref _totalAllocation, Length); } internal void ClearBuffer() { Interlocked.Add(ref _totalAllocation, -Length); _savedBuffer = null; } internal virtual void Parse() { } } internal class Huffman { private class HuffmanListNode { internal byte Value; internal int Length; internal int Bits; internal int Mask; internal HuffmanListNode Next; } private static readonly byte[][,] _codeTables; private static readonly float[] _floatLookup; private static HuffmanListNode[] _llCache; private static int[] _llCacheMaxBits; private static readonly int[] LIN_BITS; static Huffman() { _codeTables = new byte[17][,] { new byte[7, 2] { { 2, 1 }, { 0, 0 }, { 2, 1 }, { 0, 16 }, { 2, 1 }, { 0, 1 }, { 0, 17 } }, new byte[17, 2] { { 2, 1 }, { 0, 0 }, { 4, 1 }, { 2, 1 }, { 0, 16 }, { 0, 1 }, { 2, 1 }, { 0, 17 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 33 }, { 2, 1 }, { 0, 18 }, { 2, 1 }, { 0, 2 }, { 0, 34 } }, new byte[17, 2] { { 4, 1 }, { 2, 1 }, { 0, 0 }, { 0, 1 }, { 2, 1 }, { 0, 17 }, { 2, 1 }, { 0, 16 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 33 }, { 2, 1 }, { 0, 18 }, { 2, 1 }, { 0, 2 }, { 0, 34 } }, new byte[31, 2] { { 2, 1 }, { 0, 0 }, { 4, 1 }, { 2, 1 }, { 0, 16 }, { 0, 1 }, { 2, 1 }, { 0, 17 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 2, 1 }, { 0, 33 }, { 0, 18 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 34 }, { 0, 48 }, { 2, 1 }, { 0, 3 }, { 0, 19 }, { 2, 1 }, { 0, 49 }, { 2, 1 }, { 0, 50 }, { 2, 1 }, { 0, 35 }, { 0, 51 } }, new byte[31, 2] { { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 0 }, { 0, 16 }, { 0, 17 }, { 6, 1 }, { 2, 1 }, { 0, 1 }, { 2, 1 }, { 0, 32 }, { 0, 33 }, { 6, 1 }, { 2, 1 }, { 0, 18 }, { 2, 1 }, { 0, 2 }, { 0, 34 }, { 4, 1 }, { 2, 1 }, { 0, 49 }, { 0, 19 }, { 4, 1 }, { 2, 1 }, { 0, 48 }, { 0, 50 }, { 2, 1 }, { 0, 35 }, { 2, 1 }, { 0, 3 }, { 0, 51 } }, new byte[71, 2] { { 2, 1 }, { 0, 0 }, { 4, 1 }, { 2, 1 }, { 0, 16 }, { 0, 1 }, { 8, 1 }, { 2, 1 }, { 0, 17 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 0, 33 }, { 18, 1 }, { 6, 1 }, { 2, 1 }, { 0, 18 }, { 2, 1 }, { 0, 34 }, { 0, 48 }, { 4, 1 }, { 2, 1 }, { 0, 49 }, { 0, 19 }, { 4, 1 }, { 2, 1 }, { 0, 3 }, { 0, 50 }, { 2, 1 }, { 0, 35 }, { 0, 4 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 64 }, { 0, 65 }, { 2, 1 }, { 0, 20 }, { 2, 1 }, { 0, 66 }, { 0, 36 }, { 12, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 51 }, { 0, 67 }, { 0, 80 }, { 4, 1 }, { 2, 1 }, { 0, 52 }, { 0, 5 }, { 0, 81 }, { 6, 1 }, { 2, 1 }, { 0, 21 }, { 2, 1 }, { 0, 82 }, { 0, 37 }, { 4, 1 }, { 2, 1 }, { 0, 68 }, { 0, 53 }, { 4, 1 }, { 2, 1 }, { 0, 83 }, { 0, 84 }, { 2, 1 }, { 0, 69 }, { 0, 85 } }, new byte[71, 2] { { 6, 1 }, { 2, 1 }, { 0, 0 }, { 2, 1 }, { 0, 16 }, { 0, 1 }, { 2, 1 }, { 0, 17 }, { 4, 1 }, { 2, 1 }, { 0, 33 }, { 0, 18 }, { 14, 1 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 2, 1 }, { 0, 34 }, { 4, 1 }, { 2, 1 }, { 0, 48 }, { 0, 3 }, { 2, 1 }, { 0, 49 }, { 0, 19 }, { 14, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 50 }, { 0, 35 }, { 2, 1 }, { 0, 64 }, { 0, 4 }, { 2, 1 }, { 0, 65 }, { 2, 1 }, { 0, 20 }, { 0, 66 }, { 12, 1 }, { 6, 1 }, { 2, 1 }, { 0, 36 }, { 2, 1 }, { 0, 51 }, { 0, 80 }, { 4, 1 }, { 2, 1 }, { 0, 67 }, { 0, 52 }, { 0, 81 }, { 6, 1 }, { 2, 1 }, { 0, 21 }, { 2, 1 }, { 0, 5 }, { 0, 82 }, { 6, 1 }, { 2, 1 }, { 0, 37 }, { 2, 1 }, { 0, 68 }, { 0, 53 }, { 2, 1 }, { 0, 83 }, { 2, 1 }, { 0, 69 }, { 2, 1 }, { 0, 84 }, { 0, 85 } }, new byte[71, 2] { { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 0 }, { 0, 16 }, { 2, 1 }, { 0, 1 }, { 0, 17 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 33 }, { 2, 1 }, { 0, 18 }, { 2, 1 }, { 0, 2 }, { 0, 34 }, { 12, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 48 }, { 0, 3 }, { 0, 49 }, { 2, 1 }, { 0, 19 }, { 2, 1 }, { 0, 50 }, { 0, 35 }, { 12, 1 }, { 4, 1 }, { 2, 1 }, { 0, 65 }, { 0, 20 }, { 4, 1 }, { 2, 1 }, { 0, 64 }, { 0, 51 }, { 2, 1 }, { 0, 66 }, { 0, 36 }, { 10, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 4 }, { 0, 80 }, { 0, 67 }, { 2, 1 }, { 0, 52 }, { 0, 81 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 21 }, { 0, 82 }, { 2, 1 }, { 0, 37 }, { 0, 68 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 5 }, { 0, 84 }, { 0, 83 }, { 2, 1 }, { 0, 53 }, { 2, 1 }, { 0, 69 }, { 0, 85 } }, new byte[127, 2] { { 2, 1 }, { 0, 0 }, { 4, 1 }, { 2, 1 }, { 0, 16 }, { 0, 1 }, { 10, 1 }, { 2, 1 }, { 0, 17 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 2, 1 }, { 0, 33 }, { 0, 18 }, { 28, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 34 }, { 0, 48 }, { 2, 1 }, { 0, 49 }, { 0, 19 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 3 }, { 0, 50 }, { 2, 1 }, { 0, 35 }, { 0, 64 }, { 4, 1 }, { 2, 1 }, { 0, 65 }, { 0, 20 }, { 4, 1 }, { 2, 1 }, { 0, 4 }, { 0, 51 }, { 2, 1 }, { 0, 66 }, { 0, 36 }, { 28, 1 }, { 10, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 80 }, { 0, 5 }, { 0, 96 }, { 2, 1 }, { 0, 97 }, { 0, 22 }, { 12, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 67 }, { 0, 52 }, { 0, 81 }, { 2, 1 }, { 0, 21 }, { 2, 1 }, { 0, 82 }, { 0, 37 }, { 4, 1 }, { 2, 1 }, { 0, 38 }, { 0, 54 }, { 0, 113 }, { 20, 1 }, { 8, 1 }, { 2, 1 }, { 0, 23 }, { 4, 1 }, { 2, 1 }, { 0, 68 }, { 0, 83 }, { 0, 6 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 53 }, { 0, 69 }, { 0, 98 }, { 2, 1 }, { 0, 112 }, { 2, 1 }, { 0, 7 }, { 0, 100 }, { 14, 1 }, { 4, 1 }, { 2, 1 }, { 0, 114 }, { 0, 39 }, { 6, 1 }, { 2, 1 }, { 0, 99 }, { 2, 1 }, { 0, 84 }, { 0, 85 }, { 2, 1 }, { 0, 70 }, { 0, 115 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 55 }, { 0, 101 }, { 2, 1 }, { 0, 86 }, { 0, 116 }, { 6, 1 }, { 2, 1 }, { 0, 71 }, { 2, 1 }, { 0, 102 }, { 0, 117 }, { 4, 1 }, { 2, 1 }, { 0, 87 }, { 0, 118 }, { 2, 1 }, { 0, 103 }, { 0, 119 } }, new byte[127, 2] { { 6, 1 }, { 2, 1 }, { 0, 0 }, { 2, 1 }, { 0, 16 }, { 0, 1 }, { 8, 1 }, { 2, 1 }, { 0, 17 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 0, 18 }, { 24, 1 }, { 8, 1 }, { 2, 1 }, { 0, 33 }, { 2, 1 }, { 0, 34 }, { 2, 1 }, { 0, 48 }, { 0, 3 }, { 4, 1 }, { 2, 1 }, { 0, 49 }, { 0, 19 }, { 4, 1 }, { 2, 1 }, { 0, 50 }, { 0, 35 }, { 4, 1 }, { 2, 1 }, { 0, 64 }, { 0, 4 }, { 2, 1 }, { 0, 65 }, { 0, 20 }, { 30, 1 }, { 16, 1 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 66 }, { 0, 36 }, { 4, 1 }, { 2, 1 }, { 0, 51 }, { 0, 67 }, { 0, 80 }, { 4, 1 }, { 2, 1 }, { 0, 52 }, { 0, 81 }, { 0, 97 }, { 6, 1 }, { 2, 1 }, { 0, 22 }, { 2, 1 }, { 0, 6 }, { 0, 38 }, { 2, 1 }, { 0, 98 }, { 2, 1 }, { 0, 21 }, { 2, 1 }, { 0, 5 }, { 0, 82 }, { 16, 1 }, { 10, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 37 }, { 0, 68 }, { 0, 96 }, { 2, 1 }, { 0, 99 }, { 0, 54 }, { 4, 1 }, { 2, 1 }, { 0, 112 }, { 0, 23 }, { 0, 113 }, { 16, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 7 }, { 0, 100 }, { 0, 114 }, { 2, 1 }, { 0, 39 }, { 4, 1 }, { 2, 1 }, { 0, 83 }, { 0, 53 }, { 2, 1 }, { 0, 84 }, { 0, 69 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 70 }, { 0, 115 }, { 2, 1 }, { 0, 55 }, { 2, 1 }, { 0, 101 }, { 0, 86 }, { 10, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 85 }, { 0, 87 }, { 0, 116 }, { 2, 1 }, { 0, 71 }, { 0, 102 }, { 4, 1 }, { 2, 1 }, { 0, 117 }, { 0, 118 }, { 2, 1 }, { 0, 103 }, { 0, 119 } }, new byte[127, 2] { { 12, 1 }, { 4, 1 }, { 2, 1 }, { 0, 16 }, { 0, 1 }, { 2, 1 }, { 0, 17 }, { 2, 1 }, { 0, 0 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 16, 1 }, { 4, 1 }, { 2, 1 }, { 0, 33 }, { 0, 18 }, { 4, 1 }, { 2, 1 }, { 0, 34 }, { 0, 49 }, { 2, 1 }, { 0, 19 }, { 2, 1 }, { 0, 48 }, { 2, 1 }, { 0, 3 }, { 0, 64 }, { 26, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 50 }, { 0, 35 }, { 2, 1 }, { 0, 65 }, { 0, 51 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 20 }, { 0, 66 }, { 2, 1 }, { 0, 36 }, { 2, 1 }, { 0, 4 }, { 0, 80 }, { 4, 1 }, { 2, 1 }, { 0, 67 }, { 0, 52 }, { 2, 1 }, { 0, 81 }, { 0, 21 }, { 28, 1 }, { 14, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 82 }, { 0, 37 }, { 2, 1 }, { 0, 83 }, { 0, 53 }, { 4, 1 }, { 2, 1 }, { 0, 96 }, { 0, 22 }, { 0, 97 }, { 4, 1 }, { 2, 1 }, { 0, 98 }, { 0, 38 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 5 }, { 0, 6 }, { 0, 68 }, { 2, 1 }, { 0, 84 }, { 0, 69 }, { 18, 1 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 99 }, { 0, 54 }, { 4, 1 }, { 2, 1 }, { 0, 112 }, { 0, 7 }, { 0, 113 }, { 4, 1 }, { 2, 1 }, { 0, 23 }, { 0, 100 }, { 2, 1 }, { 0, 70 }, { 0, 114 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 39 }, { 2, 1 }, { 0, 85 }, { 0, 115 }, { 2, 1 }, { 0, 55 }, { 0, 86 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 101 }, { 0, 116 }, { 2, 1 }, { 0, 71 }, { 0, 102 }, { 4, 1 }, { 2, 1 }, { 0, 117 }, { 0, 87 }, { 2, 1 }, { 0, 118 }, { 2, 1 }, { 0, 103 }, { 0, 119 } }, new byte[511, 2] { { 2, 1 }, { 0, 0 }, { 6, 1 }, { 2, 1 }, { 0, 16 }, { 2, 1 }, { 0, 1 }, { 0, 17 }, { 28, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 2, 1 }, { 0, 33 }, { 0, 18 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 34 }, { 0, 48 }, { 2, 1 }, { 0, 3 }, { 0, 49 }, { 6, 1 }, { 2, 1 }, { 0, 19 }, { 2, 1 }, { 0, 50 }, { 0, 35 }, { 4, 1 }, { 2, 1 }, { 0, 64 }, { 0, 4 }, { 0, 65 }, { 70, 1 }, { 28, 1 }, { 14, 1 }, { 6, 1 }, { 2, 1 }, { 0, 20 }, { 2, 1 }, { 0, 51 }, { 0, 66 }, { 4, 1 }, { 2, 1 }, { 0, 36 }, { 0, 80 }, { 2, 1 }, { 0, 67 }, { 0, 52 }, { 4, 1 }, { 2, 1 }, { 0, 81 }, { 0, 21 }, { 4, 1 }, { 2, 1 }, { 0, 5 }, { 0, 82 }, { 2, 1 }, { 0, 37 }, { 2, 1 }, { 0, 68 }, { 0, 83 }, { 14, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 96 }, { 0, 6 }, { 2, 1 }, { 0, 97 }, { 0, 22 }, { 4, 1 }, { 2, 1 }, { 0, 128 }, { 0, 8 }, { 0, 129 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 53 }, { 0, 98 }, { 2, 1 }, { 0, 38 }, { 0, 84 }, { 4, 1 }, { 2, 1 }, { 0, 69 }, { 0, 99 }, { 2, 1 }, { 0, 54 }, { 0, 112 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 7 }, { 0, 85 }, { 0, 113 }, { 2, 1 }, { 0, 23 }, { 2, 1 }, { 0, 39 }, { 0, 55 }, { 72, 1 }, { 24, 1 }, { 12, 1 }, { 4, 1 }, { 2, 1 }, { 0, 24 }, { 0, 130 }, { 2, 1 }, { 0, 40 }, { 4, 1 }, { 2, 1 }, { 0, 100 }, { 0, 70 }, { 0, 114 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 132 }, { 0, 72 }, { 2, 1 }, { 0, 144 }, { 0, 9 }, { 2, 1 }, { 0, 145 }, { 0, 25 }, { 24, 1 }, { 14, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 115 }, { 0, 101 }, { 2, 1 }, { 0, 86 }, { 0, 116 }, { 4, 1 }, { 2, 1 }, { 0, 71 }, { 0, 102 }, { 0, 131 }, { 6, 1 }, { 2, 1 }, { 0, 56 }, { 2, 1 }, { 0, 117 }, { 0, 87 }, { 2, 1 }, { 0, 146 }, { 0, 41 }, { 14, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 103 }, { 0, 133 }, { 2, 1 }, { 0, 88 }, { 0, 57 }, { 2, 1 }, { 0, 147 }, { 2, 1 }, { 0, 73 }, { 0, 134 }, { 6, 1 }, { 2, 1 }, { 0, 160 }, { 2, 1 }, { 0, 104 }, { 0, 10 }, { 2, 1 }, { 0, 161 }, { 0, 26 }, { 68, 1 }, { 24, 1 }, { 12, 1 }, { 4, 1 }, { 2, 1 }, { 0, 162 }, { 0, 42 }, { 4, 1 }, { 2, 1 }, { 0, 149 }, { 0, 89 }, { 2, 1 }, { 0, 163 }, { 0, 58 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 74 }, { 0, 150 }, { 2, 1 }, { 0, 176 }, { 0, 11 }, { 2, 1 }, { 0, 177 }, { 0, 27 }, { 20, 1 }, { 8, 1 }, { 2, 1 }, { 0, 178 }, { 4, 1 }, { 2, 1 }, { 0, 118 }, { 0, 119 }, { 0, 148 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 135 }, { 0, 120 }, { 0, 164 }, { 4, 1 }, { 2, 1 }, { 0, 105 }, { 0, 165 }, { 0, 43 }, { 12, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 90 }, { 0, 136 }, { 0, 179 }, { 2, 1 }, { 0, 59 }, { 2, 1 }, { 0, 121 }, { 0, 166 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 106 }, { 0, 180 }, { 0, 192 }, { 4, 1 }, { 2, 1 }, { 0, 12 }, { 0, 152 }, { 0, 193 }, { 60, 1 }, { 22, 1 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 28 }, { 2, 1 }, { 0, 137 }, { 0, 181 }, { 2, 1 }, { 0, 91 }, { 0, 194 }, { 4, 1 }, { 2, 1 }, { 0, 44 }, { 0, 60 }, { 4, 1 }, { 2, 1 }, { 0, 182 }, { 0, 107 }, { 2, 1 }, { 0, 196 }, { 0, 76 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 168 }, { 0, 138 }, { 2, 1 }, { 0, 208 }, { 0, 13 }, { 2, 1 }, { 0, 209 }, { 2, 1 }, { 0, 75 }, { 2, 1 }, { 0, 151 }, { 0, 167 }, { 12, 1 }, { 6, 1 }, { 2, 1 }, { 0, 195 }, { 2, 1 }, { 0, 122 }, { 0, 153 }, { 4, 1 }, { 2, 1 }, { 0, 197 }, { 0, 92 }, { 0, 183 }, { 4, 1 }, { 2, 1 }, { 0, 29 }, { 0, 210 }, { 2, 1 }, { 0, 45 }, { 2, 1 }, { 0, 123 }, { 0, 211 }, { 52, 1 }, { 28, 1 }, { 12, 1 }, { 4, 1 }, { 2, 1 }, { 0, 61 }, { 0, 198 }, { 4, 1 }, { 2, 1 }, { 0, 108 }, { 0, 169 }, { 2, 1 }, { 0, 154 }, { 0, 212 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 184 }, { 0, 139 }, { 2, 1 }, { 0, 77 }, { 0, 199 }, { 4, 1 }, { 2, 1 }, { 0, 124 }, { 0, 213 }, { 2, 1 }, { 0, 93 }, { 0, 224 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 225 }, { 0, 30 }, { 4, 1 }, { 2, 1 }, { 0, 14 }, { 0, 46 }, { 0, 226 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 227 }, { 0, 109 }, { 2, 1 }, { 0, 140 }, { 0, 228 }, { 4, 1 }, { 2, 1 }, { 0, 229 }, { 0, 186 }, { 0, 240 }, { 38, 1 }, { 16, 1 }, { 4, 1 }, { 2, 1 }, { 0, 241 }, { 0, 31 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 170 }, { 0, 155 }, { 0, 185 }, { 2, 1 }, { 0, 62 }, { 2, 1 }, { 0, 214 }, { 0, 200 }, { 12, 1 }, { 6, 1 }, { 2, 1 }, { 0, 78 }, { 2, 1 }, { 0, 215 }, { 0, 125 }, { 2, 1 }, { 0, 171 }, { 2, 1 }, { 0, 94 }, { 0, 201 }, { 6, 1 }, { 2, 1 }, { 0, 15 }, { 2, 1 }, { 0, 156 }, { 0, 110 }, { 2, 1 }, { 0, 242 }, { 0, 47 }, { 32, 1 }, { 16, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 216 }, { 0, 141 }, { 0, 63 }, { 6, 1 }, { 2, 1 }, { 0, 243 }, { 2, 1 }, { 0, 230 }, { 0, 202 }, { 2, 1 }, { 0, 244 }, { 0, 79 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 187 }, { 0, 172 }, { 2, 1 }, { 0, 231 }, { 0, 245 }, { 4, 1 }, { 2, 1 }, { 0, 217 }, { 0, 157 }, { 2, 1 }, { 0, 95 }, { 0, 232 }, { 30, 1 }, { 12, 1 }, { 6, 1 }, { 2, 1 }, { 0, 111 }, { 2, 1 }, { 0, 246 }, { 0, 203 }, { 4, 1 }, { 2, 1 }, { 0, 188 }, { 0, 173 }, { 0, 218 }, { 8, 1 }, { 2, 1 }, { 0, 247 }, { 4, 1 }, { 2, 1 }, { 0, 126 }, { 0, 127 }, { 0, 142 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 158 }, { 0, 174 }, { 0, 204 }, { 2, 1 }, { 0, 248 }, { 0, 143 }, { 18, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 219 }, { 0, 189 }, { 2, 1 }, { 0, 234 }, { 0, 249 }, { 4, 1 }, { 2, 1 }, { 0, 159 }, { 0, 235 }, { 2, 1 }, { 0, 190 }, { 2, 1 }, { 0, 205 }, { 0, 250 }, { 14, 1 }, { 4, 1 }, { 2, 1 }, { 0, 221 }, { 0, 236 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 233 }, { 0, 175 }, { 0, 220 }, { 2, 1 }, { 0, 206 }, { 0, 251 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 191 }, { 0, 222 }, { 2, 1 }, { 0, 207 }, { 0, 238 }, { 4, 1 }, { 2, 1 }, { 0, 223 }, { 0, 239 }, { 2, 1 }, { 0, 255 }, { 2, 1 }, { 0, 237 }, { 2, 1 }, { 0, 253 }, { 2, 1 }, { 0, 252 }, { 0, 254 } }, new byte[511, 2] { { 16, 1 }, { 6, 1 }, { 2, 1 }, { 0, 0 }, { 2, 1 }, { 0, 16 }, { 0, 1 }, { 2, 1 }, { 0, 17 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 2, 1 }, { 0, 33 }, { 0, 18 }, { 50, 1 }, { 16, 1 }, { 6, 1 }, { 2, 1 }, { 0, 34 }, { 2, 1 }, { 0, 48 }, { 0, 49 }, { 6, 1 }, { 2, 1 }, { 0, 19 }, { 2, 1 }, { 0, 3 }, { 0, 64 }, { 2, 1 }, { 0, 50 }, { 0, 35 }, { 14, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 4 }, { 0, 20 }, { 0, 65 }, { 4, 1 }, { 2, 1 }, { 0, 51 }, { 0, 66 }, { 2, 1 }, { 0, 36 }, { 0, 67 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 52 }, { 2, 1 }, { 0, 80 }, { 0, 5 }, { 2, 1 }, { 0, 81 }, { 0, 21 }, { 4, 1 }, { 2, 1 }, { 0, 82 }, { 0, 37 }, { 4, 1 }, { 2, 1 }, { 0, 68 }, { 0, 83 }, { 0, 97 }, { 90, 1 }, { 36, 1 }, { 18, 1 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 53 }, { 2, 1 }, { 0, 96 }, { 0, 6 }, { 2, 1 }, { 0, 22 }, { 0, 98 }, { 4, 1 }, { 2, 1 }, { 0, 38 }, { 0, 84 }, { 2, 1 }, { 0, 69 }, { 0, 99 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 54 }, { 2, 1 }, { 0, 112 }, { 0, 7 }, { 2, 1 }, { 0, 113 }, { 0, 85 }, { 4, 1 }, { 2, 1 }, { 0, 23 }, { 0, 100 }, { 2, 1 }, { 0, 114 }, { 0, 39 }, { 24, 1 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 70 }, { 0, 115 }, { 2, 1 }, { 0, 55 }, { 0, 101 }, { 4, 1 }, { 2, 1 }, { 0, 86 }, { 0, 128 }, { 2, 1 }, { 0, 8 }, { 0, 116 }, { 4, 1 }, { 2, 1 }, { 0, 129 }, { 0, 24 }, { 2, 1 }, { 0, 130 }, { 0, 40 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 71 }, { 0, 102 }, { 2, 1 }, { 0, 131 }, { 0, 56 }, { 4, 1 }, { 2, 1 }, { 0, 117 }, { 0, 87 }, { 2, 1 }, { 0, 132 }, { 0, 72 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 144 }, { 0, 25 }, { 0, 145 }, { 4, 1 }, { 2, 1 }, { 0, 146 }, { 0, 118 }, { 2, 1 }, { 0, 103 }, { 0, 41 }, { 92, 1 }, { 36, 1 }, { 18, 1 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 133 }, { 0, 88 }, { 4, 1 }, { 2, 1 }, { 0, 9 }, { 0, 119 }, { 0, 147 }, { 4, 1 }, { 2, 1 }, { 0, 57 }, { 0, 148 }, { 2, 1 }, { 0, 73 }, { 0, 134 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 104 }, { 2, 1 }, { 0, 160 }, { 0, 10 }, { 2, 1 }, { 0, 161 }, { 0, 26 }, { 4, 1 }, { 2, 1 }, { 0, 162 }, { 0, 42 }, { 2, 1 }, { 0, 149 }, { 0, 89 }, { 26, 1 }, { 14, 1 }, { 6, 1 }, { 2, 1 }, { 0, 163 }, { 2, 1 }, { 0, 58 }, { 0, 135 }, { 4, 1 }, { 2, 1 }, { 0, 120 }, { 0, 164 }, { 2, 1 }, { 0, 74 }, { 0, 150 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 105 }, { 0, 176 }, { 0, 177 }, { 4, 1 }, { 2, 1 }, { 0, 27 }, { 0, 165 }, { 0, 178 }, { 14, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 90 }, { 0, 43 }, { 2, 1 }, { 0, 136 }, { 0, 151 }, { 2, 1 }, { 0, 179 }, { 2, 1 }, { 0, 121 }, { 0, 59 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 106 }, { 0, 180 }, { 2, 1 }, { 0, 75 }, { 0, 193 }, { 4, 1 }, { 2, 1 }, { 0, 152 }, { 0, 137 }, { 2, 1 }, { 0, 28 }, { 0, 181 }, { 80, 1 }, { 34, 1 }, { 16, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 91 }, { 0, 44 }, { 0, 194 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 11 }, { 0, 192 }, { 0, 166 }, { 2, 1 }, { 0, 167 }, { 0, 122 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 195 }, { 0, 60 }, { 4, 1 }, { 2, 1 }, { 0, 12 }, { 0, 153 }, { 0, 182 }, { 4, 1 }, { 2, 1 }, { 0, 107 }, { 0, 196 }, { 2, 1 }, { 0, 76 }, { 0, 168 }, { 20, 1 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 138 }, { 0, 197 }, { 4, 1 }, { 2, 1 }, { 0, 208 }, { 0, 92 }, { 0, 209 }, { 4, 1 }, { 2, 1 }, { 0, 183 }, { 0, 123 }, { 2, 1 }, { 0, 29 }, { 2, 1 }, { 0, 13 }, { 0, 45 }, { 12, 1 }, { 4, 1 }, { 2, 1 }, { 0, 210 }, { 0, 211 }, { 4, 1 }, { 2, 1 }, { 0, 61 }, { 0, 198 }, { 2, 1 }, { 0, 108 }, { 0, 169 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 154 }, { 0, 184 }, { 0, 212 }, { 4, 1 }, { 2, 1 }, { 0, 139 }, { 0, 77 }, { 2, 1 }, { 0, 199 }, { 0, 124 }, { 68, 1 }, { 34, 1 }, { 18, 1 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 213 }, { 0, 93 }, { 4, 1 }, { 2, 1 }, { 0, 224 }, { 0, 14 }, { 0, 225 }, { 4, 1 }, { 2, 1 }, { 0, 30 }, { 0, 226 }, { 2, 1 }, { 0, 170 }, { 0, 46 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 185 }, { 0, 155 }, { 2, 1 }, { 0, 227 }, { 0, 214 }, { 4, 1 }, { 2, 1 }, { 0, 109 }, { 0, 62 }, { 2, 1 }, { 0, 200 }, { 0, 140 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 228 }, { 0, 78 }, { 2, 1 }, { 0, 215 }, { 0, 125 }, { 4, 1 }, { 2, 1 }, { 0, 229 }, { 0, 186 }, { 2, 1 }, { 0, 171 }, { 0, 94 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 201 }, { 0, 156 }, { 2, 1 }, { 0, 241 }, { 0, 31 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 240 }, { 0, 110 }, { 0, 242 }, { 2, 1 }, { 0, 47 }, { 0, 230 }, { 38, 1 }, { 18, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 216 }, { 0, 243 }, { 2, 1 }, { 0, 63 }, { 0, 244 }, { 6, 1 }, { 2, 1 }, { 0, 79 }, { 2, 1 }, { 0, 141 }, { 0, 217 }, { 2, 1 }, { 0, 187 }, { 0, 202 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 172 }, { 0, 231 }, { 2, 1 }, { 0, 126 }, { 0, 245 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 157 }, { 0, 95 }, { 2, 1 }, { 0, 232 }, { 0, 142 }, { 2, 1 }, { 0, 246 }, { 0, 203 }, { 34, 1 }, { 18, 1 }, { 10, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 15 }, { 0, 174 }, { 0, 111 }, { 2, 1 }, { 0, 188 }, { 0, 218 }, { 4, 1 }, { 2, 1 }, { 0, 173 }, { 0, 247 }, { 2, 1 }, { 0, 127 }, { 0, 233 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 158 }, { 0, 204 }, { 2, 1 }, { 0, 248 }, { 0, 143 }, { 4, 1 }, { 2, 1 }, { 0, 219 }, { 0, 189 }, { 2, 1 }, { 0, 234 }, { 0, 249 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 159 }, { 0, 220 }, { 2, 1 }, { 0, 205 }, { 0, 235 }, { 4, 1 }, { 2, 1 }, { 0, 190 }, { 0, 250 }, { 2, 1 }, { 0, 175 }, { 0, 221 }, { 14, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 236 }, { 0, 206 }, { 0, 251 }, { 4, 1 }, { 2, 1 }, { 0, 191 }, { 0, 237 }, { 2, 1 }, { 0, 222 }, { 0, 252 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 207 }, { 0, 253 }, { 0, 238 }, { 4, 1 }, { 2, 1 }, { 0, 223 }, { 0, 254 }, { 2, 1 }, { 0, 239 }, { 0, 255 } }, new byte[511, 2] { { 2, 1 }, { 0, 0 }, { 6, 1 }, { 2, 1 }, { 0, 16 }, { 2, 1 }, { 0, 1 }, { 0, 17 }, { 42, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 2, 1 }, { 0, 33 }, { 0, 18 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 34 }, { 2, 1 }, { 0, 48 }, { 0, 3 }, { 2, 1 }, { 0, 49 }, { 0, 19 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 50 }, { 0, 35 }, { 4, 1 }, { 2, 1 }, { 0, 64 }, { 0, 4 }, { 0, 65 }, { 6, 1 }, { 2, 1 }, { 0, 20 }, { 2, 1 }, { 0, 51 }, { 0, 66 }, { 4, 1 }, { 2, 1 }, { 0, 36 }, { 0, 80 }, { 2, 1 }, { 0, 67 }, { 0, 52 }, { 138, 1 }, { 40, 1 }, { 16, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 5 }, { 0, 21 }, { 0, 81 }, { 4, 1 }, { 2, 1 }, { 0, 82 }, { 0, 37 }, { 4, 1 }, { 2, 1 }, { 0, 68 }, { 0, 53 }, { 0, 83 }, { 10, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 96 }, { 0, 6 }, { 0, 97 }, { 2, 1 }, { 0, 22 }, { 0, 98 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 38 }, { 0, 84 }, { 2, 1 }, { 0, 69 }, { 0, 99 }, { 4, 1 }, { 2, 1 }, { 0, 54 }, { 0, 112 }, { 0, 113 }, { 40, 1 }, { 18, 1 }, { 8, 1 }, { 2, 1 }, { 0, 23 }, { 2, 1 }, { 0, 7 }, { 2, 1 }, { 0, 85 }, { 0, 100 }, { 4, 1 }, { 2, 1 }, { 0, 114 }, { 0, 39 }, { 4, 1 }, { 2, 1 }, { 0, 70 }, { 0, 101 }, { 0, 115 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 55 }, { 2, 1 }, { 0, 86 }, { 0, 8 }, { 2, 1 }, { 0, 128 }, { 0, 129 }, { 6, 1 }, { 2, 1 }, { 0, 24 }, { 2, 1 }, { 0, 116 }, { 0, 71 }, { 2, 1 }, { 0, 130 }, { 2, 1 }, { 0, 40 }, { 0, 102 }, { 24, 1 }, { 14, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 131 }, { 0, 56 }, { 2, 1 }, { 0, 117 }, { 0, 132 }, { 4, 1 }, { 2, 1 }, { 0, 72 }, { 0, 144 }, { 0, 145 }, { 6, 1 }, { 2, 1 }, { 0, 25 }, { 2, 1 }, { 0, 9 }, { 0, 118 }, { 2, 1 }, { 0, 146 }, { 0, 41 }, { 14, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 133 }, { 0, 88 }, { 2, 1 }, { 0, 147 }, { 0, 57 }, { 4, 1 }, { 2, 1 }, { 0, 160 }, { 0, 10 }, { 0, 26 }, { 8, 1 }, { 2, 1 }, { 0, 162 }, { 2, 1 }, { 0, 103 }, { 2, 1 }, { 0, 87 }, { 0, 73 }, { 6, 1 }, { 2, 1 }, { 0, 148 }, { 2, 1 }, { 0, 119 }, { 0, 134 }, { 2, 1 }, { 0, 161 }, { 2, 1 }, { 0, 104 }, { 0, 149 }, { 220, 1 }, { 126, 1 }, { 50, 1 }, { 26, 1 }, { 12, 1 }, { 6, 1 }, { 2, 1 }, { 0, 42 }, { 2, 1 }, { 0, 89 }, { 0, 58 }, { 2, 1 }, { 0, 163 }, { 2, 1 }, { 0, 135 }, { 0, 120 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 164 }, { 0, 74 }, { 2, 1 }, { 0, 150 }, { 0, 105 }, { 4, 1 }, { 2, 1 }, { 0, 176 }, { 0, 11 }, { 0, 177 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 27 }, { 0, 178 }, { 2, 1 }, { 0, 43 }, { 2, 1 }, { 0, 165 }, { 0, 90 }, { 6, 1 }, { 2, 1 }, { 0, 179 }, { 2, 1 }, { 0, 166 }, { 0, 106 }, { 4, 1 }, { 2, 1 }, { 0, 180 }, { 0, 75 }, { 2, 1 }, { 0, 12 }, { 0, 193 }, { 30, 1 }, { 14, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 181 }, { 0, 194 }, { 0, 44 }, { 4, 1 }, { 2, 1 }, { 0, 167 }, { 0, 195 }, { 2, 1 }, { 0, 107 }, { 0, 196 }, { 8, 1 }, { 2, 1 }, { 0, 29 }, { 4, 1 }, { 2, 1 }, { 0, 136 }, { 0, 151 }, { 0, 59 }, { 4, 1 }, { 2, 1 }, { 0, 209 }, { 0, 210 }, { 2, 1 }, { 0, 45 }, { 0, 211 }, { 18, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 30 }, { 0, 46 }, { 0, 226 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 121 }, { 0, 152 }, { 0, 192 }, { 2, 1 }, { 0, 28 }, { 2, 1 }, { 0, 137 }, { 0, 91 }, { 14, 1 }, { 6, 1 }, { 2, 1 }, { 0, 60 }, { 2, 1 }, { 0, 122 }, { 0, 182 }, { 4, 1 }, { 2, 1 }, { 0, 76 }, { 0, 153 }, { 2, 1 }, { 0, 168 }, { 0, 138 }, { 6, 1 }, { 2, 1 }, { 0, 13 }, { 2, 1 }, { 0, 197 }, { 0, 92 }, { 4, 1 }, { 2, 1 }, { 0, 61 }, { 0, 198 }, { 2, 1 }, { 0, 108 }, { 0, 154 }, { 88, 1 }, { 86, 1 }, { 36, 1 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 139 }, { 0, 77 }, { 2, 1 }, { 0, 199 }, { 0, 124 }, { 4, 1 }, { 2, 1 }, { 0, 213 }, { 0, 93 }, { 2, 1 }, { 0, 224 }, { 0, 14 }, { 8, 1 }, { 2, 1 }, { 0, 227 }, { 4, 1 }, { 2, 1 }, { 0, 208 }, { 0, 183 }, { 0, 123 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 169 }, { 0, 184 }, { 0, 212 }, { 2, 1 }, { 0, 225 }, { 2, 1 }, { 0, 170 }, { 0, 185 }, { 24, 1 }, { 10, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 155 }, { 0, 214 }, { 0, 109 }, { 2, 1 }, { 0, 62 }, { 0, 200 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 140 }, { 0, 228 }, { 0, 78 }, { 4, 1 }, { 2, 1 }, { 0, 215 }, { 0, 229 }, { 2, 1 }, { 0, 186 }, { 0, 171 }, { 12, 1 }, { 4, 1 }, { 2, 1 }, { 0, 156 }, { 0, 230 }, { 4, 1 }, { 2, 1 }, { 0, 110 }, { 0, 216 }, { 2, 1 }, { 0, 141 }, { 0, 187 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 231 }, { 0, 157 }, { 2, 1 }, { 0, 232 }, { 0, 142 }, { 4, 1 }, { 2, 1 }, { 0, 203 }, { 0, 188 }, { 0, 158 }, { 0, 241 }, { 2, 1 }, { 0, 31 }, { 2, 1 }, { 0, 15 }, { 0, 47 }, { 66, 1 }, { 56, 1 }, { 2, 1 }, { 0, 242 }, { 52, 1 }, { 50, 1 }, { 20, 1 }, { 8, 1 }, { 2, 1 }, { 0, 189 }, { 2, 1 }, { 0, 94 }, { 2, 1 }, { 0, 125 }, { 0, 201 }, { 6, 1 }, { 2, 1 }, { 0, 202 }, { 2, 1 }, { 0, 172 }, { 0, 126 }, { 4, 1 }, { 2, 1 }, { 0, 218 }, { 0, 173 }, { 0, 204 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 174 }, { 2, 1 }, { 0, 219 }, { 0, 220 }, { 2, 1 }, { 0, 205 }, { 0, 190 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 235 }, { 0, 237 }, { 0, 238 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 217 }, { 0, 234 }, { 0, 233 }, { 2, 1 }, { 0, 222 }, { 4, 1 }, { 2, 1 }, { 0, 221 }, { 0, 236 }, { 0, 206 }, { 0, 63 }, { 0, 240 }, { 4, 1 }, { 2, 1 }, { 0, 243 }, { 0, 244 }, { 2, 1 }, { 0, 79 }, { 2, 1 }, { 0, 245 }, { 0, 95 }, { 10, 1 }, { 2, 1 }, { 0, 255 }, { 4, 1 }, { 2, 1 }, { 0, 246 }, { 0, 111 }, { 2, 1 }, { 0, 247 }, { 0, 127 }, { 12, 1 }, { 6, 1 }, { 2, 1 }, { 0, 143 }, { 2, 1 }, { 0, 248 }, { 0, 249 }, { 4, 1 }, { 2, 1 }, { 0, 159 }, { 0, 250 }, { 0, 175 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 251 }, { 0, 191 }, { 2, 1 }, { 0, 252 }, { 0, 207 }, { 4, 1 }, { 2, 1 }, { 0, 253 }, { 0, 223 }, { 2, 1 }, { 0, 254 }, { 0, 239 } }, new byte[512, 2] { { 60, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 0 }, { 0, 16 }, { 2, 1 }, { 0, 1 }, { 0, 17 }, { 14, 1 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 32 }, { 0, 2 }, { 0, 33 }, { 2, 1 }, { 0, 18 }, { 2, 1 }, { 0, 34 }, { 2, 1 }, { 0, 48 }, { 0, 3 }, { 14, 1 }, { 4, 1 }, { 2, 1 }, { 0, 49 }, { 0, 19 }, { 4, 1 }, { 2, 1 }, { 0, 50 }, { 0, 35 }, { 4, 1 }, { 2, 1 }, { 0, 64 }, { 0, 4 }, { 0, 65 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 20 }, { 0, 51 }, { 2, 1 }, { 0, 66 }, { 0, 36 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 67 }, { 0, 52 }, { 0, 81 }, { 6, 1 }, { 4, 1 }, { 2, 1 }, { 0, 80 }, { 0, 5 }, { 0, 21 }, { 2, 1 }, { 0, 82 }, { 0, 37 }, { 250, 1 }, { 98, 1 }, { 34, 1 }, { 18, 1 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 68 }, { 0, 83 }, { 2, 1 }, { 0, 53 }, { 2, 1 }, { 0, 96 }, { 0, 6 }, { 4, 1 }, { 2, 1 }, { 0, 97 }, { 0, 22 }, { 2, 1 }, { 0, 98 }, { 0, 38 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 84 }, { 0, 69 }, { 2, 1 }, { 0, 99 }, { 0, 54 }, { 4, 1 }, { 2, 1 }, { 0, 113 }, { 0, 85 }, { 2, 1 }, { 0, 100 }, { 0, 70 }, { 32, 1 }, { 14, 1 }, { 6, 1 }, { 2, 1 }, { 0, 114 }, { 2, 1 }, { 0, 39 }, { 0, 55 }, { 2, 1 }, { 0, 115 }, { 4, 1 }, { 2, 1 }, { 0, 112 }, { 0, 7 }, { 0, 23 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 101 }, { 0, 86 }, { 4, 1 }, { 2, 1 }, { 0, 128 }, { 0, 8 }, { 0, 129 }, { 4, 1 }, { 2, 1 }, { 0, 116 }, { 0, 71 }, { 2, 1 }, { 0, 24 }, { 0, 130 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 40 }, { 0, 102 }, { 2, 1 }, { 0, 131 }, { 0, 56 }, { 4, 1 }, { 2, 1 }, { 0, 117 }, { 0, 87 }, { 2, 1 }, { 0, 132 }, { 0, 72 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 145 }, { 0, 25 }, { 2, 1 }, { 0, 146 }, { 0, 118 }, { 4, 1 }, { 2, 1 }, { 0, 103 }, { 0, 41 }, { 2, 1 }, { 0, 133 }, { 0, 88 }, { 92, 1 }, { 34, 1 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 147 }, { 0, 57 }, { 2, 1 }, { 0, 148 }, { 0, 73 }, { 4, 1 }, { 2, 1 }, { 0, 119 }, { 0, 134 }, { 2, 1 }, { 0, 104 }, { 0, 161 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 162 }, { 0, 42 }, { 2, 1 }, { 0, 149 }, { 0, 89 }, { 4, 1 }, { 2, 1 }, { 0, 163 }, { 0, 58 }, { 2, 1 }, { 0, 135 }, { 2, 1 }, { 0, 120 }, { 0, 74 }, { 22, 1 }, { 12, 1 }, { 4, 1 }, { 2, 1 }, { 0, 164 }, { 0, 150 }, { 4, 1 }, { 2, 1 }, { 0, 105 }, { 0, 177 }, { 2, 1 }, { 0, 27 }, { 0, 165 }, { 6, 1 }, { 2, 1 }, { 0, 178 }, { 2, 1 }, { 0, 90 }, { 0, 43 }, { 2, 1 }, { 0, 136 }, { 0, 179 }, { 16, 1 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 144 }, { 2, 1 }, { 0, 9 }, { 0, 160 }, { 2, 1 }, { 0, 151 }, { 0, 121 }, { 4, 1 }, { 2, 1 }, { 0, 166 }, { 0, 106 }, { 0, 180 }, { 12, 1 }, { 6, 1 }, { 2, 1 }, { 0, 26 }, { 2, 1 }, { 0, 10 }, { 0, 176 }, { 2, 1 }, { 0, 59 }, { 2, 1 }, { 0, 11 }, { 0, 192 }, { 4, 1 }, { 2, 1 }, { 0, 75 }, { 0, 193 }, { 2, 1 }, { 0, 152 }, { 0, 137 }, { 67, 1 }, { 34, 1 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 28 }, { 0, 181 }, { 2, 1 }, { 0, 91 }, { 0, 194 }, { 4, 1 }, { 2, 1 }, { 0, 44 }, { 0, 167 }, { 2, 1 }, { 0, 122 }, { 0, 195 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 60 }, { 2, 1 }, { 0, 12 }, { 0, 208 }, { 2, 1 }, { 0, 182 }, { 0, 107 }, { 4, 1 }, { 2, 1 }, { 0, 196 }, { 0, 76 }, { 2, 1 }, { 0, 153 }, { 0, 168 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 138 }, { 0, 197 }, { 2, 1 }, { 0, 92 }, { 0, 209 }, { 4, 1 }, { 2, 1 }, { 0, 183 }, { 0, 123 }, { 2, 1 }, { 0, 29 }, { 0, 210 }, { 9, 1 }, { 4, 1 }, { 2, 1 }, { 0, 45 }, { 0, 211 }, { 2, 1 }, { 0, 61 }, { 0, 198 }, { 85, 250 }, { 4, 1 }, { 2, 1 }, { 0, 108 }, { 0, 169 }, { 2, 1 }, { 0, 154 }, { 0, 212 }, { 32, 1 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 184 }, { 0, 139 }, { 2, 1 }, { 0, 77 }, { 0, 199 }, { 4, 1 }, { 2, 1 }, { 0, 124 }, { 0, 213 }, { 2, 1 }, { 0, 93 }, { 0, 225 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 30 }, { 0, 226 }, { 2, 1 }, { 0, 170 }, { 0, 185 }, { 4, 1 }, { 2, 1 }, { 0, 155 }, { 0, 227 }, { 2, 1 }, { 0, 214 }, { 0, 109 }, { 20, 1 }, { 10, 1 }, { 6, 1 }, { 2, 1 }, { 0, 62 }, { 2, 1 }, { 0, 46 }, { 0, 78 }, { 2, 1 }, { 0, 200 }, { 0, 140 }, { 4, 1 }, { 2, 1 }, { 0, 228 }, { 0, 215 }, { 4, 1 }, { 2, 1 }, { 0, 125 }, { 0, 171 }, { 0, 229 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 186 }, { 0, 94 }, { 2, 1 }, { 0, 201 }, { 2, 1 }, { 0, 156 }, { 0, 110 }, { 8, 1 }, { 2, 1 }, { 0, 230 }, { 2, 1 }, { 0, 13 }, { 2, 1 }, { 0, 224 }, { 0, 14 }, { 4, 1 }, { 2, 1 }, { 0, 216 }, { 0, 141 }, { 2, 1 }, { 0, 187 }, { 0, 202 }, { 74, 1 }, { 2, 1 }, { 0, 255 }, { 64, 1 }, { 58, 1 }, { 32, 1 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 172 }, { 0, 231 }, { 2, 1 }, { 0, 126 }, { 0, 217 }, { 4, 1 }, { 2, 1 }, { 0, 157 }, { 0, 232 }, { 2, 1 }, { 0, 142 }, { 0, 203 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 188 }, { 0, 218 }, { 2, 1 }, { 0, 173 }, { 0, 233 }, { 4, 1 }, { 2, 1 }, { 0, 158 }, { 0, 204 }, { 2, 1 }, { 0, 219 }, { 0, 189 }, { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 234 }, { 0, 174 }, { 2, 1 }, { 0, 220 }, { 0, 205 }, { 4, 1 }, { 2, 1 }, { 0, 235 }, { 0, 190 }, { 2, 1 }, { 0, 221 }, { 0, 236 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 206 }, { 0, 237 }, { 2, 1 }, { 0, 222 }, { 0, 238 }, { 0, 15 }, { 4, 1 }, { 2, 1 }, { 0, 240 }, { 0, 31 }, { 0, 241 }, { 4, 1 }, { 2, 1 }, { 0, 242 }, { 0, 47 }, { 2, 1 }, { 0, 243 }, { 0, 63 }, { 18, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 244 }, { 0, 79 }, { 2, 1 }, { 0, 245 }, { 0, 95 }, { 4, 1 }, { 2, 1 }, { 0, 246 }, { 0, 111 }, { 2, 1 }, { 0, 247 }, { 2, 1 }, { 0, 127 }, { 0, 143 }, { 10, 1 }, { 4, 1 }, { 2, 1 }, { 0, 248 }, { 0, 249 }, { 4, 1 }, { 2, 1 }, { 0, 159 }, { 0, 175 }, { 0, 250 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 251 }, { 0, 191 }, { 2, 1 }, { 0, 252 }, { 0, 207 }, { 4, 1 }, { 2, 1 }, { 0, 253 }, { 0, 223 }, { 2, 1 }, { 0, 254 }, { 0, 239 } }, new byte[31, 2] { { 2, 1 }, { 0, 0 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 8 }, { 0, 4 }, { 2, 1 }, { 0, 1 }, { 0, 2 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 12 }, { 0, 10 }, { 2, 1 }, { 0, 3 }, { 0, 6 }, { 6, 1 }, { 2, 1 }, { 0, 9 }, { 2, 1 }, { 0, 5 }, { 0, 7 }, { 4, 1 }, { 2, 1 }, { 0, 14 }, { 0, 13 }, { 2, 1 }, { 0, 15 }, { 0, 11 } }, new byte[31, 2] { { 16, 1 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 0 }, { 0, 1 }, { 2, 1 }, { 0, 2 }, { 0, 3 }, { 4, 1 }, { 2, 1 }, { 0, 4 }, { 0, 5 }, { 2, 1 }, { 0, 6 }, { 0, 7 }, { 8, 1 }, { 4, 1 }, { 2, 1 }, { 0, 8 }, { 0, 9 }, { 2, 1 }, { 0, 10 }, { 0, 11 }, { 4, 1 }, { 2, 1 }, { 0, 12 }, { 0, 13 }, { 2, 1 }, { 0, 14 }, { 0, 15 } } }; _llCache = new HuffmanListNode[_codeTables.Length]; _llCacheMaxBits = new int[_codeTables.Length]; LIN_BITS = new int[32] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 8, 10, 13, 4, 5, 6, 7, 8, 9, 11, 13 }; _floatLookup = new float[8207]; for (int i = 0; i < 8207; i++) { _floatLookup[i] = (float)Math.Pow(i, 1.3333333333333333); } } internal static void Decode(BitReservoir br, int table, out float x, out float y) { if (table == 0 || table == 4 || table == 14) { x = (y = 0f); return; } byte num = DecodeSymbol(br, table); int num2 = num >> 4; int num3 = num & 0xF; int num4 = LIN_BITS[table]; if (num4 > 0 && num2 == 15) { num2 += br.GetBits(num4); } if (num2 != 0 && br.Get1Bit() != 0) { x = 0f - _floatLookup[num2]; } else { x = _floatLookup[num2]; } if (num4 > 0 && num3 == 15) { num3 += br.GetBits(num4); } if (num3 != 0 && br.Get1Bit() != 0) { y = 0f - _floatLookup[num3]; } else { y = _floatLookup[num3]; } } internal static void Decode(BitReservoir br, int table, out float x, out float y, out float v, out float w) { byte num = DecodeSymbol(br, table); v = (w = (x = (y = 0f))); if ((num & 8u) != 0) { if (br.Get1Bit() == 1) { v = 0f - _floatLookup[1]; } else { v = _floatLookup[1]; } } if ((num & 4u) != 0) { if (br.Get1Bit() == 1) { w = 0f - _floatLookup[1]; } else { w = _floatLookup[1]; } } if ((num & 2u) != 0) { if (br.Get1Bit() == 1) { x = 0f - _floatLookup[1]; } else { x = _floatLookup[1]; } } if (((uint)num & (true ? 1u : 0u)) != 0) { if (br.Get1Bit() == 1) { y = 0f - _floatLookup[1]; } else { y = _floatLookup[1]; } } } private static byte DecodeSymbol(BitReservoir br, int table) { int maxBits; HuffmanListNode huffmanListNode = GetNode(table, out maxBits); int readCount; int num = br.TryPeekBits(maxBits, out readCount); if (readCount < maxBits) { num <<= maxBits - readCount; } while (huffmanListNode != null && huffmanListNode.Length <= readCount) { if ((num & huffmanListNode.Mask) == huffmanListNode.Bits) { br.SkipBits(huffmanListNode.Length); break; } huffmanListNode = huffmanListNode.Next; } if (huffmanListNode != null && huffmanListNode.Length <= readCount) { return huffmanListNode.Value; } return 0; } private static HuffmanListNode GetNode(int table, out int maxBits) { int num = table; if (num > 16) { num = ((num > 31) ? (num - 17) : ((num < 24) ? 13 : 14)); } else { if (num > 13) { num--; } if (num > 3) { num--; } num--; } if (_llCache[num] == null) { _llCache[num] = InitTable(_codeTables[num], out maxBits); _llCacheMaxBits[num] = maxBits; } else { maxBits = _llCacheMaxBits[num]; } return _llCache[num]; } private static HuffmanListNode InitTable(byte[,] tree, out int maxBits) { List<byte> list = new List<byte>(); List<int> list2 = new List<int>(); List<int> list3 = new List<int>(); int length = tree.GetLength(0); for (int i = 0; i < length; i++) { if (tree[i, 0] == 0) { int num = 0; int item = 0; int num2 = i; do { num2 = FindPreviousNode(tree, num2, out var bit); num |= bit << item++; } while (num2 > 0); list.Add(tree[i, 1]); list2.Add(item); list3.Add(num); } } return BuildLinkedList(list, list2, list3, out maxBits); } private static int FindPreviousNode(byte[,] tree, int idx, out int bit) { for (int num = idx - 1; num >= 0; num--) { if (tree[num, 0] != 0) { for (int i = 0; i < 2; i++) { if (num + tree[num, i] != idx) { continue; } if (tree[num, i] >= 250) { int result = FindPreviousNode(tree, num, out bit); if (bit != i) { throw new InvalidOperationException(); } return result; } bit = i; return num; } } } throw new InvalidOperationException(); } private static HuffmanListNode BuildLinkedList(List<byte> values, List<int> lengthList, List<int> codeList, out int maxBits) { HuffmanListNode[] array = new HuffmanListNode[lengthList.Count]; maxBits = lengthList.Max(); for (int j = 0; j < array.Length; j++) { int num = maxBits - lengthList[j]; array[j] = new HuffmanListNode { Value = values[j], Length = lengthList[j], Bits = codeList[j] << num, Mask = (1 << lengthList[j]) - 1 << num }; } Array.Sort(array, (HuffmanListNode i1, HuffmanListNode i2) => i1.Length - i2.Length); for (int k = 1; k < array.Length && array[k].Length < 99999; k++) { array[k - 1].Next = array[k]; } return array[0]; } } internal class ID3Frame : FrameBase { private int _version; internal int Version { get { if (_version == 0) { return 1; } return _version; } } internal static ID3Frame TrySync(uint syncMark) { if ((syncMark & 0xFFFFFF00u) == 1229206272) { return new ID3Frame { _version = 2 }; } if ((syncMark & 0xFFFFFF00u) == 1413564160) { if ((syncMark & 0xFF) == 43) { return new ID3Frame { _version = 1 }; } return new ID3Frame { _version = 0 }; } return null; } private ID3Frame() { } protected override int Validate() { switch (_version) { case 2: { byte[] array = new byte[7]; if (Read(3, array) == 7) { byte b; switch (array[0]) { case 2: b = 63; break; case 3: b = 31; break; case 4: b = 15; break; default: return -1; } int num = (array[3] << 21) | (array[4] << 14) | (array[5] << 7) | array[6]; if (((array[2] & b) | (array[3] & 0x80) | (array[4] & 0x80) | (array[5] & 0x80) | (array[6] & 0x80)) == 0 && array[1] != byte.MaxValue) { return num + 10; } } break; } case 1: return 355; case 0: return 128; } return -1; } internal override void Parse() { switch (_version) { case 2: ParseV2(); break; case 1: ParseV1Enh(); break; case 0: ParseV1(3); break; } } private void ParseV1(int offset) { } private void ParseV1Enh() { ParseV1(230); } private void ParseV2() { } internal void Merge(ID3Frame newFrame) { } } internal abstract class LayerDecoderBase { protected const int SBLIMIT = 32; private const float INV_SQRT_2 = 0.70710677f; private static float[] DEWINDOW_TABLE = new float[512] { 0f, -1.5259E-05f, -1.5259E-05f, -1.5259E-05f, -1.5259E-05f, -1.5259E-05f, -1.5259E-05f, -3.0518E-05f, -3.0518E-05f, -3.0518E-05f, -3.0518E-05f, -4.5776E-05f, -4.5776E-05f, -6.1035E-05f, -6.1035E-05f, -7.6294E-05f, -7.6294E-05f, -9.1553E-05f, -0.000106812f, -0.000106812f, -0.00012207f, -0.000137329f, -0.000152588f, -0.000167847f, -0.000198364f, -0.000213623f, -0.000244141f, -0.000259399f, -0.000289917f, -0.000320435f, -0.000366211f, -0.000396729f, -0.000442505f, -0.000473022f, -0.000534058f, -0.000579834f, -0.00062561f, -0.000686646f, -0.000747681f, -0.000808716f, -0.00088501f, -0.000961304f, -0.001037598f, -0.001113892f, -0.001205444f, -0.001296997f, -0.00138855f, -0.001480103f, -0.001586914f, -0.001693726f, -0.001785278f, -0.001907349f, -0.00201416f, -0.002120972f, -0.002243042f, -0.002349854f, -0.002456665f, -0.002578735f, -0.002685547f, -0.002792358f, -0.00289917f, -0.002990723f, -0.003082275f, -0.003173828f, 0.003250122f, 0.003326416f, 0.003387451f, 0.003433228f, 0.003463745f, 0.003479004f, 0.003479004f, 0.003463745f, 0.003417969f, 0.003372192f, 0.00328064f, 0.003173828f, 0.003051758f, 0.002883911f, 0.002700806f, 0.002487183f, 0.002227783f, 0.001937866f, 0.001617432f, 0.001266479f, 0.000869751f, 0.000442505f, -3.0518E-05f, -0.000549316f, -0.001098633f, -0.001693726f, -0.002334595f, -0.003005981f, -0.003723145f, -0.004486084f, -0.0052948f, -0.006118774f, -0.007003784f, -0.007919312f, -0.008865356f, -0.009841919f, -0.010848999f, -0.011886597f, -0.012939453f, -0.014022827f, -0.01512146f, -0.016235352f, -0.017349243f, -0.018463135f, -0.019577026f, -0.020690918f, -0.02178955f, -0.022857666f, -0.023910522f, -0.024932861f, -0.025909424f, -0.02684021f, -0.02772522f, -0.028533936f, -0.029281616f, -0.029937744f, -0.030532837f, -0.03100586f, -0.03138733f, -0.031661987f, -0.031814575f, -0.031845093f, -0.03173828f, -0.03147888f, 0.031082153f, 0.030517578f, 0.029785156f, 0.028884888f, 0.027801514f, 0.026535034f, 0.02508545f, 0.023422241f, 0.021575928f, 0.01953125f, 0.01725769f, 0.014801025f, 0.012115479f, 0.009231567f, 0.006134033f, 0.002822876f, -0.000686646f, -0.004394531f, -0.00831604f, -0.012420654f, -0.016708374f, -0.0211792f, -0.025817871f, -0.03060913f, -0.03555298f, -0.040634155f, -0.045837402f, -0.051132202f, -0.056533813f, -0.06199646f, -0.06752014f, -0.07305908f, -0.07862854f, -0.08418274f, -0.08970642f, -0.09516907f, -0.10054016f, -0.1058197f, -0.110946655f, -0.11592102f, -0.12069702f, -0.1252594f, -0.12956238f, -0.1335907f, -0.13729858f, -0.14067078f, -0.14367676f, -0.1462555f, -0.14842224f, -0.15011597f, -0.15130615f, -0.15196228f, -0.15206909f, -0.15159607f, -0.15049744f, -0.1487732f, -0.1463623f, -0.14326477f, -0.13945007f, -0.1348877f, -0.12957764f, -0.12347412f, -0.11657715f, -0.1088562f, 0.10031128f, 0.090927124f, 0.08068848f, 0.06959534f, 0.057617188f, 0.044784546f, 0.031082153f, 0.01651001f, 0.001068115f, -0.015228271f, -0.03237915f, -0.050354004f, -0.06916809f, -0.088775635f, -0.10916138f, -0.13031006f, -0.15220642f, -0.17478943f, -0.19805908f, -0.22198486f, -0.24650574f, -0.2715912f, -0.2972107f, -0.32331848f, -0.34986877f, -0.37680054f, -0.40408325f, -0.43165588f, -0.45947266f, -0.48747253f, -0.51560974f, -0.54382324f, -0.57203674f, -0.6002197f, -0.6282959f, -0.6562195f, -0.6839142f, -0.71131897f, -0.7383728f, -0.7650299f, -0.791214f, -0.816864f, -0.84194946f, -0.8663635f, -0.89009094f, -0.9130554f, -0.9351959f, -0.95648193f, -0.9768524f, -0.99624634f, -1.0146179f, -1.0319366f, -1.0481567f, -1.0632172f, -1.0771179f, -1.0897827f, -1.1012115f, -1.1113739f, -1.120224f, -1.1277466f, -1.1339264f, -1.1387634f, -1.1422119f, -1.1442871f, 1.144989f, 1.1442871f, 1.1422119f, 1.1387634f, 1.1339264f, 1.1277466f, 1.120224f, 1.1113739f, 1.1012115f, 1.0897827f, 1.0771179f, 1.0632172f, 1.0481567f, 1.0319366f, 1.0146179f, 0.99624634f, 0.9768524f, 0.95648193f, 0.9351959f, 0.9130554f, 0.89009094f, 0.8663635f, 0.84194946f, 0.816864f, 0.791214f, 0.7650299f, 0.7383728f, 0.71131897f, 0.6839142f, 0.6562195f, 0.6282959f, 0.6002197f, 0.57203674f, 0.54382324f, 0.51560974f, 0.48747253f, 0.45947266f, 0.43165588f, 0.40408325f, 0.37680054f, 0.34986877f, 0.32331848f, 0.2972107f, 0.2715912f, 0.24650574f, 0.22198486f, 0.19805908f, 0.17478943f, 0.15220642f, 0.13031006f, 0.10916138f, 0.088775635f, 0.06916809f, 0.050354004f, 0.03237915f, 0.015228271f, -0.001068115f, -0.01651001f, -0.031082153f, -0.044784546f, -0.057617188f, -0.06959534f, -0.08068848f, -0.090927124f, 0.10031128f, 0.1088562f, 0.11657715f, 0.12347412f, 0.12957764f, 0.1348877f, 0.13945007f, 0.14326477f, 0.1463623f, 0.1487732f, 0.15049744f, 0.15159607f, 0.15206909f, 0.15196228f, 0.15130615f, 0.15011597f, 0.14842224f, 0.1462555f, 0.14367676f, 0.14067078f, 0.13729858f, 0.1335907f, 0.12956238f, 0.1252594f, 0.12069702f, 0.11592102f, 0.110946655f, 0.1058197f, 0.10054016f, 0.09516907f, 0.08970642f, 0.08418274f, 0.07862854f, 0.07305908f, 0.06752014f, 0.06199646f, 0.056533813f, 0.051132202f, 0.045837402f, 0.040634155f, 0.03555298f, 0.03060913f, 0.025817871f, 0.0211792f, 0.016708374f, 0.012420654f, 0.00831604f, 0.004394531f, 0.000686646f, -0.002822876f, -0.006134033f, -0.009231567f, -0.012115479f, -0.014801025f, -0.01725769f, -0.01953125f, -0.021575928f, -0.023422241f, -0.02508545f, -0.026535034f, -0.027801514f, -0.028884888f, -0.029785156f, -0.030517578f, 0.031082153f, 0.03147888f, 0.03173828f, 0.031845093f, 0.031814575f, 0.031661987f, 0.03138733f, 0.03100586f, 0.030532837f, 0.029937744f, 0.029281616f, 0.028533936f, 0.02772522f, 0.02684021f, 0.025909424f, 0.024932861f, 0.023910522f, 0.022857666f, 0.02178955f, 0.020690918f, 0.019577026f, 0.018463135f, 0.017349243f, 0.016235352f, 0.01512146f, 0.014022827f, 0.012939453f, 0.011886597f, 0.010848999f, 0.009841919f, 0.008865356f, 0.007919312f, 0.007003784f, 0.006118774f, 0.0052948f, 0.004486084f, 0.003723145f, 0.003005981f, 0.002334595f, 0.001693726f, 0.001098633f, 0.000549316f, 3.0518E-05f, -0.000442505f, -0.000869751f, -0.001266479f, -0.001617432f, -0.001937866f, -0.002227783f, -0.002487183f, -0.002700806f, -0.002883911f, -0.003051758f, -0.003173828f, -0.00328064f, -0.003372192f, -0.003417969f, -0.003463745f, -0.003479004f, -0.003479004f, -0.003463745f, -0.003433228f, -0.003387451f, -0.003326416f, 0.003250122f, 0.003173828f, 0.003082275f, 0.002990723f, 0.00289917f, 0.002792358f, 0.002685547f, 0.002578735f, 0.002456665f, 0.002349854f, 0.002243042f, 0.002120972f, 0.00201416f, 0.001907349f, 0.001785278f, 0.001693726f, 0.001586914f, 0.001480103f, 0.00138855f, 0.001296997f, 0.001205444f, 0.001113892f, 0.001037598f, 0.000961304f, 0.00088501f, 0.000808716f, 0.000747681f, 0.000686646f, 0.00062561f, 0.000579834f, 0.000534058f, 0.000473022f, 0.000442505f, 0.000396729f, 0.000366211f, 0.000320435f, 0.000289917f, 0.000259399f, 0.000244141f, 0.000213623f, 0.000198364f, 0.000167847f, 0.000152588f, 0.000137329f, 0.00012207f, 0.000106812f, 0.000106812f, 9.1553E-05f, 7.6294E-05f, 7.6294E-05f, 6.1035E-05f, 6.1035E-05f, 4.5776E-05f, 4.5776E-05f, 3.0518E-05f, 3.0518E-05f, 3.0518E-05f, 3.0518E-05f, 1.5259E-05f, 1.5259E-05f, 1.5259E-05f, 1.5259E-05f, 1.5259E-05f, 1.5259E-05f }; private static float[] SYNTH_COS64_TABLE = new float[31] { 0.500603f, 0.5024193f, 0.50547093f, 0.5097956f, 0.5154473f, 0.5224986f, 0.5310426f, 0.5411961f, 0.5531039f, 0.56694406f, 0.582935f, 0.6013449f, 0.6225041f, 0.6468218f, 0.6748083f, 0.70710677f, 0.7445363f, 0.7881546f, 0.8393496f, 0.8999762f, 0.9725682f, 1.0606776f, 1.1694399f, 1.306563f, 1.4841646f, 1.7224472f, 2.057781f, 2.5629156f, 3.4076085f, 5.1011486f, 10.190008f }; private List<float[]> _synBuf = new List<float[]>(2); private List<int> _bufOffset = new List<int>(2); private float[] _eq; private float[] ippuv = new float[512]; private float[] ei32 = new float[16]; private float[] eo32 = new float[16]; private float[] oi32 = new float[16]; private float[] oo32 = new float[16]; private float[] ei16 = new float[8]; private float[] eo16 = new float[8]; private float[] oi16 = new float[8]; private float[] oo16 = new float[8]; private float[] ei8 = new float[4]; private float[] tmp8 = new float[6]; private float[] oi8 = new float[4]; private float[] oo8 = new float[4]; internal StereoMode StereoMode { get; set; } internal LayerDecoderBase() { StereoMode = StereoMode.Both; } internal abstract int DecodeFrame(IMpegFrame frame, float[] ch0, float[] ch1); internal void SetEQ(float[] eq) { if (eq == null || eq.Length == 32) { _eq = eq; } } internal virtual void ResetForSeek() { _synBuf.Clear(); _bufOffset.Clear(); } protected void InversePolyPhase(int channel, float[] data) { GetBufAndOffset(channel, out var synBuf, out var k); if (_eq != null) { for (int i = 0; i < 32; i++) { data[i] *= _eq[i]; } } DCT32(data, synBuf, k); BuildUVec(ippuv, synBuf, k); DewindowOutput(ippuv, data); } private void GetBufAndOffset(int channel, out float[] synBuf, out int k) { while (_synBuf.Count <= channel) { _synBuf.Add(new float[1024]); } while (_bufOffset.Count <= channel) { _bufOffset.Add(0); } synBuf = _synBuf[channel]; k = _bufOffset[channel]; k = (k - 32) & 0x1FF; _bufOffset[channel] = k; } private void DCT32(float[] _in, float[] _out, int k) { for (int i = 0; i < 16; i++) { ei32[i] = _in[i] + _in[31 - i]; oi32[i] = (_in[i] - _in[31 - i]) * SYNTH_COS64_TABLE[2 * i]; } DCT16(ei32, eo32); DCT16(oi32, oo32); for (int i = 0; i < 15; i++) { _out[2 * i + k] = eo32[i]; _out[2 * i + 1 + k] = oo32[i] + oo32[i + 1]; } _out[30 + k] = eo32[15]; _out[31 + k] = oo32[15]; } private void DCT16(float[] _in, float[] _out) { float num = _in[0]; float num2 = _in[15]; ei16[0] = num + num2; oi16[0] = (num - num2) * SYNTH_COS64_TABLE[1]; num = _in[1]; num2 = _in[14]; ei16[1] = num + num2; oi16[1] = (num - num2) * SYNTH_COS64_TABLE[5]; num = _in[2]; num2 = _in[13]; ei16[2] = num + num2; oi16[2] = (num - num2) * SYNTH_COS64_TABLE[9]; num = _in[3]; num2 = _in[12]; ei16[3] = num + num2; oi16[3] = (num - num2) * SYNTH_COS64_TABLE[13]; num = _in[4]; num2 = _in[11]; ei16[4] = num + num2; oi16[4] = (num - num2) * SYNTH_COS64_TABLE[17]; num = _in[5]; num2 = _in[10]; ei16[5] = num + num2; oi16[5] = (num - num2) * SYNTH_COS64_TABLE[21]; num = _in[6]; num2 = _in[9]; ei16[6] = num + num2; oi16[6] = (num - num2) * SYNTH_COS64_TABLE[25]; num = _in[7]; num2 = _in[8]; ei16[7] = num + num2; oi16[7] = (num - num2) * SYNTH_COS64_TABLE[29]; DCT8(ei16, eo16); DCT8(oi16, oo16); _out[0] = eo16[0]; _out[1] = oo16[0] + oo16[1]; _out[2] = eo16[1]; _out[3] = oo16[1] + oo16[2]; _out[4] = eo16[2]; _out[5] = oo16[2] + oo16[3]; _out[6] = eo16[3]; _out[7] = oo16[3] + oo16[4]; _out[8] = eo16[4]; _out[9] = oo16[4] + oo16[5]; _out[10] = eo16[5]; _out[11] = oo16[5] + oo16[6]; _out[12] = eo16[6]; _out[13] = oo16[6] + oo16[7]; _out[14] = eo16[7]; _out[15] = oo16[7]; } private void DCT8(float[] _in, float[] _out) { ei8[0] = _in[0] + _in[7]; ei8[1] = _in[3] + _in[4]; ei8[2] = _in[1] + _in[6]; ei8[3] = _in[2] + _in[5]; tmp8[0] = ei8[0] + ei8[1]; tmp8[1] = ei8[2] + ei8[3]; tmp8[2] = (ei8[0] - ei8[1]) * SYNTH_COS64_TABLE[7]; tmp8[3] = (ei8[2] - ei8[3]) * SYNTH_COS64_TABLE[23]; tmp8[4] = (tmp8[2] - tmp8[3]) * 0.70710677f; _out[0] = tmp8[0] + tmp8[1]; _out[2] = tmp8[2] + tmp8[3] + tmp8[4]; _out[4] = (tmp8[0] - tmp8[1]) * 0.70710677f; _out[6] = tmp8[4]; oi8[0] = (_in[0] - _in[7]) * SYNTH_COS64_TABLE[3]; oi8[1] = (_in[1] - _in[6]) * SYNTH_COS64_TABLE[11]; oi8[2] = (_in[2] - _in[5]) * SYNTH_COS64_TABLE[19]; oi8[3] = (_in[3] - _in[4]) * SYNTH_COS64_TABLE[27]; tmp8[0] = oi8[0] + oi8[3]; tmp8[1] = oi8[1] + oi8[2]; tmp8[2] = (oi8[0] - oi8[3]) * SYNTH_COS64_TABLE[7]; tmp8[3] = (oi8[1] - oi8[2]) * SYNTH_COS64_TABLE[23]; tmp8[4] = tmp8[2] + tmp8[3]; tmp8[5] = (tmp8[2] - tmp8[3]) * 0.70710677f; oo8[0] = tmp8[0] + tmp8[1]; oo8[1] = tmp8[4] + tmp8[5]; oo8[2] = (tmp8[0] - tmp8[1]) * 0.70710677f; oo8[3] = tmp8[5]; _out[1] = oo8[0] + oo8[1]; _out[3] = oo8[1] + oo8[2]; _out[5] = oo8[2] + oo8[3]; _out[7] = oo8[3]; } private void BuildUVec(float[] u_vec, float[] cur_synbuf, int k) { int num = 0; for (int i = 0; i < 8; i++) { for (int j = 0; j < 16; j++) { u_vec[num + j] = cur_synbuf[k + j + 16]; u_vec[num + j + 17] = 0f - cur_synbuf[k + 31 - j]; } k = (k + 32) & 0x1FF; for (int j = 0; j < 16; j++) { u_vec[num + j + 32] = 0f - cur_synbuf[k + 16 - j]; u_vec[num + j + 48] = 0f - cur_synbuf[k + j]; } u_vec[num + 16] = 0f; k = (k + 32) & 0x1FF; num += 64; } } private void DewindowOutput(float[] u_vec, float[] samples) { for (int i = 0; i < 512; i++) { u_vec[i] *= DEWINDOW_TABLE[i]; } for (int j = 0; j < 32; j++) { float num = u_vec[j]; num += u_vec[j + 32]; num += u_vec[j + 64]; num += u_vec[j + 96]; num += u_vec[j + 128]; num += u_vec[j + 160]; num += u_vec[j + 192]; num += u_vec[j + 224]; num += u_vec[j + 256]; num += u_vec[j + 288]; num += u_vec[j + 320]; num += u_vec[j + 352]; num += u_vec[j + 384]; num += u_vec[j + 416]; num += u_vec[j + 448]; num += u_vec[j + 480]; u_vec[j] = num; } for (int k = 0; k < 32; k++) { samples[k] = u_vec[k]; } } } internal class LayerIDecoder : LayerIIDecoderBase { private static readonly int[] _rateTable = new int[32]; private static readonly int[][] _allocLookupTable = new int[1][] { new int[17] { 4, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 } }; internal static bool GetCRC(MpegFrame frame, ref uint crc) { return LayerIIDecoderBase.GetCRC(frame, _rateTable, _allocLookupTable, readScfsiBits: false, ref crc); } internal LayerIDecoder() : base(_allocLookupTable, 1) { } protected override int[] GetRateTable(IMpegFrame frame) { return _rateTable; } protected override void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels) { } } internal class LayerIIDecoder : LayerIIDecoderBase { private static readonly int[][] _rateLookupTable = new int[5][] { new int[27] { 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, new int[30] { 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, new int[8] { 4, 4, 5, 5, 5, 5, 5, 5 }, new int[12] { 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }, new int[30] { 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 } }; private static readonly int[][] _allocLookupTable = new int[8][] { new int[5] { 2, 0, -5, -7, 16 }, new int[9] { 3, 0, -5, -7, 3, -10, 4, 5, 16 }, new int[17] { 4, 0, -5, -7, 3, -10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16 }, new int[17] { 4, 0, -5, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, new int[17] { 4, 0, -5, -7, -10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, new int[9] { 3, 0, -5, -7, -10, 4, 5, 6, 9 }, new int[17] { 4, 0, -5, -7, 3, -10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, new int[5] { 2, 0, -5, -7, 3 } }; internal static bool GetCRC(MpegFrame frame, ref uint crc) { return LayerIIDecoderBase.GetCRC(frame, SelectTable(frame), _allocLookupTable, readScfsiBits: true, ref crc); } private static int[] SelectTable(IMpegFrame frame) { int num = frame.BitRate / ((frame.ChannelMode == MpegChannelMode.Mono) ? 1 : 2) / 1000; if (frame.Version == MpegVersion.Version1) { if ((num >= 56 && num <= 80) || (frame.SampleRate == 48000 && num >= 56)) { return _rateLookupTable[0]; } if (frame.SampleRate != 48000 && num >= 96) { return _rateLookupTable[1]; } if (frame.SampleRate != 32000 && num <= 48) { return _rateLookupTable[2]; } return _rateLookupTable[3]; } return _rateLookupTable[4]; } internal LayerIIDecoder() : base(_allocLookupTable, 3) { } protected override int[] GetRateTable(IMpegFrame frame) { return SelectTable(frame); } protected override void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels) { for (int i = 0; i < 30; i++) { for (int j = 0; j < channels; j++) { if (scfsi[j][i] == 2) { scfsi[j][i] = frame.ReadBits(2); } } } } } internal abstract class LayerIIDecoderBase : LayerDecoderBase { protected const int SSLIMIT = 12; private static readonly float[] _groupedC = new float[5] { 0f, 0f, 1.3333334f, 1.6f, 1.7777778f }; private static readonly float[] _groupedD = new float[5] { 0f, 0f, -0.5f, -0.5f, -0.5f }; private static readonly float[] _C = new float[17] { 0f, 0f, 1.3333334f, 1.1428572f, 1.0666667f, 1.032258f, 1.0158731f, 1.007874f, 1.0039216f, 1.0019569f, 1.0009775f, 1.0004885f, 1.0002443f, 1.0001221f, 1.000061f, 1.0000305f, 1.0000153f }; private static readonly float[] _D = new float[17] { 0f, 0f, -0.5f, -0.75f, -0.875f, -0.9375f, -31f / 32f, -63f / 64f, -127f / 128f, -0.99609375f, -0.9980469f, -0.99902344f, -0.9995117f, -0.99975586f, -0.9998779f, -0.99993896f, -0.9999695f }; private static readonly float[] _denormalMultiplier = new float[64] { 2f, 1.587401f, 1.2599211f, 1f, 0.7937005f, 0.62996054f, 0.5f, 0.39685026f, 0.31498027f, 0.25f, 0.19842513f, 0.15749013f, 0.125f, 0.099212565f, 0.07874507f, 0.0625f, 0.049606282f, 0.039372534f, 1f / 32f, 0.024803141f, 0.019686267f, 1f / 64f, 0.012401571f, 0.009843133f, 1f / 128f, 0.0062007853f, 0.0049215667f, 0.00390625f, 0.0031003926f, 0.0024607833f, 0.001953125f, 0.0015501963f, 0.0012303917f, 0.0009765625f, 0.00077509816f, 0.00061519584f, 0.00048828125f, 0.00038754908f, 0.00030759792f, 0.00024414062f, 0.00019377454f, 0.00015379896f, 0.00012207031f, 9.688727E-05f, 7.689948E-05f, 6.1035156E-05f, 4.8443635E-05f, 3.844974E-05f, 3.0517578E-05f, 2.4221818E-05f, 1.922487E-05f, 1.5258789E-05f, 1.2110909E-05f, 9.612435E-06f, 7.6293945E-06f, 6.0554544E-06f, 4.8062175E-06f, 3.8146973E-06f, 3.0277272E-06f, 2.4031087E-06f, 1.9073486E-06f, 1.5138636E-06f, 1.2015544E-06f, 9.536743E-07f }; private int _channels; private int _jsbound; private int _granuleCount; private int[][] _allocLookupTable; private int[][] _scfsi; private int[][] _samples; private int[][][] _scalefac; private float[] _polyPhaseBuf; private int[][] _allocation; protected static bool GetCRC(MpegFrame frame, int[] rateTable, int[][] allocLookupTable, bool readScfsiBits, ref uint crc) { int num = 0; int num2 = rateTable.Length; int num3 = num2; if (frame.ChannelMode == MpegChannelMode.JointStereo) { num3 = frame.ChannelModeExtension * 4 + 4; } int num4 = ((frame.ChannelMode == MpegChannelMode.Mono) ? 1 : 2); int i; for (i = 0; i < num3; i++) { int num5 = allocLookupTable[rateTable[i]][0]; for (int j = 0; j < num4; j++) { int num6 = frame.ReadBits(num5); if (num6 > 0) { num += 2; } MpegFrame.UpdateCRC(num6, num5, ref crc); } } for (; i < num2; i++) { int num7 = allocLookupTable[rateTable[i]][0]; int num8 = frame.ReadBits(num7); if (num8 > 0) { num += num4 * 2; } MpegFrame.UpdateCRC(num8, num7, ref crc); } if (readScfsiBits) { while (num >= 2) { MpegFrame.UpdateCRC(frame.ReadBits(2), 2, ref crc); num -= 2; } } return true; } protected LayerIIDecoderBase(int[][] allocLookupTable, int granuleCount) { _allocLookupTable = allocLookupTable; _granuleCount = granuleCount; _allocation = new int[2][] { new int[32], new int[32] }; _scfsi = new int[2][] { new int[32], new int[32] }; _samples = new int[2][] { new int[384 * _granuleCount], new int[384 * _granuleCount] }; _scalefac = new int[2][][] { new int[3][], new int[3][] }; for (int i = 0; i < 3; i++) { _scalefac[0][i] = new int[32]; _scalefac[1][i] = new int[32]; } _polyPhaseBuf = new float[32]; } internal override int DecodeFrame(IMpegFrame frame, float[] ch0, float[] ch1) { InitFrame(frame); int[] rateTable = GetRateTable(frame); ReadAllocation(frame, rateTable); for (int i = 0; i < _scfsi[0].Length; i++) { _scfsi[0][i] = ((_allocation[0][i] != 0) ? 2 : (-1)); _scfsi[1][i] = ((_allocation[1][i] != 0) ? 2 : (-1)); } ReadScaleFactorSelection(frame, _scfsi, _channels); ReadScaleFactors(frame); ReadSamples(frame); return DecodeSamples(ch0, ch1); } private void InitFrame(IMpegFrame frame) { switch (frame.ChannelMode) { case MpegChannelMode.Mono: _channels = 1; _jsbound = 32; break; case MpegChannelMode.JointStereo: _channels = 2; _jsbound = frame.ChannelModeExtension * 4 + 4; break; default: _channels = 2; _jsbound = 32; break; } } protected abstract int[] GetRateTable(IMpegFrame frame); private void ReadAllocation(IMpegFrame frame, int[] rateTable) { int num = rateTable.Length; if (_jsbound > num) { _jsbound = num; } Array.Clear(_allocation[0], 0, 32); Array.Clear(_allocation[1], 0, 32); int i; for (i = 0; i < _jsbound; i++) { int[] array = _allocLookupTable[rateTable[i]]; int bitCount = array[0]; for (int j = 0; j < _channels; j++) { _allocation[j][i] = array[frame.ReadBits(bitCount) + 1]; } } for (; i < num; i++) { int[] array2 = _allocLookupTable[rateTable[i]]; _allocation[0][i] = (_allocation[1][i] = array2[frame.ReadBits(array2[0]) + 1]); } } protected abstract void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels); private void ReadScaleFactors(IMpegFrame frame) { for (int i = 0; i < 32; i++) { for (int j = 0; j < _channels; j++) { switch (_scfsi[j][i]) { case 0: _scalefac[j][0][i] = frame.ReadBits(6); _scalefac[j][1][i] = frame.ReadBits(6); _scalefac[j][2][i] = frame.ReadBits(6); break; case 1: _scalefac[j][0][i] = (_scalefac[j][1][i] = frame.ReadBits(6)); _scalefac[j][2][i] = frame.ReadBits(6); break; case 2: _scalefac[j][0][i] = (_scalefac[j][1][i] = (_scalefac[j][2][i] = frame.ReadBits(6))); break; case 3: _scalefac[j][0][i] = frame.ReadBits(6); _scalefac[j][1][i] = (_scalefac[j][2][i] = frame.ReadBits(6)); break; default: _scalefac[j][0][i] = 63; _scalefac[j][1][i] = 63; _scalefac[j][2][i] = 63; break; } } } } private void ReadSamples(IMpegFrame frame) { int num = 0; int num2 = 0; while (num < 12) { int num3 = 0; while (num3 < 32) { for (int i = 0; i < _channels; i++) { if (i == 0 || num3 < _jsbound) { int num4 = _allocation[i][num3]; if (num4 != 0) { if (num4 < 0) { int num5 = frame.ReadBits(-num4); int num6 = (1 << -num4 / 2 + -num4 % 2 - 1) + 1; _samples[i][num2] = num5 % num6; num5 /= num6; _samples[i][num2 + 32] = num5 % num6; _samples[i][num2 + 64] = num5 / num6; } else { for (int j = 0; j < _granuleCount; j++) { _samples[i][num2 + 32 * j] = frame.ReadBits(num4); } } } else { for (int k = 0; k < _granuleCount; k++) { _samples[i][num2 + 32 * k] = 0; } } } else { for (int l = 0; l < _granuleCount; l++) { _samples[1][num2 + 32 * l] = _samples[0][num2 + 32 * l]; } } } num3++; num2++; } num++; num2 += 32 * (_granuleCount - 1); } } private int DecodeSamples(float[] ch0, float[] ch1) { float[][] array = new float[2][]; int num = 0; int num2 = _channels - 1; if (_channels == 1 || base.StereoMode == StereoMode.LeftOnly) { array[0] = ch0; num2 = 0; } else if (base.StereoMode == StereoMode.RightOnly) { array[1] = ch0; num = 1; } else { array[0] = ch0; array[1] = ch1; } int num3 = 0; for (int i = num; i <= num2; i++) { num3 = 0; for (int j = 0; j < _granuleCount; j++) { for (int k = 0; k < 12; k++) { int num4 = 0; while (num4 < 32) { int num5 = _allocation[i][num4]; if (num5 != 0) { float[] array2; float[] array3; if (num5 < 0) { num5 = -num5 / 2 + -num5 % 2 - 1; array2 = _groupedC; array3 = _groupedD; } else { array2 = _C; array3 = _D; } _polyPhaseBuf[num4] = array2[num5] * ((float)(_samples[i][num3] << 16 - num5) / 32768f + array3[num5]) * _denormalMultiplier[_scalefac[i][j][num4]]; } else { _polyPhaseBuf[num4] = 0f; } num4++; num3++; } InversePolyPhase(i, _polyPhaseBuf); Array.Copy(_polyPhaseBuf, 0, array[i], num3 - 32, 32); } } } if (_channels == 2 && base.StereoMode == StereoMode.DownmixToMono) { for (int l = 0; l < num3; l++) { ch0[l] = (ch0[l] + ch1[l]) / 2f; } } return num3; } } internal sealed class LayerIIIDecoder : LayerDecoderBase { private class HybridMDCT { private const float PI = MathF.PI; private static float[][] _swin; private static float[] icos72_table; private List<float[]> _prevBlock; private List<float[]> _nextBlock; private float[] _imdctTemp = new float[18]; private float[] _imdctResult = new float[36]; private const float sqrt32 = 0.8660254f; static HybridMDCT() { icos72_table = new float[35] { 0.50047636f, 0.5019099f, 0.5043145f, 0.5077133f, 0.51213974f, 0.5176381f, 0.5242646f, 0.5320889f, 0.5411961f, 0.55168897f, 0.56369096f, 0.57735026f, 0.59284455f, 0.61038727f, 0.6302362f, 0.65270364f, 0.67817086f, 0.70710677f, 0.7400936f, 0.7778619f, 0.8213398f, 0.8717234f, 0.9305795f, 1f, 1.0828403f, 1.1831008f, 1.306563f, 1.4619021f, 1.6627548f, 1.9318516f, 2.3101132f, 2.8793852f, 3.830649f, 5.7368565f, 11.462792f }; _swin = new float[4][] { new float[36], new float[36], new float[36], new float[36] }; for (int i = 0; i < 36; i++) { _swin[0][i] = (float)Math.Sin(0.0872664675116539 * ((double)i + 0.5)); } for (int i = 0; i < 18; i++) { _swin[1][i] = (float)Math.Sin(0.0872664675116539 * ((double)i + 0.5)); } for (int i = 18; i < 24; i++) { _swin[1][i] = 1f; } for (int i = 24; i < 30; i++) { _swin[1][i] = (float)Math.Sin(0.2617993950843811 * ((double)i + 0.5 - 18.0)); } for (int i = 30; i < 36; i++) { _swin[1][i] = 0f; } for (int i = 0; i < 6; i++) { _swin[3][i] = 0f; } for (int i = 6; i < 12; i++) { _swin[3][i] = (float)Math.Sin(0.2617993950843811 * ((double)i + 0.5 - 6.0)); } for (int i = 12; i < 18; i++) { _swin[3][i] = 1f; } for (int i = 18; i < 36; i++) { _swin[3][i] = (float)Math.Sin(0.0872664675116539 * ((double)i + 0.5)); } for (int i = 0; i < 12; i++) { _swin[2][i] = (float)Math.Sin(0.2617993950843811 * ((double)i + 0.5)); } for (int i = 12; i < 36; i++) { _swin[2][i] = 0f; } } internal HybridMDCT() { _prevBlock = new List<float[]>(); _nextBlock = new List<float[]>(); } internal void Reset() { _prevBlock.Clear(); _nextBlock.Clear(); } private void GetPrevBlock(int channel, out float[] prevBlock, out float[] nextBlock) { while (_prevBlock.Count <= channel) { _prevBlock.Add(new float[576]); } while (_nextBlock.Count <= channel) { _nextBlock.Add(new float[576]); } prevBlock = _prevBlock[channel]; nextBlock = _nextBlock[channel]; _nextBlock[channel] = prevBlock; _prevBlock[channel] = nextBlock; } internal void Apply(float[] fsIn, int channel, int blockType, bool doMixed) { GetPrevBlock(channel, out var prevBlock, out var nextBlock); int sbStart = 0; if (doMixed) { LongImpl(fsIn, 0, 2, nextBlock, 0); sbStart = 2; } if (blockType == 2) { ShortImpl(fsIn, sbStart, nextBlock); } else { LongImpl(fsIn, sbStart, 32, nextBlock, blockType); } for (int i = 0; i < 576; i++) { fsIn[i] += prevBlock[i]; } } private void LongImpl(float[] fsIn, int sbStart, int sbLimit, float[] nextblck, int blockType) { int i = sbStart; int num = sbStart * 18; for (; i < sbLimit; i++) { Array.Copy(fsIn, num, _imdctTemp, 0, 18); LongIMDCT(_imdctTemp, _imdctResult); float[] array = _swin[blockType]; int j; for (j = 0; j < 18; j++) { fsIn[num++] = _imdctResult[j] * array[j]; } num -= 18; for (; j < 36; j++) { nextblck[num++] = _imdctResult[j] * array[j]; } } } private static void LongIMDCT(float[] invec, float[] outvec) { float[] array = new float[17]; float[] array2 = new float[18]; float[] array3 = new float[9]; float[] array4 = new float[9]; float[] array5 = new float[9]; float[] array6 = new float[9]; int i; for (i = 0; i < 17; i++) { array[i] = invec[i] + invec[i + 1]; } array3[0] = invec[0]; array4[0] = array[0]; int num = 0; i = 1; while (i < 9) { array3[i] = array[num + 1]; array4[i] = array[num] + array[num + 2]; i++; num += 2; } imdct_9pt(array3, array5); imdct_9pt(array4, array6); for (i = 0; i < 9; i++) { array6[i] *= ICOS36_A(i); array2[i] = (array5[i] + array6[i]) * ICOS72_A(i); } for (; i < 18; i++) { array2[i] = (array5[17 - i] - array6[17 - i]) * ICOS72_A(i); } outvec[0] = array2[9]; outvec[1] = array2[10]; outvec[2] = array2[11]; outvec[3] = array2[12]; outvec[4] = array2[13]; outvec[5] = array2[14]; outvec[6] = array2[15]; outvec[7] = array2[16]; outvec[8] = array2[17]; outvec[9] = 0f - array2[17]; outvec[10] = 0f - array2[16]; outvec[11] = 0f - array2[15]; outvec[12] = 0f - array2[14]; outvec[13] = 0f - array2[13]; outvec[14] = 0f - array2[12]; outvec[15] = 0f - array2[11]; outvec[16] = 0f - array2[10]; outvec[17] = 0f - array2[9]; outvec[35] = (outvec[18] = 0f - array2[8]); outvec[34] = (outvec[19] = 0f - array2[7]); outvec[33] = (outvec[20] = 0f - array2[6]); outvec[32] = (outvec[21] = 0f - array2[5]); outvec[31] = (outvec[22] = 0f - array2[4]); outvec[30] = (outvec[23] = 0f - array2[3]); outvec[29] = (outvec[24] = 0f - array2[2]); outvec[28] = (outvec[25] = 0f - array2[1]); outvec[27] = (outvec[26] = 0f - array2[0]); } private static float ICOS72_A(int i) { return icos72_table[2 * i]; } private static float ICOS36_A(int i) { return icos72_table[4 * i + 1]; } private static void imdct_9pt(float[] invec, float[] outvec) { float[] array = new float[5]; float[] array2 = new float[4]; float num = invec[6] / 2f + invec[0]; float num2 = invec[0] - invec[6]; float num3 = invec[2] - invec[4] - invec[8]; array[0] = num + invec[2] * 0.9396926f + invec[4] * 0.76604444f + invec[8] * 0.17364818f; array[1] = num3 / 2f + num2; array[2] = num - invec[2] * 0.17364818f - invec[4] * 0.9396926f + invec[8] * 0.76604444f; array[3] = num - invec[2] * 0.76604444f + invec[4] * 0.17364818f - invec[8] * 0.9396926f; array[4] = num2 - num3; float num4 = invec[1] + invec[3]; float num5 = invec[3] + invec[5]; num = (invec[5] + invec[7]) * 0.5f + invec[1]; array2[0] = num + num4 * 0.9396926f + num5 * 0.76604444f; array2[1] = (invec[1] - invec[5]) * 1.5f - invec[7]; array2[2] = num - num4 * 0.17364818f - num5 * 0.9396926f; array2[3] = num - num4 * 0.76604444f + num5 * 0.17364818f; array2[0] += invec[7] * 0.17364818f; array2[1] -= invec[7] * 0.5f; array2[2] += invec[7] * 0.76604444f; array2[3] -= invec[7] * 0.9396926f; array2[0] *= 0.5077133f; array2[1] *= 0.57735026f; array2[2] *= 0.7778619f; array2[3] *= 1.4619021f; for (int i = 0; i < 4; i++) { outvec[i] = array[i] + array2[i]; } outvec[4] = array[4]; for (int i = 5; i < 9; i++) { outvec[i] = array[8 - i] - array2[8 - i]; } } private void ShortImpl(float[] fsIn, int sbStart, float[] nextblck) { _ = _swin[2]; int num = sbStart; int num2 = sbStart * 18; while (num < 32) { int i = 0; int num3 = 0; for (; i < 3; i++) { int num4 = num2 + i; for (int j = 0; j < 6; j++) { _imdctTemp[num3 + j] = fsIn[num4]; num4 += 3; } num3 += 6; } Array.Clear(fsIn, num2, 6); ShortIMDCT(_imdctTemp, 0, _imdctResult); Array.Copy(_imdctResult, 0, fsIn, num2 + 6, 12); ShortIMDCT(_imdct