Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CustomSails v1.3.0
CustomSails.dll
Decompiled a month agousing 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)]; } } }