Decompiled source of CustomSails v1.3.0

CustomSails.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.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 CustomSails
{
	[BepInPlugin("com.rolopogo.CustomSails", "CustomSails", "1.3.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.rolopogo.CustomSails";

		public const string PluginVersion = "1.3.0";

		public static Plugin instance;

		public ConfigEntry<bool> enabledConfig;

		private ConfigEntry<bool> requireKeyConfig;

		private ConfigEntry<KeyboardShortcut> keyConfig;

		private void Awake()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			instance = this;
			enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enables CustomSails");
			requireKeyConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RequireKeyPress", true, "Require holding a key to set or show the sail URL");
			keyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "EditKey", new KeyboardShortcut((KeyCode)306, Array.Empty<KeyCode>()), "Key to hold to set or show the sail URL");
			if (enabledConfig.Value)
			{
				Harmony.CreateAndPatchAll(typeof(Ship_Patch), "com.rolopogo.CustomSails");
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"CustomSails 1.3.0 loaded");
		}

		public bool AllowInput()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (!requireKeyConfig.Value)
			{
				return true;
			}
			KeyboardShortcut value = keyConfig.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 0)
			{
				return true;
			}
			if (!Input.GetKey(((KeyboardShortcut)(ref value)).MainKey))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
			{
				if (!Input.GetKey(modifier))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class Ship_Patch
	{
		[HarmonyPatch(typeof(Ship), "Awake")]
		[HarmonyPostfix]
		private static void Ship_Awake(Ship __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<ShipSailUrlSetter>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<ShipSailUrlSetter>();
			}
		}

		[HarmonyPatch(typeof(Player), "Interact")]
		[HarmonyPrefix]
		private static bool Player_Interact(Player __instance, GameObject go, bool hold, bool alt)
		{
			if (hold || (Object)(object)go == (Object)null || (Object)(object)Plugin.instance == (Object)null || !Plugin.instance.AllowInput())
			{
				return true;
			}
			ShipSailUrlSetter componentInParent = go.GetComponentInParent<ShipSailUrlSetter>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return true;
			}
			componentInParent.Interact((Humanoid)(object)__instance, hold: false, alt);
			return false;
		}

		[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
		[HarmonyPostfix]
		private static void Hud_UpdateCrosshair(Hud __instance, Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)Plugin.instance == (Object)null || !Plugin.instance.AllowInput())
			{
				return;
			}
			GameObject hoverObject = ((Humanoid)player).GetHoverObject();
			if ((Object)(object)hoverObject == (Object)null || hoverObject.GetComponentInParent<Hoverable>() is ShipSailUrlSetter)
			{
				return;
			}
			ShipSailUrlSetter componentInParent = hoverObject.GetComponentInParent<ShipSailUrlSetter>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return;
			}
			string hoverText = componentInParent.GetHoverText();
			if (!string.IsNullOrEmpty(hoverText) && !((Object)(object)__instance.m_hoverName == (Object)null))
			{
				string text = ((TMP_Text)__instance.m_hoverName).text ?? string.Empty;
				if (!text.Contains(hoverText))
				{
					((TMP_Text)__instance.m_hoverName).text = text + hoverText;
				}
			}
		}
	}
	public class ShipSailUrlSetter : MonoBehaviour, Hoverable, Interactable, TextReceiver
	{
		private const int CharacterLimit = 256;

		private const string ZdoKey = "SailUrl";

		private string m_url;

		private ZNetView m_nview;

		private Ship m_ship;

		private readonly List<Renderer> m_sailRenderers = new List<Renderer>();

		private readonly List<Material> m_sailMaterials = new List<Material>();

		private readonly List<Texture> m_ownedTextures = new List<Texture>();

		private void Awake()
		{
			m_nview = ((Component)this).GetComponent<ZNetView>();
			m_ship = ((Component)this).GetComponent<Ship>();
			CacheSailRenderers();
			if (!((Object)(object)m_nview == (Object)null) && m_nview.GetZDO() != null)
			{
				UpdateText();
				((MonoBehaviour)this).InvokeRepeating("UpdateText", 2f, 2f);
			}
		}

		public string GetHoverText()
		{
			if ((Object)(object)Plugin.instance == (Object)null || !Plugin.instance.AllowInput())
			{
				return string.Empty;
			}
			return Localization.instance.Localize("\n[<color=yellow><b>$KEY_Use</b></color>] Set sail URL") + "\n" + GetText();
		}

		public string GetHoverName()
		{
			if ((Object)(object)Plugin.instance == (Object)null || !Plugin.instance.AllowInput())
			{
				return string.Empty;
			}
			return "Sail URL";
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public bool Interact(Humanoid character, bool hold, bool alt)
		{
			if (hold || (Object)(object)Plugin.instance == (Object)null || !Plugin.instance.AllowInput())
			{
				return false;
			}
			if ((Object)(object)TextInput.instance == (Object)null)
			{
				return false;
			}
			TextInput.instance.RequestText((TextReceiver)(object)this, "$piece_sign_input", 256);
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		private void UpdateText()
		{
			if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid())
			{
				string text = GetText();
				if (!(m_url == text))
				{
					ApplyDownloadedUrl(text);
				}
			}
		}

		public string GetText()
		{
			if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid())
			{
				return string.Empty;
			}
			return m_nview.GetZDO().GetString("SailUrl", string.Empty);
		}

		public void SetText(string text)
		{
			ApplyDownloadedUrl(text);
		}

		private void ApplyDownloadedUrl(string text)
		{
			if (!string.IsNullOrWhiteSpace(text) && Uri.TryCreate(text, UriKind.Absolute, out Uri result) && (!(result.Scheme != Uri.UriSchemeHttp) || !(result.Scheme != Uri.UriSchemeHttps)))
			{
				((MonoBehaviour)this).StopAllCoroutines();
				((MonoBehaviour)this).StartCoroutine(DownloadImage(text));
			}
		}

		private void RememberUrl(string url)
		{
			if ((Object)(object)m_nview != (Object)null && m_nview.IsValid())
			{
				m_nview.ClaimOwnership();
				m_nview.GetZDO().Set("SailUrl", url);
			}
			m_url = url;
		}

		private void ApplyTexture(Texture texture)
		{
			if (m_sailMaterials.Count == 0)
			{
				CacheSailRenderers();
			}
			foreach (Material sailMaterial in m_sailMaterials)
			{
				SetMaterialTexture(sailMaterial, texture);
			}
		}

		private static void SetMaterialTexture(Material material, Texture texture)
		{
			if (!((Object)(object)material == (Object)null))
			{
				if (material.HasProperty("_MainTex"))
				{
					material.SetTexture("_MainTex", texture);
				}
				if (material.HasProperty("_BaseMap"))
				{
					material.SetTexture("_BaseMap", texture);
				}
			}
		}

		private void CacheSailRenderers()
		{
			m_sailRenderers.Clear();
			m_sailMaterials.Clear();
			HashSet<Renderer> seen = new HashSet<Renderer>();
			Renderer[] componentsInChildren;
			if ((Object)(object)m_ship != (Object)null && (Object)(object)m_ship.m_sailObject != (Object)null)
			{
				componentsInChildren = m_ship.m_sailObject.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer renderer in componentsInChildren)
				{
					Add(renderer);
				}
			}
			componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (((Object)val).name.IndexOf("sail", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					Add(val);
				}
			}
			void Add(Renderer val2)
			{
				if ((Object)(object)val2 != (Object)null && seen.Add(val2))
				{
					m_sailRenderers.Add(val2);
					Material[] materials = val2.materials;
					for (int j = 0; j < materials.Length; j++)
					{
						if ((Object)(object)materials[j] != (Object)null)
						{
							m_sailMaterials.Add(materials[j]);
						}
					}
				}
			}
		}

		private IEnumerator DownloadImage(string url)
		{
			m_url = url;
			UnityWebRequest request = UnityWebRequest.Get(url);
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					Debug.LogWarning((object)("CustomSails: failed to download " + url + ": " + request.error));
					yield break;
				}
				byte[] data = request.downloadHandler.data;
				if (data == null || data.Length == 0)
				{
					Debug.LogWarning((object)("CustomSails: empty image at " + url));
					yield break;
				}
				if (GifDecoder.IsGif(data))
				{
					List<GifDecoder.Frame> list;
					try
					{
						list = GifDecoder.Decode(data);
					}
					catch (Exception ex)
					{
						Debug.LogWarning((object)("CustomSails: failed to decode GIF " + url + ": " + ex.Message));
						yield break;
					}
					if (list == null || list.Count == 0)
					{
						Debug.LogWarning((object)("CustomSails: GIF had no frames: " + url));
						yield break;
					}
					RememberUrl(url);
					ReplaceOwnedTextures(list);
					if (list.Count == 1)
					{
						ApplyTexture((Texture)(object)list[0].Texture);
					}
					else
					{
						((MonoBehaviour)this).StartCoroutine(PlayGif(list));
					}
					yield break;
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val, data))
				{
					Debug.LogWarning((object)("CustomSails: downloaded data was not a valid image: " + url));
					Object.Destroy((Object)(object)val);
					yield break;
				}
				RememberUrl(url);
				ReplaceOwnedTextures((IEnumerable<Texture>)(object)new Texture2D[1] { val });
				ApplyTexture((Texture)(object)val);
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private IEnumerator PlayGif(List<GifDecoder.Frame> frames)
		{
			int index = 0;
			while (true)
			{
				ApplyTexture((Texture)(object)frames[index].Texture);
				yield return (object)new WaitForSeconds(frames[index].Delay);
				index = (index + 1) % frames.Count;
			}
		}

		private void ReplaceOwnedTextures(IEnumerable<GifDecoder.Frame> frames)
		{
			List<Texture> list = new List<Texture>();
			foreach (GifDecoder.Frame frame in frames)
			{
				if (frame != null && (Object)(object)frame.Texture != (Object)null)
				{
					list.Add((Texture)(object)frame.Texture);
				}
			}
			ReplaceOwnedTextures(list);
		}

		private void ReplaceOwnedTextures(IEnumerable<Texture> textures)
		{
			foreach (Texture ownedTexture in m_ownedTextures)
			{
				if ((Object)(object)ownedTexture != (Object)null)
				{
					Object.Destroy((Object)(object)ownedTexture);
				}
			}
			m_ownedTextures.Clear();
			foreach (Texture texture in textures)
			{
				if ((Object)(object)texture != (Object)null)
				{
					m_ownedTextures.Add(texture);
				}
			}
		}

		private void OnDestroy()
		{
			ReplaceOwnedTextures(Array.Empty<Texture>());
		}
	}
	internal static class GifDecoder
	{
		public sealed class Frame
		{
			public Texture2D Texture;

			public float Delay;
		}

		private const int MaxFrames = 48;

		private const int MaxSide = 1024;

		public static bool IsGif(byte[] data)
		{
			if (data != null && data.Length >= 6 && data[0] == 71 && data[1] == 73)
			{
				return data[2] == 70;
			}
			return false;
		}

		public static List<Frame> Decode(byte[] data)
		{
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Expected O, but got Unknown
			List<Frame> list = new List<Frame>();
			if (!IsGif(data))
			{
				return list;
			}
			using (MemoryStream memoryStream = new MemoryStream(data, writable: false))
			{
				using BinaryReader binaryReader = new BinaryReader(memoryStream);
				binaryReader.ReadBytes(6);
				int num = binaryReader.ReadUInt16();
				int num2 = binaryReader.ReadUInt16();
				if (num <= 0 || num2 <= 0 || num > 1024 || num2 > 1024)
				{
					throw new InvalidDataException($"GIF size {num}x{num2} is not supported");
				}
				byte b = binaryReader.ReadByte();
				int num3 = binaryReader.ReadByte();
				binaryReader.ReadByte();
				Color32[] array = null;
				if ((b & 0x80) != 0)
				{
					array = ReadColorTable(binaryReader, 1 << (b & 7) + 1);
				}
				Color32[] array2 = (Color32[])(object)new Color32[num * num2];
				Color32[] array3 = (Color32[])(object)new Color32[num * num2];
				Color32 color = (Color32)((array != null && num3 < array.Length) ? array[num3] : new Color32((byte)0, (byte)0, (byte)0, (byte)0));
				color.a = 0;
				Fill(array2, color);
				int num4 = 10;
				int num5 = 0;
				int transparentIndex = -1;
				int num6 = 0;
				int x = 0;
				int y = 0;
				int w = 0;
				int h = 0;
				while (memoryStream.Position < memoryStream.Length && list.Count < 48)
				{
					switch (binaryReader.ReadByte())
					{
					case 59:
						break;
					case 33:
						if (binaryReader.ReadByte() == 249)
						{
							int count = binaryReader.ReadByte();
							byte[] array6 = binaryReader.ReadBytes(count);
							SkipSubBlocks(binaryReader);
							if (array6.Length >= 4)
							{
								num5 = (array6[0] >> 2) & 7;
								num4 = array6[1] | (array6[2] << 8);
								transparentIndex = (((array6[0] & 1) != 0) ? array6[3] : (-1));
							}
						}
						else
						{
							SkipSubBlocks(binaryReader);
						}
						continue;
					default:
						SkipSubBlocks(binaryReader);
						continue;
					case 44:
					{
						int num7 = binaryReader.ReadUInt16();
						int num8 = binaryReader.ReadUInt16();
						int num9 = binaryReader.ReadUInt16();
						int num10 = binaryReader.ReadUInt16();
						byte b2 = binaryReader.ReadByte();
						Color32[] array4 = null;
						if ((b2 & 0x80) != 0)
						{
							array4 = ReadColorTable(binaryReader, 1 << (b2 & 7) + 1);
						}
						Color32[] array5 = array4 ?? array;
						if (array5 == null)
						{
							throw new InvalidDataException("GIF frame has no color table");
						}
						switch (num6)
						{
						case 2:
							ClearRect(array2, num, num2, x, y, w, h, color);
							break;
						case 3:
							Array.Copy(array3, array2, array2.Length);
							break;
						}
						if (num5 == 3)
						{
							Array.Copy(array2, array3, array2.Length);
						}
						int minCodeSize = binaryReader.ReadByte();
						byte[] indices = LzwDecode(ReadSubBlockData(binaryReader), minCodeSize, num9 * num10);
						bool interlace = (b2 & 0x40) != 0;
						BlitFrame(array2, num, num2, indices, num9, num10, num7, num8, array5, transparentIndex, interlace);
						Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false);
						((Texture)val).wrapMode = (TextureWrapMode)1;
						((Texture)val).filterMode = (FilterMode)1;
						val.SetPixels32(FlipVertically(array2, num, num2));
						val.Apply(false, false);
						float delay = ((num4 <= 1) ? 0.1f : ((float)num4 / 100f));
						list.Add(new Frame
						{
							Texture = val,
							Delay = delay
						});
						num6 = num5;
						x = num7;
						y = num8;
						w = num9;
						h = num10;
						num4 = 10;
						num5 = 0;
						transparentIndex = -1;
						continue;
					}
					}
					break;
				}
			}
			return list;
		}

		private static Color32[] ReadColorTable(BinaryReader reader, int count)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Color32[] array = (Color32[])(object)new Color32[count];
			for (int i = 0; i < count; i++)
			{
				array[i] = new Color32(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), byte.MaxValue);
			}
			return array;
		}

		private static void SkipSubBlocks(BinaryReader reader)
		{
			while (true)
			{
				int num = reader.ReadByte();
				if (num == 0)
				{
					break;
				}
				reader.BaseStream.Position += num;
			}
		}

		private static byte[] ReadSubBlockData(BinaryReader reader)
		{
			using MemoryStream memoryStream = new MemoryStream();
			while (true)
			{
				int num = reader.ReadByte();
				if (num == 0)
				{
					break;
				}
				memoryStream.Write(reader.ReadBytes(num), 0, num);
			}
			return memoryStream.ToArray();
		}

		private static void Fill(Color32[] pixels, Color32 color)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < pixels.Length; i++)
			{
				pixels[i] = color;
			}
		}

		private static void ClearRect(Color32[] canvas, int width, int height, int x, int y, int w, int h, Color32 color)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < h; i++)
			{
				int num = y + i;
				if (num < 0 || num >= height)
				{
					continue;
				}
				for (int j = 0; j < w; j++)
				{
					int num2 = x + j;
					if (num2 >= 0 && num2 < width)
					{
						canvas[num * width + num2] = color;
					}
				}
			}
		}

		private static void BlitFrame(Color32[] canvas, int width, int height, byte[] indices, int frameWidth, int frameHeight, int left, int top, Color32[] palette, int transparentIndex, bool interlace)
		{
			int[] array = new int[4] { 0, 4, 2, 1 };
			int[] array2 = new int[4] { 8, 8, 4, 2 };
			int sourceRow = 0;
			if (!interlace)
			{
				for (int i = 0; i < frameHeight; i++)
				{
					WriteRow(i);
				}
				return;
			}
			for (int j = 0; j < 4; j++)
			{
				for (int k = array[j]; k < frameHeight; k += array2[j])
				{
					WriteRow(k);
				}
			}
			void WriteRow(int destRow)
			{
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				if (sourceRow < frameHeight)
				{
					int num = sourceRow * frameWidth;
					int num2 = top + destRow;
					if (num2 >= 0 && num2 < height)
					{
						for (int l = 0; l < frameWidth; l++)
						{
							int num3 = left + l;
							if (num3 >= 0 && num3 < width)
							{
								int num4 = indices[num + l];
								if (num4 != transparentIndex && num4 < palette.Length)
								{
									canvas[num2 * width + num3] = palette[num4];
								}
							}
						}
					}
					sourceRow++;
				}
			}
		}

		private static Color32[] FlipVertically(Color32[] source, int width, int height)
		{
			Color32[] array = (Color32[])(object)new Color32[source.Length];
			for (int i = 0; i < height; i++)
			{
				Array.Copy(source, i * width, array, (height - 1 - i) * width, width);
			}
			return array;
		}

		private static byte[] LzwDecode(byte[] compressed, int minCodeSize, int pixelCount)
		{
			byte[] array = new byte[pixelCount];
			int num = 0;
			if (compressed == null || compressed.Length == 0 || minCodeSize < 2)
			{
				return array;
			}
			int num2 = 1 << minCodeSize;
			int num3 = num2 + 1;
			int codeSize = minCodeSize + 1;
			int num4 = num3 + 1;
			int[] array2 = new int[4096];
			byte[] array3 = new byte[4096];
			byte[] array4 = new byte[4096];
			for (int i = 0; i < num2; i++)
			{
				array2[i] = -1;
				array3[i] = (byte)i;
			}
			int dataPos = 0;
			int bitBuffer = 0;
			int bitCount = 0;
			int num5 = -1;
			while (num < pixelCount)
			{
				int num6 = ReadCode();
				if (num6 < 0 || num6 == num3)
				{
					break;
				}
				if (num6 == num2)
				{
					codeSize = minCodeSize + 1;
					num4 = num3 + 1;
					num5 = -1;
					continue;
				}
				int num7 = 0;
				int num8 = num6;
				if (num8 == num4)
				{
					if (num5 < 0)
					{
						break;
					}
					array4[num7++] = GetFirstByte(array2, array3, num5);
					num8 = num5;
				}
				if (num8 >= num4)
				{
					break;
				}
				while (num8 >= num2)
				{
					array4[num7++] = array3[num8];
					num8 = array2[num8];
					if (num8 < 0 || num7 >= array4.Length)
					{
						break;
					}
				}
				if (num8 < 0)
				{
					break;
				}
				array4[num7++] = array3[num8];
				byte b = array4[num7 - 1];
				while (num7 > 0 && num < pixelCount)
				{
					array[num++] = array4[--num7];
				}
				if (num5 >= 0 && num4 < 4096)
				{
					array2[num4] = num5;
					array3[num4] = b;
					num4++;
					if (num4 == 1 << codeSize && codeSize < 12)
					{
						codeSize++;
					}
				}
				num5 = num6;
			}
			return array;
			int ReadCode()
			{
				for (; bitCount < codeSize; bitCount += 8)
				{
					if (dataPos >= compressed.Length)
					{
						return -1;
					}
					bitBuffer |= compressed[dataPos++] << bitCount;
				}
				int result = bitBuffer & ((1 << codeSize) - 1);
				bitBuffer >>= codeSize;
				bitCount -= codeSize;
				return result;
			}
		}

		private static byte GetFirstByte(int[] prefix, byte[] suffix, int code)
		{
			int num = 0;
			while (code >= 0 && prefix[code] != -1 && num++ < 4096)
			{
				code = prefix[code];
			}
			return suffix[Math.Max(code, 0)];
		}
	}
}