Decompiled source of ShipMusic v0.7.0

plugins/ShipMusic/ShipMusic.dll

Decompiled 4 days ago
using 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
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 <