Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ShipMusic v0.7.0
plugins/ShipMusic/ShipMusic.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using NLayer; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ShipMusic")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.7.0.0")] [assembly: AssemblyInformationalVersion("0.7.0+b765a0fab82a3212197c9c122cd45f491c6782a4")] [assembly: AssemblyProduct("ShipMusic")] [assembly: AssemblyTitle("ShipMusic")] [assembly: AssemblyVersion("0.7.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 ShipMusic { [DataContract] internal sealed class RadioStation { [DataMember] public string stationuuid { get; set; } [DataMember] public string name { get; set; } [DataMember] public string url_resolved { get; set; } [DataMember] public string tags { get; set; } [DataMember] public string codec { get; set; } [DataMember] public int hls { get; set; } [DataMember] public int lastcheckok { get; set; } public bool Playable { get { if (Guid.TryParse(stationuuid, out var _) && lastcheckok == 1 && hls == 0 && string.Equals(codec, "MP3", StringComparison.OrdinalIgnoreCase)) { return RadioHttp.IsPublicUrl(url_resolved); } return false; } } } internal static class RadioHttp { public static bool IsPublicUrl(string value) { if (value != null && value.Length <= 2048 && Uri.TryCreate(value, UriKind.Absolute, out Uri result) && (result.Scheme == "https" || result.Scheme == "http") && result.UserInfo.Length == 0 && !result.IsLoopback && result.Host.Contains(".")) { if (IPAddress.TryParse(result.Host, out IPAddress address)) { return PublicAddress(address); } return true; } return false; } private static bool PublicAddress(IPAddress address) { if (IPAddress.IsLoopback(address)) { return false; } if (address.IsIPv4MappedToIPv6) { address = address.MapToIPv4(); } byte[] addressBytes = address.GetAddressBytes(); if (addressBytes.Length == 16) { if (!address.IsIPv6LinkLocal && !address.IsIPv6SiteLocal && addressBytes[0] != byte.MaxValue && (addressBytes[0] & 0xFE) != 252) { return !address.Equals(IPAddress.IPv6Any); } return false; } if (addressBytes[0] != 0 && addressBytes[0] != 10 && addressBytes[0] != 127 && addressBytes[0] < 224 && (addressBytes[0] != 169 || addressBytes[1] != 254) && (addressBytes[0] != 172 || addressBytes[1] < 16 || addressBytes[1] > 31) && (addressBytes[0] != 192 || addressBytes[1] != 168)) { if (addressBytes[0] == 100 && addressBytes[1] >= 64) { return addressBytes[1] > 127; } return true; } return false; } public static HttpWebResponse Open(string url, CancellationToken token) { for (int i = 0; i < 6; i++) { token.ThrowIfCancellationRequested(); if (!IsPublicUrl(url)) { throw new IOException("Unsupported radio address."); } Uri uri = new Uri(url); IPAddress[] hostAddresses = Dns.GetHostAddresses(uri.DnsSafeHost); for (int j = 0; j < hostAddresses.Length; j++) { if (!PublicAddress(hostAddresses[j])) { throw new IOException("Radio address must be public."); } } HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(uri); httpWebRequest.UserAgent = "ShipMusic/0.7.0"; httpWebRequest.Timeout = 8000; httpWebRequest.ReadWriteTimeout = 8000; httpWebRequest.AllowAutoRedirect = false; httpWebRequest.Headers["Icy-MetaData"] = "0"; HttpWebResponse httpWebResponse; using (token.Register(httpWebRequest.Abort)) { httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); } if (httpWebResponse.StatusCode >= HttpStatusCode.MultipleChoices && httpWebResponse.StatusCode < HttpStatusCode.BadRequest) { string text = httpWebResponse.Headers["Location"]; httpWebResponse.Dispose(); if (string.IsNullOrEmpty(text)) { throw new IOException("Missing radio redirect."); } url = new Uri(uri, text).AbsoluteUri; continue; } return httpWebResponse; } throw new IOException("Too many radio redirects."); } } internal sealed class RadioCatalog { private string[] _servers; public static RadioStation[] Parse(byte[] json) { if (json == null || json.Length > 1048576) { throw new IOException("Radio catalog response too large."); } using MemoryStream stream = new MemoryStream(json); return (RadioStation[])new DataContractJsonSerializer(typeof(RadioStation[])).ReadObject(stream); } private string[] Servers() { if (_servers != null) { return _servers; } List<string> list = new List<string>(); try { IPAddress[] hostAddresses = Dns.GetHostAddresses("all.api.radio-browser.info"); for (int i = 0; i < hostAddresses.Length; i++) { string hostName = Dns.GetHostEntry(hostAddresses[i]).HostName; if (hostName.EndsWith(".api.radio-browser.info", StringComparison.OrdinalIgnoreCase) && !list.Contains(hostName)) { list.Add(hostName); } if (list.Count == 3) { break; } } } catch { } if (list.Count == 0) { list.Add("de1.api.radio-browser.info"); list.Add("nl1.api.radio-browser.info"); } int index = new Random().Next(list.Count); string item = list[index]; list.RemoveAt(index); list.Insert(0, item); return _servers = list.ToArray(); } private RadioStation[] Get(string path, CancellationToken token) { Exception innerException = null; string[] array = Servers(); foreach (string text in array) { token.ThrowIfCancellationRequested(); try { using HttpWebResponse httpWebResponse = RadioHttp.Open("https://" + text + path, token); using (token.Register(httpWebResponse.Close)) { using Stream stream = httpWebResponse.GetResponseStream(); using MemoryStream memoryStream = new MemoryStream(); byte[] array2 = new byte[8192]; int num; while ((num = stream.Read(array2, 0, array2.Length)) > 0) { token.ThrowIfCancellationRequested(); if (memoryStream.Length + num > 1048576) { throw new IOException("Radio catalog response too large."); } memoryStream.Write(array2, 0, num); } return Parse(memoryStream.ToArray()); } } catch (Exception ex) { token.ThrowIfCancellationRequested(); innerException = ex; } } throw new IOException("Radio Browser unavailable.", innerException); } public RadioStation[] Search(string tags, CancellationToken token) { List<RadioStation> list = new List<RadioStation>(); HashSet<string> hashSet = new HashSet<string>(); string[] array = tags.Split(','); for (int i = 0; i < array.Length && i < 8; i++) { string text = array[i].Trim(); if (text.Length == 0 || text.Length > 60) { continue; } RadioStation[] array2 = Get("/json/stations/search?tag=" + Uri.EscapeDataString(text) + "&tagExact=true&codec=MP3&hidebroken=true&limit=20&order=random", token); foreach (RadioStation radioStation in array2) { if (radioStation != null && radioStation.Playable && MatchesTag(radioStation.tags, text) && hashSet.Add(radioStation.stationuuid)) { list.Add(radioStation); } } } if (list.Count == 0) { throw new IOException("No playable MP3 stations for the configured radio tags."); } return list.ToArray(); } internal static bool MatchesTag(string tags, string term) { string[] array = (tags ?? "").Split(','); for (int i = 0; i < array.Length; i++) { if (string.Equals(array[i].Trim(), term.Trim(), StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public RadioStation Lookup(string id, CancellationToken token) { if (!Guid.TryParseExact(id, "D", out var _)) { throw new IOException("Invalid station ID."); } RadioStation[] array = Get("/json/stations/byuuid/" + id, token); foreach (RadioStation radioStation in array) { if (radioStation != null && radioStation.Playable && string.Equals(radioStation.stationuuid, id, StringComparison.OrdinalIgnoreCase)) { return radioStation; } } throw new IOException("Radio station is unavailable or not an MP3 stream."); } } internal sealed class RadioPcmBuffer { private readonly float[] _samples; private readonly object _gate = new object(); private int _read; private int _write; private int _count; public int Count { get { lock (_gate) { return _count; } } } public RadioPcmBuffer(int capacity) { _samples = new float[capacity]; } public int Write(float[] data, int offset, int count) { lock (_gate) { count = Math.Min(count, _samples.Length - _count); int num = Math.Min(count, _samples.Length - _write); Array.Copy(data, offset, _samples, _write, num); Array.Copy(data, offset + num, _samples, 0, count - num); _write = (_write + count) % _samples.Length; _count += count; return count; } } public void Read(float[] data) { lock (_gate) { int num = Math.Min(data.Length, _count); int num2 = Math.Min(num, _samples.Length - _read); Array.Copy(_samples, _read, data, 0, num2); Array.Copy(_samples, 0, data, num2, num - num2); Array.Clear(data, num, data.Length - num); _read = (_read + num) % _samples.Length; _count -= num; } } } internal sealed class IcyAudioStream : Stream { private readonly Stream _inner; private readonly int _interval; private int _remaining; private long _position; public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { return _position; } set { throw new NotSupportedException(); } } public IcyAudioStream(Stream inner, int interval) { _inner = inner; _interval = Math.Max(0, interval); _remaining = _interval; } public override int Read(byte[] buffer, int offset, int count) { if (count == 0) { return 0; } if (_interval == 0) { int num = _inner.Read(buffer, offset, count); _position += num; return num; } if (_remaining == 0) { int num2 = _inner.ReadByte(); if (num2 < 0) { return 0; } for (int i = 0; i < num2 * 16; i++) { if (_inner.ReadByte() < 0) { throw new EndOfStreamException("Truncated radio metadata."); } } _remaining = _interval; } int num3 = _inner.Read(buffer, offset, Math.Min(count, _remaining)); _remaining -= num3; _position += num3; return num3; } public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } internal interface IRadioStream : IDisposable { RadioPcmBuffer Buffer { get; } int SampleRate { get; } bool Finished { get; } string Error { get; } } internal sealed class RadioStream : IRadioStream, IDisposable { private readonly CancellationTokenSource _stop = new CancellationTokenSource(); private volatile RadioPcmBuffer _buffer; private volatile int _sampleRate; private volatile bool _finished; private volatile string _error; private int _disposed; private readonly Task _worker; public RadioPcmBuffer Buffer => _buffer; public int SampleRate => _sampleRate; public bool Finished => _finished; public string Error => _error; public RadioStream(string url) { RadioStream radioStream = this; _worker = Task.Run(delegate { radioStream.Run(url); }); } private void Run(string url) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown try { using HttpWebResponse httpWebResponse = RadioHttp.Open(url, _stop.Token); using (_stop.Token.Register(httpWebResponse.Close)) { using Stream inner = httpWebResponse.GetResponseStream(); int.TryParse(httpWebResponse.Headers["icy-metaint"], out var result); using IcyAudioStream icyAudioStream = new IcyAudioStream(inner, result); MpegFile val = new MpegFile((Stream)icyAudioStream); try { val.StereoMode = (StereoMode)3; int sampleRate = val.SampleRate; if (sampleRate < 8000 || sampleRate > 48000) { throw new IOException("Unsupported radio sample rate."); } _sampleRate = sampleRate; _buffer = new RadioPcmBuffer(sampleRate * 8); float[] array = new float[4096]; while (!_stop.IsCancellationRequested) { int num = val.ReadSamples(array, 0, array.Length); if (num == 0) { throw new EndOfStreamException("Radio stream ended."); } int num2 = 0; while (num2 < num && !_stop.IsCancellationRequested) { num2 += _buffer.Write(array, num2, num - num2); if (num2 < num) { _stop.Token.WaitHandle.WaitOne(20); } } } } finally { ((IDisposable)val)?.Dispose(); } } } catch (Exception ex) { if (!_stop.IsCancellationRequested) { _error = ex.GetType().Name + ": radio connection or decoding failed."; } } finally { _finished = true; } } public void Dispose() { if (Interlocked.Exchange(ref _disposed, 1) == 0) { _stop.Cancel(); _worker.ContinueWith(delegate { _stop.Dispose(); }, TaskScheduler.Default); } } } internal sealed class SharedMusic : IDisposable { private sealed class Wanted { public SharedTrackOffer Offer; public long Peer; public bool Seen; } private sealed class Loaded { public AudioClip Clip; public UnityWebRequest Request; } private sealed class Upload { public long Peer; public int Offset; } private sealed class PendingChunk { public float Sent; } private sealed class Download { public Wanted Want; public SharedTrackBuffer Buffer; public int NextOffset; public float LastProgress; public readonly Dictionary<int, PendingChunk> Pending = new Dictionary<int, PendingChunk>(); } internal const string OfferKey = "fenikonir.shipmusic.offer.v1"; private const string RequestRpc = "fenikonir.shipmusic.request.v1"; private const string ChunkRpc = "fenikonir.shipmusic.chunk.v1"; private readonly ConfigEntry<bool> _share; private readonly ConfigEntry<bool> _receive; private readonly ConfigEntry<int> _maxSize; private readonly ConfigEntry<int> _cacheSize; private readonly ConfigEntry<int> _uploadLimit; private readonly ManualLogSource _log; private readonly string _cache; private readonly Dictionary<string, Wanted> _wanted = new Dictionary<string, Wanted>(); private readonly Dictionary<string, Loaded> _loaded = new Dictionary<string, Loaded>(); private readonly Dictionary<string, float> _failed = new Dictionary<string, float>(); private readonly List<string> _remove = new List<string>(); private const int WindowSize = 16; private readonly List<Upload> _uploads = new List<Upload>(); private long _lastRecipient; private ZRoutedRpc _router; private SharedTrackOffer _localOffer; private byte[] _localBytes; private string _localIdentity; private Player _localPlayer; private Download _download; private float _sendBudget; private float _lastTick; private bool _disposed; private bool _cacheDirty = true; private int MaxBytes => Math.Min(_maxSize.Value, _cacheSize.Value) * 1024 * 1024; public static double Now { get { if (!((Object)(object)ZNet.instance != (Object)null)) { return 0.0; } return ZNet.instance.GetTimeSeconds(); } } public SharedMusic(ConfigFile config, ManualLogSource log) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown _log = log; _share = config.Bind<bool>("Sharing", "ShareMyTracks", false, "Send your selected OGG recording to nearby listeners while at the helm."); _receive = config.Bind<bool>("Sharing", "ReceiveSharedTracks", false, "Download and play the helmsman's recording. Off uses your local songs."); _maxSize = config.Bind<int>("Sharing", "MaxTrackSizeMB", 15, new ConfigDescription("Maximum compressed OGG size to send or receive.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>())); _cacheSize = config.Bind<int>("Sharing", "CacheSizeMB", 200, new ConfigDescription("Disk cache budget. Old cached tracks are removed first.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(15, 1000), Array.Empty<object>())); _cacheSize.SettingChanged += CacheChanged; _uploadLimit = config.Bind<int>("Sharing", "UploadLimitKBps", 1024, new ConfigDescription("Total OGG upload limit in KiB/s, shared by all listeners. Lower this on slow connections.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 4096), Array.Empty<object>())); _cache = Path.Combine(Paths.CachePath, "ShipMusic", "shared-v1"); } private void CacheChanged(object sender, EventArgs args) { _cacheDirty = true; } public static ZDO Data(Component component) { ZNetView val = (((Object)(object)component != (Object)null) ? component.GetComponent<ZNetView>() : null); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetZDO(); } public unsafe void SetLocal(Player player, Ship ship, string key, string path) { //IL_0045: 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) ZDO val = Data((Component)(object)player); ZDO val2 = Data((Component)(object)ship); bool flag = _share.Value && val2 != null && val != null && val.IsOwner() && path != null; object obj; if (!flag) { obj = ""; } else { string[] array = new string[5]; ZDOID uid = val2.m_uid; array[0] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); array[1] = "|"; array[2] = key; array[3] = "|"; array[4] = MaxBytes.ToString(); obj = string.Concat(array); } string text = (string)obj; if ((Object)(object)_localPlayer == (Object)(object)player && text == _localIdentity) { return; } ClearLocal(); _localPlayer = player; _localIdentity = text; if (!flag) { return; } try { FileInfo fileInfo = new FileInfo(path); if (!fileInfo.Exists || fileInfo.Length < 64 || fileInfo.Length > MaxBytes) { throw new IOException("Track exceeds the configured size limit or is unavailable."); } byte[] array2 = File.ReadAllBytes(path); if (array2.Length > MaxBytes || !SharedTrackOffer.IsVorbis(array2)) { throw new IOException("Expected OGG Vorbis."); } _localBytes = array2; _localOffer = new SharedTrackOffer { Ship = ((object)Unsafe.As<ZDOID, ZDOID>(ref val2.m_uid)/*cast due to .constrained prefix*/).ToString(), Hash = SharedTrackOffer.Digest(array2), Length = array2.Length, Started = Now, Session = Guid.NewGuid().ToString("N") }; val.Set("fenikonir.shipmusic.offer.v1", _localOffer.Encode()); _log.LogInfo((object)$"Sharing selected recording: {Path.GetFileName(path)} ({array2.Length / 1024} KiB)"); } catch (Exception ex) { _log.LogWarning((object)("Could not share track: " + ex.Message)); } } private void ClearLocal() { ZDO val = Data((Component)(object)_localPlayer); if (val != null && val.IsOwner()) { val.Set("fenikonir.shipmusic.offer.v1", ""); } _localOffer = null; _localBytes = null; _uploads.Clear(); } public void BeginFrame() { ZRoutedRpc instance = ZRoutedRpc.instance; if (_router != instance) { ResetTransfers(); _router = instance; if (_router != null) { _router.Register<ZPackage>("fenikonir.shipmusic.request.v1", (Action<long, ZPackage>)OnRequest); _router.Register<ZPackage>("fenikonir.shipmusic.chunk.v1", (Action<long, ZPackage>)OnChunk); } } foreach (Wanted value in _wanted.Values) { value.Seen = false; } float realtimeSinceStartup = Time.realtimeSinceStartup; _sendBudget = Math.Min(98304f, _sendBudget + Math.Max(0f, realtimeSinceStartup - _lastTick) * (float)_uploadLimit.Value * 1024f); _lastTick = realtimeSinceStartup; } public AudioClip Resolve(Ship ship, Player captain, AudioClip fallback, out double started, out string session) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) started = -1.0; session = ""; if ((Object)(object)captain == (Object)null) { return fallback; } SharedTrackOffer localOffer; if ((Object)(object)captain == (Object)(object)Player.m_localPlayer) { localOffer = _localOffer; if (localOffer == null) { return fallback; } started = localOffer.Started; session = localOffer.Session; return fallback; } if (!_receive.Value || (Object)(object)Player.m_localPlayer == (Object)null || Vector3.Distance(((Component)Player.m_localPlayer).transform.position, ((Component)ship).transform.position) > 50f) { return fallback; } ZDO val = Data((Component)(object)captain); ZDO val2 = Data((Component)(object)ship); if (val == null || val2 == null) { return fallback; } localOffer = SharedTrackOffer.Parse(val.GetString("fenikonir.shipmusic.offer.v1", ""), MaxBytes); if (localOffer == null || localOffer.Ship != ((object)Unsafe.As<ZDOID, ZDOID>(ref val2.m_uid)/*cast due to .constrained prefix*/).ToString() || localOffer.Started > Now + 10.0) { return fallback; } if (!_wanted.TryGetValue(localOffer.Hash, out var value)) { if (_wanted.Count >= 4) { return fallback; } value = new Wanted(); _wanted.Add(localOffer.Hash, value); } value.Offer = localOffer; value.Peer = val.GetOwner(); value.Seen = true; if (_loaded.TryGetValue(localOffer.Hash, out var value2) && (Object)(object)value2.Clip != (Object)null) { started = localOffer.Started; session = localOffer.Session; return value2.Clip; } return fallback; } public void EndFrame() { //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Invalid comparison between Unknown and I4 //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Invalid comparison between Unknown and I4 _remove.Clear(); foreach (KeyValuePair<string, Wanted> item in _wanted) { if (!item.Value.Seen || !_receive.Value) { _remove.Add(item.Key); } } foreach (string item2 in _remove) { _wanted.Remove(item2); } _remove.Clear(); foreach (KeyValuePair<string, Loaded> item3 in _loaded) { if (!_wanted.ContainsKey(item3.Key)) { _remove.Add(item3.Key); } } foreach (string item4 in _remove) { Release(_loaded[item4]); _loaded.Remove(item4); _cacheDirty = true; } if (_download != null && (!_wanted.TryGetValue(_download.Want.Offer.Hash, out var value) || value.Peer != _download.Want.Peer || value.Offer.Session != _download.Want.Offer.Session || value.Offer.Length > MaxBytes)) { _download = null; } foreach (KeyValuePair<string, Loaded> item5 in _loaded) { Loaded value2 = item5.Value; if (value2.Request == null || !value2.Request.isDone) { continue; } try { if ((int)value2.Request.result != 1) { throw new IOException(value2.Request.error); } value2.Clip = DownloadHandlerAudioClip.GetContent(value2.Request); if ((Object)(object)value2.Clip == (Object)null || (int)value2.Clip.loadState == 3) { throw new IOException("OGG decoding failed."); } } catch (Exception ex) { Fail(item5.Key, ex.Message); } finally { value2.Request.Dispose(); value2.Request = null; } } if (_cacheDirty) { TrimCache(0); _cacheDirty = false; } if (_router == null) { return; } SendQueuedChunks(); if (_download == null) { foreach (KeyValuePair<string, Wanted> item6 in _wanted) { if (!_loaded.ContainsKey(item6.Key) && (!_failed.TryGetValue(item6.Key, out var value3) || !(Time.realtimeSinceStartup < value3)) && !TryCached(item6.Value.Offer)) { _download = new Download { Want = new Wanted { Offer = item6.Value.Offer, Peer = item6.Value.Peer }, Buffer = new SharedTrackBuffer(item6.Value.Offer.Length, MaxBytes), LastProgress = Time.realtimeSinceStartup }; _log.LogInfo((object)$"Receiving shared track ({item6.Value.Offer.Length / 1024} KiB)"); break; } } } if (_download == null) { return; } if (Time.realtimeSinceStartup - _download.LastProgress > 22f) { Fail(_download.Want.Offer.Hash, "Transfer timed out; using local music."); _download = null; return; } foreach (KeyValuePair<int, PendingChunk> item7 in _download.Pending) { if (!(Time.realtimeSinceStartup - item7.Value.Sent < 1f)) { RequestChunk(item7.Key, item7.Value); } } while (_download.Pending.Count < 16 && _download.NextOffset < _download.Buffer.Bytes.Length) { int nextOffset = _download.NextOffset; _download.NextOffset += 24576; PendingChunk pendingChunk = new PendingChunk(); _download.Pending.Add(nextOffset, pendingChunk); RequestChunk(nextOffset, pendingChunk); } } private void RequestChunk(int offset, PendingChunk pending) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(_download.Want.Offer.Session); val.Write(_download.Want.Offer.Hash); val.Write(offset); pending.Sent = Time.realtimeSinceStartup; _router.InvokeRoutedRPC(_download.Want.Peer, "fenikonir.shipmusic.request.v1", new object[1] { val }); } private void OnRequest(long sender, ZPackage package) { if (_disposed || !_share.Value || _localOffer == null || _localBytes == null || _router == null) { return; } try { if (package.Size() > 256) { return; } string text = package.ReadString(); string text2 = package.ReadString(); int num = package.ReadInt(); if (text != _localOffer.Session || text2 != _localOffer.Hash || num < 0 || num >= _localBytes.Length || num % 24576 != 0 || _localBytes.Length > MaxBytes || !CanSendTo(sender)) { return; } int num2 = 0; foreach (Upload upload in _uploads) { if (upload.Peer == sender) { if (upload.Offset == num) { return; } num2++; } } if (num2 < 16 && _uploads.Count < 128) { _uploads.Add(new Upload { Peer = sender, Offset = num }); } } catch (Exception ex) { _log.LogDebug((object)("Rejected track request: " + ex.Message)); } } private bool CanSendTo(long peer) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; Ship val = (((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead()) ? localPlayer.GetControlledShip() : null); if ((Object)(object)val == (Object)null || ((object)Unsafe.As<ZDOID, ZDOID>(ref Data((Component)(object)val)?.m_uid)/*cast due to .constrained prefix*/).ToString() != _localOffer.Ship) { return false; } foreach (Player allPlayer in Player.GetAllPlayers()) { ZDO obj = Data((Component)(object)allPlayer); if (obj != null && obj.GetOwner() == peer && Vector3.Distance(((Component)allPlayer).transform.position, ((Component)val).transform.position) <= 60f) { return true; } } return false; } private void SendQueuedChunks() { //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown if (!_share.Value || _localOffer == null || _localBytes == null || _localBytes.Length > MaxBytes) { _uploads.Clear(); return; } for (int i = 0; i < 8; i++) { if (_uploads.Count <= 0) { break; } int num = _uploads.FindIndex((Upload upload2) => upload2.Peer != _lastRecipient); if (num < 0) { num = 0; } Upload upload = _uploads[num]; if (!CanSendTo(upload.Peer)) { _uploads.RemoveAt(num); continue; } int num2 = Math.Min(24576, _localBytes.Length - upload.Offset); if (_sendBudget < (float)num2) { break; } _uploads.RemoveAt(num); _sendBudget -= num2; _lastRecipient = upload.Peer; int num3 = _uploads.Count; int num4 = 0; while (num4 < num3) { if (_uploads[num4].Peer != upload.Peer) { num4++; continue; } Upload item = _uploads[num4]; _uploads.RemoveAt(num4); _uploads.Add(item); num3--; } byte[] array = new byte[num2]; Buffer.BlockCopy(_localBytes, upload.Offset, array, 0, num2); ZPackage val = new ZPackage(); val.Write(_localOffer.Session); val.Write(_localOffer.Hash); val.Write(upload.Offset); val.Write(array); _router.InvokeRoutedRPC(upload.Peer, "fenikonir.shipmusic.chunk.v1", new object[1] { val }); } } private void OnChunk(long sender, ZPackage package) { if (_disposed || !_receive.Value || _download == null) { return; } try { if (package.Size() > 24832 || sender != _download.Want.Peer) { return; } string text = package.ReadString(); string text2 = package.ReadString(); int num = package.ReadInt(); int num2 = package.ReadInt(); if (text != _download.Want.Offer.Session || text2 != _download.Want.Offer.Hash || num2 < 1 || num2 > 24576 || _download.Buffer.Bytes.Length > MaxBytes || !_download.Pending.ContainsKey(num)) { return; } byte[] chunk = package.ReadByteArray(num2); if (!_download.Buffer.Append(num, chunk)) { return; } _download.Pending.Remove(num); _download.LastProgress = Time.realtimeSinceStartup; if (_download.Buffer.Complete) { byte[] bytes = _download.Buffer.Bytes; if (SharedTrackOffer.Digest(bytes) != text2 || !SharedTrackOffer.IsVorbis(bytes)) { throw new IOException("Track integrity/format check failed."); } Directory.CreateDirectory(_cache); TrimCache(bytes.Length); string path = CachePath(text2); File.WriteAllBytes(path, bytes); BeginDecode(text2, path); _download = null; _log.LogInfo((object)"Shared track received and verified; preparing playback."); } } catch (Exception ex) { if (_download != null) { Fail(_download.Want.Offer.Hash, ex.Message); } _download = null; } } private string CachePath(string hash) { return Path.Combine(_cache, hash + ".ogg"); } private bool TryCached(SharedTrackOffer offer) { try { string text = CachePath(offer.Hash); FileInfo fileInfo = new FileInfo(text); if (!fileInfo.Exists || fileInfo.Length != offer.Length) { return false; } byte[] bytes = File.ReadAllBytes(text); if (SharedTrackOffer.Digest(bytes) != offer.Hash || !SharedTrackOffer.IsVorbis(bytes)) { File.Delete(text); return false; } File.SetLastWriteTimeUtc(text, DateTime.UtcNow); BeginDecode(offer.Hash, text); return true; } catch (Exception ex) { _log.LogWarning((object)("Cache read failed: " + ex.Message)); return false; } } private void BeginDecode(string hash, string path) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, (AudioType)14); ((DownloadHandlerAudioClip)audioClip.downloadHandler).streamAudio = true; audioClip.SendWebRequest(); _loaded[hash] = new Loaded { Request = audioClip }; } private void TrimCache(int incoming) { try { if (!Directory.Exists(_cache)) { return; } FileInfo[] files = new DirectoryInfo(_cache).GetFiles("*.ogg"); Array.Sort(files, (FileInfo a, FileInfo b) => a.LastWriteTimeUtc.CompareTo(b.LastWriteTimeUtc)); long num = incoming; FileInfo[] array = files; foreach (FileInfo fileInfo in array) { num += fileInfo.Length; } array = files; foreach (FileInfo fileInfo2 in array) { if (num <= (long)_cacheSize.Value * 1024L * 1024) { break; } if (!_loaded.ContainsKey(Path.GetFileNameWithoutExtension(fileInfo2.Name))) { long length = fileInfo2.Length; fileInfo2.Delete(); num -= length; } } if (incoming <= 0 || num <= (long)_cacheSize.Value * 1024L * 1024) { return; } throw new IOException("Cache budget exhausted."); } catch (Exception ex) { if (incoming > 0) { throw; } _log.LogWarning((object)("Cache cleanup failed: " + ex.Message)); } } private void Fail(string hash, string reason) { if (_failed.Count >= 128) { _failed.Clear(); } _failed[hash] = Time.realtimeSinceStartup + 60f; _log.LogWarning((object)("Shared track unavailable: " + reason)); } private static void Release(Loaded loaded) { if ((Object)(object)loaded.Clip != (Object)null) { Object.Destroy((Object)(object)loaded.Clip); } UnityWebRequest request = loaded.Request; if (request != null) { request.Abort(); } UnityWebRequest request2 = loaded.Request; if (request2 != null) { request2.Dispose(); } } private void ResetTransfers() { _download = null; foreach (Loaded value in _loaded.Values) { Release(value); } _loaded.Clear(); _wanted.Clear(); _failed.Clear(); _uploads.Clear(); _sendBudget = 0f; _lastTick = Time.realtimeSinceStartup; _localIdentity = null; } public void Dispose() { _disposed = true; ClearLocal(); ResetTransfers(); _cacheSize.SettingChanged -= CacheChanged; } } internal sealed class SharedTrackOffer { public string Ship; public string Hash; public string Session; public int Length; public double Started; public string Encode() { return string.Join("|", Ship, Hash, Length.ToString(CultureInfo.InvariantCulture), Started.ToString("R", CultureInfo.InvariantCulture), Session); } public static SharedTrackOffer Parse(string value, int maxBytes) { if (string.IsNullOrEmpty(value) || value.Length > 256) { return null; } string[] array = value.Split('|'); if (array.Length != 5 || array[0].Length == 0 || !IsHash(array[1]) || !int.TryParse(array[2], out var result) || result < 64 || result > maxBytes || !double.TryParse(array[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || double.IsNaN(result2) || double.IsInfinity(result2) || result2 < 0.0 || !Guid.TryParseExact(array[4], "N", out var _)) { return null; } return new SharedTrackOffer { Ship = array[0], Hash = array[1], Length = result, Started = result2, Session = array[4] }; } public static bool IsHash(string hash) { if (hash == null || hash.Length != 64) { return false; } foreach (char c in hash) { if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) { return false; } } return true; } public static string Digest(byte[] bytes) { using SHA256 sHA = SHA256.Create(); return BitConverter.ToString(sHA.ComputeHash(bytes)).Replace("-", "").ToLowerInvariant(); } public static bool IsVorbis(byte[] bytes) { if (bytes == null || bytes.Length < 64 || bytes[0] != 79 || bytes[1] != 103 || bytes[2] != 103 || bytes[3] != 83 || bytes[4] != 0) { return false; } int num = 27 + bytes[26]; if (num + 7 <= bytes.Length && bytes[num] == 1 && bytes[num + 1] == 118 && bytes[num + 2] == 111 && bytes[num + 3] == 114 && bytes[num + 4] == 98 && bytes[num + 5] == 105) { return bytes[num + 6] == 115; } return false; } } internal sealed class SharedTrackBuffer { public const int ChunkSize = 24576; public readonly byte[] Bytes; private readonly bool[] _received; public int ReceivedBytes { get; private set; } public bool Complete => ReceivedBytes == Bytes.Length; public SharedTrackBuffer(int length, int maxBytes) { if (length < 64 || length > maxBytes) { throw new ArgumentOutOfRangeException("length"); } Bytes = new byte[length]; _received = new bool[(length + 24576 - 1) / 24576]; } public bool Append(int offset, byte[] chunk) { if (chunk == null || offset < 0 || offset >= Bytes.Length || offset % 24576 != 0 || _received[offset / 24576] || chunk.Length != Math.Min(24576, Bytes.Length - offset)) { return false; } Buffer.BlockCopy(chunk, 0, Bytes, offset, chunk.Length); _received[offset / 24576] = true; ReceivedBytes += chunk.Length; return true; } } [BepInPlugin("fenikonir.shipmusic", "Ship Music", "0.7.0")] public sealed class ShipMusicPlugin : BaseUnityPlugin { private readonly Dictionary<Ship, AudioSource> _shipSources = new Dictionary<Ship, AudioSource>(); private readonly List<Ship> _removedShips = new List<Ship>(); private readonly Dictionary<string, AudioClip> _musicClips = new Dictionary<string, AudioClip>(StringComparer.Ordinal); private static readonly Dictionary<string, string> TrackFiles = new Dictionary<string, string>(StringComparer.Ordinal) { { "Raft", "raft.ogg" }, { "Karve", "karve.ogg" }, { "VikingShip", "longship.ogg" }, { "VikingShip_Ashlands", "drakkar.ogg" } }; private UnityWebRequest _loadRequest; private ConfigEntry<int> _volume; private FileSystemWatcher _configWatcher; private long _reloadAfterTicks; private int _reloadAttempts; private int _watcherError; private readonly List<string> _extraTracks = new List<string>(); private Ship _localHelm; private Player _selectionPlayer; private int _trackIndex = -1; private bool _tracksLoaded; private const string SelectionKey = "fenikonir.shipmusic.selection"; private SharedMusic _sharing; private ShipRadio _radio; private readonly Dictionary<string, string> _trackPaths = new Dictionary<string, string>(StringComparer.Ordinal); private readonly Dictionary<Ship, string> _playSessions = new Dictionary<Ship, string>(); private double _selectedStart; private string _selectedSession; private void Awake() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown ((BaseUnityPlugin)this).Logger.LogInfo((object)"Ship Music loaded"); _volume = ((BaseUnityPlugin)this).Config.Bind<int>("Audio", "Volume", 50, new ConfigDescription("Music volume in percent. 0 = muted, 100 = maximum.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); _volume.SettingChanged += OnVolumeChanged; _sharing = new SharedMusic(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); _radio = new ShipRadio(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, delegate(string message) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, message, 0, (Sprite)null, false); } }); StartConfigWatcher(); string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); ((MonoBehaviour)this).StartCoroutine(LoadTracks(directoryName)); } private IEnumerator LoadTracks(string directory) { foreach (KeyValuePair<string, string> trackFile in TrackFiles) { yield return LoadMusic(trackFile.Key, Path.Combine(directory, trackFile.Value)); } string path = Path.Combine(directory, "tracks"); Directory.CreateDirectory(path); string[] files = Directory.GetFiles(path); Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase); string[] array = files; foreach (string path2 in array) { if (!string.Equals(Path.GetExtension(path2), ".ogg", StringComparison.OrdinalIgnoreCase)) { continue; } string key = "tracks/" + Path.GetFileName(path2).ToLowerInvariant(); if (!_musicClips.ContainsKey(key)) { yield return LoadMusic(key, path2); if (_musicClips.ContainsKey(key)) { _extraTracks.Add(key); } } } _tracksLoaded = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Extra tracks ready: {_extraTracks.Count}. F8 cycles tracks at the helm."); } private IEnumerator LoadMusic(string prefab, string path) { if (!File.Exists(path)) { ((BaseUnityPlugin)this).Logger.LogError((object)("Music file not found: " + path + ". Place it beside ShipMusic.dll and restart the game.")); yield break; } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Loading music: " + path)); _loadRequest = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, (AudioType)14); try { yield return _loadRequest.SendWebRequest(); if ((int)_loadRequest.result != 1) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to load " + path + ": " + _loadRequest.error)); yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(_loadRequest); if ((Object)(object)content == (Object)null || (int)content.loadState == 3) { if ((Object)(object)content != (Object)null) { Object.Destroy((Object)(object)content); } ((BaseUnityPlugin)this).Logger.LogError((object)("Could not decode " + path + ". Use an OGG Vorbis audio file.")); } else { _musicClips.Add(prefab, content); _trackPaths[prefab] = path; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Music loaded: {prefab} -> {Path.GetFileName(path)}, {content.length:F1} seconds."); } } finally { ShipMusicPlugin shipMusicPlugin = this; UnityWebRequest loadRequest = shipMusicPlugin._loadRequest; if (loadRequest != null) { loadRequest.Dispose(); } shipMusicPlugin._loadRequest = null; } } private void Update() { ReloadChangedConfig(); _sharing.BeginFrame(); UpdateTrackSelection(); UpdateRadio(); UpdateShipAudio(); _sharing.EndFrame(); } private void PublishSelection(Player player, Ship ship, string track) { if ((Object)(object)player == (Object)null) { return; } ZNetView component = ((Component)player).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsOwner()) { ZDO zDO = component.GetZDO(); if (zDO != null) { ZNetView val = (((Object)(object)ship != (Object)null) ? ((Component)ship).GetComponent<ZNetView>() : null); ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); zDO.Set("fenikonir.shipmusic.selection", (val2 != null && track.Length > 0) ? (((object)Unsafe.As<ZDOID, ZDOID>(ref val2.m_uid)/*cast due to .constrained prefix*/).ToString() + "\n" + track) : ""); } } } private void UpdateTrackSelection() { Player localPlayer = Player.m_localPlayer; Ship val = (((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead()) ? localPlayer.GetControlledShip() : null); if ((Object)(object)localPlayer != (Object)(object)_selectionPlayer || (Object)(object)val != (Object)(object)_localHelm) { PublishSelection(_selectionPlayer, null, ""); _selectionPlayer = localPlayer; _localHelm = val; _trackIndex = -1; PublishSelection(localPlayer, null, ""); } bool flag = (Object)(object)val != (Object)null && Input.GetKeyDown((KeyCode)289); _radio.UpdateLocal(localPlayer, val, flag); UpdateSharedSelection(localPlayer, val); if (_radio.Enabled || !flag) { return; } if (!_tracksLoaded) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Tracks are still loading."); return; } if (_extraTracks.Count == 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"No extra tracks. Add OGG Vorbis files to tracks beside the DLL and restart."); return; } _trackIndex++; if (_trackIndex >= _extraTracks.Count) { _trackIndex = -1; } string text = ((_trackIndex < 0) ? "" : _extraTracks[_trackIndex]); PublishSelection(localPlayer, val, text); UpdateSharedSelection(localPlayer, val); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Selected music: " + ((text.Length == 0) ? "boat default" : text))); } private void UpdateSharedSelection(Player player, Ship helm) { if (_radio.Enabled) { _sharing.SetLocal(player, null, "", null); return; } string text = ((_trackIndex < 0 || _trackIndex >= _extraTracks.Count) ? "" : _extraTracks[_trackIndex]); if (text.Length == 0 && (Object)(object)helm != (Object)null) { text = ((Object)((Component)helm).gameObject).name; if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - 7); } } _trackPaths.TryGetValue(text, out var value); _sharing.SetLocal(player, helm, text, value); } private void UpdateRadio() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) Ship ship = null; Player captain = null; Player localPlayer = Player.m_localPlayer; float num = 50f; if (_radio.Enabled && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead()) { foreach (IMonoUpdater instance in Ship.Instances) { Ship val = (Ship)(object)((instance is Ship) ? instance : null); if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled || (Object)(object)val.m_shipControlls == (Object)null || !val.m_shipControlls.HaveValidUser()) { continue; } float num2 = Vector3.Distance(((Component)localPlayer).transform.position, ((Component)val).transform.position); if (!(num2 > num)) { Player player = Player.GetPlayer(val.m_shipControlls.GetUser()); ZDO obj = SharedMusic.Data((Component)(object)player); RadioSelection radioSelection = RadioSelection.Parse((obj != null) ? obj.GetString("fenikonir.shipmusic.radio.v1", "") : null); if (radioSelection != null && !(radioSelection.Ship != ((object)Unsafe.As<ZDOID, ZDOID>(ref SharedMusic.Data((Component)(object)val)?.m_uid)/*cast due to .constrained prefix*/).ToString())) { ship = val; captain = player; num = num2; } } } } _radio.Listen(ship, captain); } private AudioClip GetShipClip(Ship ship, ShipControlls controls) { _selectedStart = -1.0; _selectedSession = ""; string text = ((Object)((Component)ship).gameObject).name; if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length); } if (!TrackFiles.ContainsKey(text)) { return null; } _musicClips.TryGetValue(text, out var value); Player val = (((Object)(object)controls != (Object)null) ? Player.GetPlayer(controls.GetUser()) : null); if (_radio.WantsShip(ship)) { return _radio.Clip ?? value; } if (_radio.Enabled) { ZDO obj = SharedMusic.Data((Component)(object)val); if (RadioSelection.Parse((obj != null) ? obj.GetString("fenikonir.shipmusic.radio.v1", "") : null) != null) { return null; } } ZNetView val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<ZNetView>() : null); ZNetView component = ((Component)ship).GetComponent<ZNetView>(); ZDO val3 = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); ZDO val4 = (((Object)(object)val2 != (Object)null) ? val2.GetZDO() : null); if (val4 != null && val3 != null) { string text2 = val4.GetString("fenikonir.shipmusic.selection", ""); string text3 = ((object)Unsafe.As<ZDOID, ZDOID>(ref val3.m_uid)/*cast due to .constrained prefix*/).ToString() + "\n"; if (text2.StartsWith(text3, StringComparison.Ordinal)) { string text4 = text2.Substring(text3.Length); if (text4.StartsWith("tracks/", StringComparison.Ordinal) && _musicClips.TryGetValue(text4, out var value2)) { value = value2; } } } return _sharing.Resolve(ship, val, value, out _selectedStart, out _selectedSession); } private void UpdateShipAudio() { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Invalid comparison between Unknown and I4 //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)Player.m_localPlayer != (Object)null; if (flag) { foreach (IMonoUpdater instance in Ship.Instances) { Ship val = (Ship)(object)((instance is Ship) ? instance : null); if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled) { continue; } ShipControlls shipControlls = val.m_shipControlls; bool flag2 = (Object)(object)shipControlls != (Object)null && shipControlls.HaveValidUser(); AudioClip val2 = (flag2 ? GetShipClip(val, shipControlls) : null); string text = (flag2 ? _selectedSession : ""); if (!_shipSources.TryGetValue(val, out var value)) { if (!flag2 || (Object)(object)val2 == (Object)null || (int)val2.loadState != 2) { continue; } GameObject val3 = new GameObject("ShipMusicAudio"); val3.transform.SetParent(((Component)val).transform, false); value = val3.AddComponent<AudioSource>(); value.playOnAwake = false; value.clip = val2; value.loop = true; value.spatialBlend = 1f; value.dopplerLevel = 0f; value.rolloffMode = (AudioRolloffMode)1; value.minDistance = 10f; value.maxDistance = 40f; value.volume = Mathf.Clamp01((float)_volume.Value / 100f); _shipSources.Add(val, value); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Ship audio attached: " + ((Object)((Component)val).gameObject).name)); } if ((Object)(object)value == (Object)null) { continue; } _playSessions.TryGetValue(val, out var value2); if ((Object)(object)value.clip != (Object)(object)val2 || value2 != text) { value.Stop(); value.clip = val2; _playSessions[val] = text; } if (flag2 && (Object)(object)val2 != (Object)null && !value.isPlaying) { if (_selectedStart >= 0.0 && val2.length > 0f) { value.time = (float)(Math.Max(0.0, SharedMusic.Now - _selectedStart) % (double)val2.length); } value.Play(); } else if (!flag2 && value.isPlaying) { value.Stop(); } } } _removedShips.Clear(); foreach (KeyValuePair<Ship, AudioSource> shipSource in _shipSources) { if (!flag || (Object)(object)shipSource.Key == (Object)null || !((Behaviour)shipSource.Key).isActiveAndEnabled || !Ship.Instances.Contains((IMonoUpdater)(object)shipSource.Key) || (Object)(object)shipSource.Value == (Object)null) { if ((Object)(object)shipSource.Value != (Object)null) { shipSource.Value.Stop(); Object.Destroy((Object)(object)((Component)shipSource.Value).gameObject); } _removedShips.Add(shipSource.Key); } } foreach (Ship removedShip in _removedShips) { _shipSources.Remove(removedShip); _playSessions.Remove(removedShip); } } private void OnVolumeChanged(object sender, EventArgs args) { ApplyVolume(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Music volume: {_volume.Value}%"); } private void StartConfigWatcher() { try { _configWatcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); _configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; _configWatcher.Changed += OnConfigFileChanged; _configWatcher.Created += OnConfigFileChanged; _configWatcher.Renamed += OnConfigFileChanged; _configWatcher.Error += OnConfigWatcherError; _configWatcher.EnableRaisingEvents = true; } catch (Exception ex) { _configWatcher?.Dispose(); _configWatcher = null; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Config watching unavailable; restart to apply file edits: " + ex.Message)); } } private void OnConfigFileChanged(object sender, FileSystemEventArgs args) { Interlocked.Exchange(ref _reloadAttempts, 0); Interlocked.Exchange(ref _reloadAfterTicks, DateTime.UtcNow.AddMilliseconds(250.0).Ticks); } private void OnConfigWatcherError(object sender, ErrorEventArgs args) { Interlocked.Exchange(ref _watcherError, 1); OnConfigFileChanged(sender, null); } private void ReloadChangedConfig() { if (Interlocked.Exchange(ref _watcherError, 0) != 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Config watcher reported an error. Reloading config; if later edits are ignored, restart the game."); } long num = Interlocked.Read(in _reloadAfterTicks); if (num == 0L || DateTime.UtcNow.Ticks < num || Interlocked.CompareExchange(ref _reloadAfterTicks, 0L, num) != num) { return; } try { if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { throw new IOException("Config file is temporarily unavailable."); } ((BaseUnityPlugin)this).Config.Reload(); } catch (Exception ex) { if (Interlocked.Increment(ref _reloadAttempts) < 3) { Interlocked.CompareExchange(ref _reloadAfterTicks, DateTime.UtcNow.AddMilliseconds(500.0).Ticks, 0L); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not reload music config; save it again to retry: " + ex.Message)); } } } private void ApplyVolume() { foreach (AudioSource value in _shipSources.Values) { if ((Object)(object)value != (Object)null) { value.volume = Mathf.Clamp01((float)_volume.Value / 100f); } } } private void OnDestroy() { PublishSelection(_selectionPlayer, null, ""); _sharing?.Dispose(); _radio?.Dispose(); _configWatcher?.Dispose(); _configWatcher = null; if (_volume != null) { _volume.SettingChanged -= OnVolumeChanged; } ((MonoBehaviour)this).StopAllCoroutines(); UnityWebRequest loadRequest = _loadRequest; if (loadRequest != null) { loadRequest.Dispose(); } _loadRequest = null; foreach (AudioSource value in _shipSources.Values) { if (!((Object)(object)value == (Object)null)) { value.Stop(); Object.Destroy((Object)(object)((Component)value).gameObject); } } _shipSources.Clear(); foreach (AudioClip value2 in _musicClips.Values) { if ((Object)(object)value2 != (Object)null) { Object.Destroy((Object)(object)value2); } } _musicClips.Clear(); } } internal sealed class RadioSelection { public string Ship; public string Station; public string Session; public string Encode() { return Ship + "|" + Station + "|" + Session; } public static RadioSelection Parse(string value) { if (string.IsNullOrEmpty(value) || value.Length > 200) { return null; } string[] array = value.Split('|'); if (array.Length != 3 || array[0].Length == 0 || !Guid.TryParseExact(array[1], "D", out var result) || !Guid.TryParseExact(array[2], "N", out result)) { return null; } return new RadioSelection { Ship = array[0], Station = array[1], Session = array[2] }; } } internal sealed class ShipRadio : IDisposable { internal const string SelectionKey = "fenikonir.shipmusic.radio.v1"; private readonly ConfigEntry<bool> _enabled; private readonly ConfigEntry<string> _tags; private readonly ManualLogSource _log; private readonly Action<string> _notify; private readonly Func<string, CancellationToken, RadioStation[]> _search; private readonly Func<string, CancellationToken, RadioStation> _lookup; private readonly Func<string, IRadioStream> _open; private readonly Random _random = new Random(); private Player _localPlayer; private Ship _helm; private Ship _target; private string _localTags; private string _session = ""; private string _localStation = ""; private string _localSession = ""; private RadioStation[] _stations; private RadioStation _selectedStation; private Task<RadioStation[]> _searchTask; private Task<RadioStation> _lookupTask; private CancellationTokenSource _searchStop; private CancellationTokenSource _lookupStop; private IRadioStream _stream; private AudioClip _clip; private float _searchRetry; private float _streamRetry; private float _nextSwitch; private float _connectingSince; private float _catalogExpires; private int _failures; public bool Enabled => _enabled.Value; public AudioClip Clip => _clip; public ShipRadio(ConfigFile config, ManualLogSource log, Action<string> notify = null, Func<string, CancellationToken, RadioStation[]> search = null, Func<string, CancellationToken, RadioStation> lookup = null, Func<string, IRadioStream> open = null) { _log = log; _notify = notify; RadioCatalog radioCatalog = new RadioCatalog(); _search = search ?? new Func<string, CancellationToken, RadioStation[]>(radioCatalog.Search); _lookup = lookup ?? new Func<string, CancellationToken, RadioStation>(radioCatalog.Lookup); _open = open ?? ((Func<string, IRadioStream>)((string url) => new RadioStream(url))); _enabled = config.Bind<bool>("Radio", "Enabled", true, "Play live internet radio on occupied ships. F8 at the helm changes station. Disable to use local OGG music and file sharing."); _tags = config.Bind<string>("Radio", "Tags", "viking,viking metal,nordic folk,folk metal,pagan metal", "Comma-separated Radio Browser genre tags (up to eight). A random MP3 station is chosen; the station controls its playlist."); } public void UpdateLocal(Player player, Ship helm, bool next) { string tags = _tags.Value ?? ""; if (tags.Length > 512) { tags = tags.Substring(0, 512); } if (!Enabled) { helm = null; } if ((Object)(object)_localPlayer != (Object)(object)player || (Object)(object)_helm != (Object)(object)helm || _localTags != tags) { ClearOffer(); Cancel(ref _searchStop, ref _searchTask); if (_localTags != tags) { _stations = null; } _localPlayer = player; _helm = helm; _localTags = tags; _localStation = ""; _localSession = ""; _selectedStation = null; _searchRetry = 0f; } ZDO val = SharedMusic.Data((Component)(object)helm); if ((Object)(object)helm == (Object)null || val == null) { return; } ZDO obj = SharedMusic.Data((Component)(object)player); if (obj == null || !obj.IsOwner()) { return; } if (next && Time.realtimeSinceStartup >= _nextSwitch) { _nextSwitch = Time.realtimeSinceStartup + 1f; _localSession = ""; _searchRetry = 0f; } if (_searchTask != null && _searchTask.IsCompleted) { try { _stations = _searchTask.GetAwaiter().GetResult(); _catalogExpires = Time.realtimeSinceStartup + 900f; } catch (Exception) { _log.LogWarning((object)"Radio Browser search failed; retrying in 30 seconds. Local music continues."); _searchRetry = Time.realtimeSinceStartup + 30f; } Cancel(ref _searchStop, ref _searchTask); } if (_localSession.Length != 0 || Time.realtimeSinceStartup < _searchRetry) { return; } if (Time.realtimeSinceStartup >= _catalogExpires) { _stations = null; } if (_stations == null || _stations.Length == 0) { if (_searchTask == null) { _searchStop = new CancellationTokenSource(); CancellationToken token = _searchStop.Token; _searchTask = Task.Run(() => _search(tags, token), token); } return; } int num = _random.Next(_stations.Length); if (_stations.Length > 1 && _stations[num].stationuuid == _localStation) { num = (num + 1) % _stations.Length; } _selectedStation = _stations[num]; _localStation = _selectedStation.stationuuid; _localSession = Guid.NewGuid().ToString("N"); RadioSelection radioSelection = new RadioSelection { Ship = ((object)Unsafe.As<ZDOID, ZDOID>(ref val.m_uid)/*cast due to .constrained prefix*/).ToString(), Station = _localStation, Session = _localSession }; SharedMusic.Data((Component)(object)player).Set("fenikonir.shipmusic.radio.v1", radioSelection.Encode()); string text = SafeName(_selectedStation.name); _log.LogInfo((object)("Selected radio: " + text)); _notify?.Invoke("Radio: " + text); } internal static string SafeName(string value) { value = (value ?? "Radio").Replace("\r", " ").Replace("\n", " ").Replace("<", "") .Replace(">", ""); if (value.Length <= 100) { return value; } return value.Substring(0, 100); } public bool WantsShip(Ship ship) { if (Enabled) { return (Object)(object)_target == (Object)(object)ship; } return false; } public void Listen(Ship ship, Player captain) { //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Expected O, but got Unknown object obj; if (!Enabled || !((Object)(object)ship != (Object)null) || !((Object)(object)captain != (Object)null)) { obj = null; } else { ZDO obj2 = SharedMusic.Data((Component)(object)captain); obj = RadioSelection.Parse((obj2 != null) ? obj2.GetString("fenikonir.shipmusic.radio.v1", "") : null); } RadioSelection radioSelection = (RadioSelection)obj; if (radioSelection == null || radioSelection.Ship != ((object)Unsafe.As<ZDOID, ZDOID>(ref SharedMusic.Data((Component)(object)ship)?.m_uid)/*cast due to .constrained prefix*/).ToString()) { ship = null; } string text = (((Object)(object)ship != (Object)null) ? radioSelection.Session : ""); if ((Object)(object)_target != (Object)(object)ship || _session != text) { StopPlayback(); _target = ship; _session = text; _streamRetry = 0f; _failures = 0; } if ((Object)(object)ship == (Object)null) { return; } if (_lookupTask != null && _lookupTask.IsCompleted) { try { RadioStation result = _lookupTask.GetAwaiter().GetResult(); _stream = _open(result.url_resolved); _connectingSince = Time.realtimeSinceStartup; _log.LogInfo((object)("Connecting to radio: " + SafeName(result.name))); } catch (Exception) { Retry("Station lookup failed."); } Cancel(ref _lookupStop, ref _lookupTask); } if (_stream != null) { if (_stream.Finished || ((Object)(object)_clip == (Object)null && Time.realtimeSinceStartup - _connectingSince > 20f)) { Retry(_stream.Error ?? "Radio buffering timed out."); return; } RadioPcmBuffer buffer = _stream.Buffer; if ((Object)(object)_clip == (Object)null && buffer != null && _stream.SampleRate > 0 && buffer.Count >= _stream.SampleRate * 2) { _clip = AudioClip.Create("ShipMusicRadio", _stream.SampleRate, 1, _stream.SampleRate, true, new PCMReaderCallback(buffer.Read)); _failures = 0; _log.LogInfo((object)"Radio buffered; starting live playback."); } } if (_stream == null && _lookupTask == null && Time.realtimeSinceStartup >= _streamRetry) { _lookupStop = new CancellationTokenSource(); CancellationToken token = _lookupStop.Token; string id = radioSelection.Station; _lookupTask = Task.Run(() => _lookup(id, token), token); } } private void Retry(string reason) { StopPlayback(); _streamRetry = Time.realtimeSinceStartup + (float)Math.Min(60, 5 * (1 << Math.Min(_failures++, 3))); _log.LogWarning((object)("Radio unavailable: " + reason + " Local music continues; reconnect scheduled.")); if ((Object)(object)_target == (Object)(object)_helm && _failures >= 2 && _stations != null && _stations.Length > 1) { _localSession = ""; _searchRetry = _streamRetry; } } private void ClearOffer() { ZDO val = SharedMusic.Data((Component)(object)_localPlayer); if (val != null && val.IsOwner()) { val.Set("fenikonir.shipmusic.radio.v1", ""); } } private static void Cancel<T>(ref CancellationTokenSource stop, ref Task<T> task) { CancellationTokenSource source = stop; Task<T> task2 = task; stop = null; task = null; if (source == null) { return; } source.Cancel(); if (task2 == null) { source.Dispose(); return; } task2.ContinueWith(delegate(Task<T> done) { _ = done.Exception; source.Dispose(); }, TaskScheduler.Default); } private void StopPlayback() { Cancel(ref _lookupStop, ref _lookupTask); _stream?.Dispose(); _stream = null; if ((Object)(object)_clip != (Object)null) { Object.Destroy((Object)(object)_clip); } _clip = null; } public void Dispose() { ClearOffer(); Cancel(ref _searchStop, ref _searchTask); StopPlayback(); _target = null; _helm = null; } } }
plugins/ShipMusic/NLayer.dll
Decompiled 4 days 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.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; 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: AssemblyCopyright("© Mark Heath 2026")] [assembly: AssemblyDescription("Fully Managed MPEG 1 & 2 Decoder for Layers 1, 2, & 3")] [assembly: AssemblyFileVersion("3.0.0.0")] [assembly: AssemblyInformationalVersion("3.0.0+046c7ce422970f8f0f0bc205d6c6341bcb7debf1")] [assembly: AssemblyProduct("NLayer")] [assembly: AssemblyTitle("NLayer")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/naudio/NLayer")] [assembly: AssemblyVersion("3.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 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 int _encoderDelay; private int _encoderPadding; private bool _decoderDelaySkipped; private float[] _readBuf = new float[2304]; private int _readBufLen; private int _readBufOfs; public int SampleRate => _reader.SampleRate; public int Channels => OutputChannels; private int OutputChannels { get { if (StereoMode != StereoMode.Both) { return 1; } return _reader.Channels; } } public bool CanSeek => _reader.CanSeek; public long Length { get { long sampleCount = _reader.SampleCount; if (sampleCount < 0) { return -1L; } return (sampleCount - _encoderDelay - _encoderPadding) * OutputChannels * 4; } } public TimeSpan Duration { get { long sampleCount = _reader.SampleCount; if (sampleCount == -1) { return TimeSpan.Zero; } return TimeSpan.FromSeconds((double)(sampleCount - _encoderDelay - _encoderPadding) / (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 / OutputChannels; 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 * OutputChannels; _eofFound = false; _decoderDelaySkipped = num3 > 0 || _encoderDelay == 0; _readBufOfs = (_readBufLen = 0); } } } public TimeSpan Time { get { return TimeSpan.FromSeconds((double)_position / 4.0 / (double)OutputChannels / (double)_reader.SampleRate); } set { Position = (long)(value.TotalSeconds * (double)_reader.SampleRate * (double)OutputChannels * 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(); _encoderDelay = _reader.EncoderDelay; _encoderPadding = _reader.EncoderPadding; } 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 long GetTotalBytes() { long sampleCount = _reader.SampleCount; if (sampleCount < 0) { return long.MaxValue; } return (sampleCount - _encoderDelay - _encoderPadding) * OutputChannels * 4; } private bool TryFillReadBuffer() { while (true) { if (_eofFound) { return false; } MpegFrame mpegFrame = _reader.NextFrame(); if (mpegFrame == null) { _eofFound = true; return false; } try { _readBufLen = _decoder.DecodeFrame(mpegFrame, _readBuf, 0) * 4; _readBufOfs = 0; if (!_decoderDelaySkipped) { int num = _encoderDelay * OutputChannels * 4; if (num > 0 && num <= _readBufLen) { _readBufOfs = num; } _decoderDelaySkipped = true; if (_readBufOfs >= _readBufLen) { _readBufLen = (_readBufOfs = 0); } } } catch (InvalidDataException) { _decoder.Reset(); _readBufOfs = (_readBufLen = 0); continue; } catch (EndOfStreamException) { _eofFound = true; return false; } finally { mpegFrame.ClearBuffer(); } break; } return true; } private int AvailableBytes(long totalBytes, int count) { if (_readBufLen <= _readBufOfs) { return 0; } int num = _readBufLen - _readBufOfs; if (num > count) { num = count; } long num2 = totalBytes - _position; if (num > num2) { num = (int)num2; } return num; } private void AdvanceReadBuffer(int temp) { _position += temp; _readBufOfs += temp; if (_readBufOfs == _readBufLen) { _readBufLen = 0; } } private int ReadSamplesImpl(Array buffer, int index, int count, int bitDepth) { int num = 0; long totalBytes = GetTotalBytes(); lock (_seekLock) { while (count > 0) { if (_position >= totalBytes) { _eofFound = true; break; } int num2 = AvailableBytes(totalBytes, count); if (num2 > 0) { if (bitDepth == 32) { Buffer.BlockCopy(_readBuf, _readBufOfs, buffer, index, num2); } else { byte[] array = (byte[])buffer; int num3 = _readBufOfs / 4; int num4 = index / 4; int num5 = num2 / 4; if (bitDepth == 8) { for (int i = 0; i < num5; i++) { array[num4 + i] = (byte)Math.Round(127.5f * _readBuf[num3 + i] + 127.5f); } } else { for (int j = 0; j < num5; j++) { int num6 = (int)Math.Round(32767.5f * _readBuf[num3 + j] - 0.5f); if (num6 < 0) { num6 += 65536; } array[2 * (num4 + j)] = (byte)(num6 % 256); array[2 * (num4 + j) + 1] = (byte)(num6 / 256); } } } num += num2; count -= num2; index += num2; AdvanceReadBuffer(num2); } if (_readBufLen == 0 && !TryFillReadBuffer()) { break; } } } 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 < RequiredSampleCount(frame)) { 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 < RequiredSampleCount(frame)) { 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 DecodeToChannels(IMpegFrame frame) { 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) { return 0; } layerDecoderBase.SetEQ(_eqFactors); layerDecoderBase.StereoMode = StereoMode; return layerDecoderBase.DecodeFrame(frame, _ch0, _ch1); } private bool IsSingleChannel(IMpegFrame frame) { if (frame.ChannelMode != MpegChannelMode.Mono) { return StereoMode != StereoMode.Both; } return true; } private int DecodeFrameImpl(IMpegFrame frame, Array dest, int destOffset) { int num = DecodeToChannels(frame); if (num > 0) { if (IsSingleChannel(frame)) { Buffer.BlockCopy(_ch0, 0, dest, destOffset * 4, num * 4); } else if (dest is float[] array) { for (int i = 0; i < num; i++) { array[destOffset++] = _ch0[i]; array[destOffset++] = _ch1[i]; } num *= 2; } else { for (int j = 0; j < num; j++) { Buffer.BlockCopy(_ch0, j * 4, dest, destOffset * 4, 4); destOffset++; Buffer.BlockCopy(_ch1, j * 4, dest, destOffset * 4, 4); destOffset++; } num *= 2; } return num; } return 0; } private static int RequiredSampleCount(IMpegFrame frame) { return ((frame.ChannelMode == MpegChannelMode.Mono) ? 1 : 2) * frame.SampleCount; } 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 & 8) != 0) { if (br.Get1Bit() == 1) { v = 0f - _floatLookup[1]; } else { v = _floatLookup[1]; } } if ((num & 4) != 0) { if (br.Get1Bit() == 1) { w = 0f - _floatLookup[1]; } else { w = _floatLookup[1]; } } if ((num & 2) != 0) { if (br.Get1Bit() == 1) { x = 0f - _floatLookup[1]; } else { x = _floatLookup[1]; } } if ((num & 1) != 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 i = 0; i < array.Length; i++) { int num = maxBits - lengthList[i]; array[i] = new HuffmanListNode { Value = values[i], Length = lengthList[i], Bits = codeList[i] << num, Mask = (1 << lengthList[i]) - 1 << num }; } Array.Sort(array, (HuffmanListNode huffmanListNode, HuffmanListNode huffmanListNode2) => huffmanListNode.Length - huffmanListNode2.Length); for (int num2 = 1; num2 < array.Length && array[num2].Length < 99999; num2++) { array[num2 - 1].Next = array[num2]; } return array[0]; } } internal class ID3Frame : FrameBase { private int _version; private int _encoderDelay = -1; private int _encoderPadding = -1; private bool _v2Parsed; internal int EncoderDelay { get { if (_encoderDelay >= 0) { return _encoderDelay; } return 0; } } internal int EncoderPadding { get { if (_encoderPadding >= 0) { return _encoderPadding; } return 0; } } 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() { _v2Parsed = true; _encoderDelay = 0; _encoderPadding = 0; byte[] array = new byte[7]; if (Read(3, array) != 7) { return; } int num = array[0]; if (num != 3 && num != 4) { return; } int num2 = (array[3] << 21) | (array[4] << 14) | (array[5] << 7) | array[6]; int i = 10; int num3; for (byte[] array2 = new byte[10]; i < num2 + 10 && Read(i, array2, 0, 10) >= 10; i += 10 + num3) { if (array2[0] == 0) { break; } num3 = ((num != 4) ? ((array2[4] << 24) | (array2[5] << 16) | (array2[6] << 8) | array2[7]) : ((array2[4] << 21) | (array2[5] << 14) | (array2[6] << 7) | array2[7])); if (num3 <= 0) { break; } if (array2[0] == 84 && array2[1] == 88 && array2[2] == 88 && array2[3] == 88 && num3 < 4096) { byte[] array3 = new byte[num3]; if (Read(i + 10, array3, 0, num3) == num3) { TryParseITunSMPB(array3, num3); } } if (_encoderDelay > 0 || _encoderPadding > 0) { break; } } } private void TryParseITunSMPB(byte[] data, int length) { if (length < 2) { return; } int num = data[0]; string text; try { text = ((num == 1 || num == 2) ? Encoding.Unicode : Encoding.UTF8).GetString(data, 1, length - 1); } catch { return; } if (text.Length > 0 && text[0] == '\ufeff') { text = text.Substring(1); } int num2 = text.IndexOf('\0'); if (num2 >= 0 && !(text.Substring(0, num2) != "iTunSMPB")) { string text2 = text.Substring(num2 + 1).Trim(new char[1]); if (text2.Length > 0 && text2[0] == '\ufeff') { text2 = text2.Substring(1); } string[] array = text2.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length >= 3 && int.TryParse(array[1], NumberStyles.HexNumber, null, out var result) && int.TryParse(array[2], NumberStyles.HexNumber, null, out var result2)) { _encoderDelay = result; _encoderPadding = result2; } } } internal void ParseEagerIfNeeded() { if (_version == 2 && !_v2Parsed) { 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, 7 }, 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, -10 } }; 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 float[][] _chanBufs = new float[2][]; 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 - 2) + 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[][] chanBufs = _chanBufs; int num = 0; int num2 = _channels - 1; if (_channels == 1 || base.StereoMode == StereoMode.LeftOnly) { chanBufs[0] = ch0; num2 = 0; } else if (base.StereoMode == StereoMode.RightOnly) { chanBufs[1] = ch0; num = 1; } else { chanBufs[0] = ch0; chanBufs[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[] array; float[] array2; if (num5 < 0) { num5 = -num5 / 2 + -num5 % 2 - 1; array = _groupedC; array2 = _groupedD; } else { array = _C; array2 = _D; } _polyPhaseBuf[num4] = array[num5] * ((float)(_samples[i][num3] << 16 - num5) / 32768f + array2[num5]) * _denormalMultiplier[_scalefac[i][j][num4]]; } else { _polyPhaseBuf[num4] = 0f; } num4++; num3++; } InversePolyPhase(i, _polyPhaseBuf); Array.Copy(_polyPhaseBuf, 0, chanBufs[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 float[] _longImdct_H = new float[17]; private float[] _longImdct_h = new float[18]; private float[] _longImdct_even = new float[9]; private float[] _longImdct_odd = new float[9]; private float[] _longImdct_evenIdct = new float[9]; private float[] _longImdct_oddIdct = new float[9]; private float[] _imdct9pt_even_idct = new float[5]; private float[] _imdct9pt_odd_idct = new float[4]; private float[] _shortImdct_H = new float[6]; private float[] _shortImdct_h = new float[6]; private float[] _shortImdct_evenIdct = new float[3]; private float[] _shortImdct_oddIdct = new float[3]; 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 <