Decompiled source of Custom Paintings v0.1.7

BepInEx/plugins/CustomPaintings/CustomPaintings.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using System.Xml;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("0.1.7.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")]
[assembly: AssemblyVersion("0.1.7.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 CustomPaintings
{
	internal sealed class MenuButton : GoPointerButton
	{
		private static readonly FieldInfo OutlineField = AccessTools.Field(typeof(GoPointerButton), "outline");

		internal Action Clicked;

		public override void Start()
		{
			Component component = ((Component)this).GetComponent("Outline");
			if ((Object)(object)component == (Object)null)
			{
				((GoPointerButton)this).Start();
			}
			else
			{
				OutlineField?.SetValue(this, component);
			}
		}

		public override void OnActivate()
		{
			if (!base.unclickable)
			{
				Clicked?.Invoke();
			}
		}
	}
	internal sealed class PaintingPicture : MonoBehaviour
	{
		private static readonly HashSet<string> reportedMeshes = new HashSet<string>();

		private ShipItem item;

		private SaveablePrefab saveable;

		private Renderer canvasRenderer;

		private Material[] originals;

		private Material custom;

		private CanvasShape canvas;

		private bool knownCanvas;

		private Texture2D picture;

		private float pictureAspect;

		private MeshRenderer overlay;

		private Material overlayMaterial;

		private bool failureReported;

		public string Path { get; private set; }

		public FitMode Mode { get; private set; }

		public string Background { get; private set; } = "#000000";

		public bool CanChoose
		{
			get
			{
				if ((Object)(object)item != (Object)null && item.sold && (Object)(object)saveable != (Object)null)
				{
					return saveable.instanceId != 0;
				}
				return false;
			}
		}

		public static bool IsPainting(ShipItem item)
		{
			if ((Object)(object)item != (Object)null && ((object)item).GetType() == typeof(ShipItem) && item.name == "painting" && (Object)(object)((Component)item).GetComponent<Renderer>() != (Object)null)
			{
				return ((Component)item).GetComponent<Renderer>().sharedMaterials.Length >= 1;
			}
			return false;
		}

		public void Init(ShipItem shipItem)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			item = shipItem;
			saveable = ((Component)item).GetComponent<SaveablePrefab>();
			canvasRenderer = ((Component)item).GetComponent<Renderer>();
			originals = canvasRenderer.sharedMaterials;
			MeshFilter component = ((Component)item).GetComponent<MeshFilter>();
			Mesh val = ((component != null) ? component.sharedMesh : null);
			string text = (((Object)(object)val != (Object)null) ? ((Object)val).name : "(no mesh)");
			knownCanvas = PictureFit.TryGetCanvas(text, out canvas);
			if (!knownCanvas && (Object)(object)val != (Object)null)
			{
				Bounds bounds = val.bounds;
				if (((Bounds)(ref bounds)).size.y > 0f)
				{
					CanvasShape canvasShape = default(CanvasShape);
					bounds = val.bounds;
					canvasShape.Width = ((Bounds)(ref bounds)).size.x;
					bounds = val.bounds;
					canvasShape.Height = ((Bounds)(ref bounds)).size.y;
					canvas = canvasShape;
				}
			}
			if (reportedMeshes.Add(text))
			{
				Plugin.Log.LogInfo((object)(knownCanvas ? $"Frame {text}: canvas {canvas.Width:0.000} x {canvas.Height:0.000}, aspect {canvas.Aspect:0.000}." : ("Frame " + text + " is not a known painting frame. Its shape is estimated from its outline, and Fit shows as Fill.")));
			}
			if ((Object)(object)saveable != (Object)null && saveable.instanceId != 0 && Plugin.Store.TryGet(saveable.instanceId, saveable.prefabIndex, out var record))
			{
				Mode = record.FitMode;
				Background = record.BackgroundColor;
				Show(record.Path);
			}
		}

		public void Choose(string path, FitMode mode, string background)
		{
			if (CanChoose)
			{
				Mode = mode;
				Background = background;
				Plugin.Store.Set(saveable.instanceId, saveable.prefabIndex, path, mode, background);
				failureReported = false;
				if (path == Path && (Object)(object)picture == (Object)null)
				{
					Show(null, restore: false);
				}
				if (path == Path)
				{
					Apply();
				}
				else
				{
					Show(path);
				}
			}
		}

		public void SetFit(FitMode mode, string background)
		{
			if (Path != null)
			{
				Choose(Path, mode, background);
			}
		}

		public void ChooseOriginal()
		{
			if ((Object)(object)saveable != (Object)null)
			{
				Plugin.Store.Remove(saveable.instanceId);
			}
			Show(null);
		}

		private void Show(string path, bool restore = true)
		{
			if (path == Path)
			{
				return;
			}
			PictureTextures.Instance?.Release(Path, this);
			Path = path;
			picture = null;
			if (path == null)
			{
				if (restore)
				{
					RestoreOriginal();
				}
			}
			else
			{
				PictureTextures.Instance?.Acquire(path, this);
			}
		}

		public void OnPictureReady(Texture2D texture, float aspect)
		{
			picture = texture;
			pictureAspect = aspect;
			Apply();
		}

		private void Apply()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)canvasRenderer == (Object)null || (Object)(object)picture == (Object)null)
			{
				return;
			}
			if ((Object)(object)custom == (Object)null)
			{
				custom = new Material(originals[0])
				{
					name = ((Object)originals[0]).name + " (custom picture)"
				};
			}
			FitMode num = ((Mode != FitMode.Fit || knownCanvas) ? Mode : FitMode.Fill);
			bool flag = num == FitMode.Fit;
			if (custom.HasProperty("_Color"))
			{
				if (flag)
				{
					PictureFit.ParseColor(Background, out var r, out var g, out var b);
					custom.color = new Color(r, g, b, 1f);
				}
				else
				{
					custom.color = (originals[0].HasProperty("_Color") ? originals[0].color : Color.white);
				}
			}
			custom.mainTexture = (Texture)(object)(flag ? Texture2D.whiteTexture : picture);
			PictureFit.Crop(num, pictureAspect, canvas.Aspect, out var scaleX, out var scaleY, out var offsetX, out var offsetY);
			custom.mainTextureScale = new Vector2(scaleX, scaleY);
			custom.mainTextureOffset = new Vector2(offsetX, offsetY);
			Material[] array = (Material[])originals.Clone();
			array[0] = custom;
			canvasRenderer.sharedMaterials = array;
			if (flag)
			{
				EnsureOverlay();
				overlayMaterial.mainTexture = (Texture)(object)picture;
				PictureFit.FitSize(pictureAspect, canvas.Aspect, out var widthFraction, out var heightFraction);
				((Component)overlay).transform.localPosition = new Vector3(canvas.CenterX, canvas.CenterY, canvas.Z - 0.003f);
				((Component)overlay).transform.localScale = new Vector3(canvas.Width * widthFraction, canvas.Height * heightFraction, 1f);
				((Renderer)overlay).enabled = true;
			}
			else if ((Object)(object)overlay != (Object)null)
			{
				((Renderer)overlay).enabled = false;
			}
		}

		private void EnsureOverlay()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)overlay != (Object)null)
			{
				return;
			}
			GameObject val = new GameObject("Custom Paintings picture");
			val.layer = ((Component)this).gameObject.layer;
			val.transform.SetParent(((Component)this).transform, false);
			val.AddComponent<MeshFilter>().sharedMesh = Quad.Mesh;
			overlay = val.AddComponent<MeshRenderer>();
			((Renderer)overlay).shadowCastingMode = (ShadowCastingMode)0;
			overlayMaterial = new Material(originals[0])
			{
				name = ((Object)originals[0]).name + " (custom picture overlay)"
			};
			if (overlayMaterial.HasProperty("_Color"))
			{
				overlayMaterial.color = (originals[0].HasProperty("_Color") ? originals[0].color : Color.white);
			}
			overlayMaterial.mainTextureScale = Vector2.one;
			overlayMaterial.mainTextureOffset = Vector2.zero;
			((Renderer)overlay).sharedMaterial = overlayMaterial;
			LODGroup component = ((Component)this).GetComponent<LODGroup>();
			if ((Object)(object)component != (Object)null)
			{
				LOD[] lODs = component.GetLODs();
				if (lODs.Length != 0)
				{
					List<Renderer> list = new List<Renderer>((IEnumerable<Renderer>)(((object)lODs[0].renderers) ?? ((object)new Renderer[0]))) { (Renderer)(object)overlay };
					lODs[0].renderers = list.ToArray();
					component.SetLODs(lODs);
				}
			}
		}

		public void OnPictureFailed()
		{
			picture = null;
			RestoreOriginal();
			if (!failureReported)
			{
				failureReported = true;
				Plugin.Log.LogWarning((object)("A painting keeps its original art because its picture could not be loaded: " + Path));
			}
		}

		private void RestoreOriginal()
		{
			if ((Object)(object)canvasRenderer != (Object)null && originals != null)
			{
				canvasRenderer.sharedMaterials = originals;
			}
			if ((Object)(object)overlay != (Object)null)
			{
				((Renderer)overlay).enabled = false;
			}
			if ((Object)(object)custom != (Object)null)
			{
				Object.Destroy((Object)(object)custom);
			}
			custom = null;
		}

		private void OnDestroy()
		{
			PictureTextures.Instance?.Release(Path, this);
			if ((Object)(object)custom != (Object)null)
			{
				Object.Destroy((Object)(object)custom);
			}
			if ((Object)(object)overlayMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)overlayMaterial);
			}
		}
	}
	internal static class Quad
	{
		private static Mesh mesh;

		public static Mesh Mesh
		{
			get
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Expected O, but got Unknown
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0064: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0095: Unknown result type (might be due to invalid IL or missing references)
				//IL_009a: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
				//IL_0102: Unknown result type (might be due to invalid IL or missing references)
				//IL_0119: Unknown result type (might be due to invalid IL or missing references)
				//IL_011e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0125: Unknown result type (might be due to invalid IL or missing references)
				//IL_012a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0131: Unknown result type (might be due to invalid IL or missing references)
				//IL_0136: Unknown result type (might be due to invalid IL or missing references)
				//IL_013d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0142: Unknown result type (might be due to invalid IL or missing references)
				//IL_016d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0172: Unknown result type (might be due to invalid IL or missing references)
				//IL_018d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0192: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
				//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)mesh != (Object)null)
				{
					return mesh;
				}
				mesh = new Mesh
				{
					name = "Custom Paintings quad"
				};
				mesh.vertices = (Vector3[])(object)new Vector3[4]
				{
					new Vector3(-0.5f, -0.5f, 0f),
					new Vector3(0.5f, -0.5f, 0f),
					new Vector3(0.5f, 0.5f, 0f),
					new Vector3(-0.5f, 0.5f, 0f)
				};
				mesh.uv = (Vector2[])(object)new Vector2[4]
				{
					new Vector2(0f, 0f),
					new Vector2(1f, 0f),
					new Vector2(1f, 1f),
					new Vector2(0f, 1f)
				};
				mesh.normals = (Vector3[])(object)new Vector3[4]
				{
					Vector3.back,
					Vector3.back,
					Vector3.back,
					Vector3.back
				};
				mesh.tangents = (Vector4[])(object)new Vector4[4]
				{
					new Vector4(1f, 0f, 0f, -1f),
					new Vector4(1f, 0f, 0f, -1f),
					new Vector4(1f, 0f, 0f, -1f),
					new Vector4(1f, 0f, 0f, -1f)
				};
				mesh.triangles = new int[6] { 0, 3, 2, 0, 2, 1 };
				mesh.RecalculateBounds();
				return mesh;
			}
		}
	}
	internal enum FitMode
	{
		Fill,
		Fit,
		Stretch
	}
	internal struct CanvasShape
	{
		public float Width;

		public float Height;

		public float CenterX;

		public float CenterY;

		public float Z;

		public float Aspect => Width / Height;
	}
	internal static class PictureFit
	{
		private static readonly Dictionary<string, CanvasShape> Canvases = new Dictionary<string, CanvasShape>
		{
			{
				"painting",
				new CanvasShape
				{
					Width = 1.331f,
					Height = 0.832f,
					Z = -0.009f
				}
			},
			{
				"painting_square",
				new CanvasShape
				{
					Width = 1.234f,
					Height = 1.165f,
					CenterX = 0.001f,
					CenterY = 0.0005f,
					Z = -0.01f
				}
			},
			{
				"painting_almost_square",
				new CanvasShape
				{
					Width = 1.097f,
					Height = 0.832f,
					Z = -0.009f
				}
			},
			{
				"painting_wide",
				new CanvasShape
				{
					Width = 1.531f,
					Height = 0.832f,
					Z = -0.009f
				}
			},
			{
				"painting_wide_wide",
				new CanvasShape
				{
					Width = 1.871f,
					Height = 0.832f,
					Z = -0.009f
				}
			},
			{
				"painting_vertical",
				new CanvasShape
				{
					Width = 1.032f,
					Height = 1.331f,
					Z = -0.009f
				}
			},
			{
				"painting_vertical_long",
				new CanvasShape
				{
					Width = 1.261f,
					Height = 1.837f,
					Z = -0.008f
				}
			}
		};

		public static bool TryGetCanvas(string meshName, out CanvasShape canvas)
		{
			canvas = default(CanvasShape);
			if (string.IsNullOrEmpty(meshName))
			{
				return false;
			}
			string key = (meshName.EndsWith(" Instance", StringComparison.Ordinal) ? meshName.Substring(0, meshName.Length - " Instance".Length) : meshName);
			return Canvases.TryGetValue(key, out canvas);
		}

		public static void TargetSize(int width, int height, int maxSide, out int targetWidth, out int targetHeight)
		{
			if (width <= 0 || height <= 0)
			{
				throw new ArgumentOutOfRangeException("width");
			}
			long num = Math.Max(width, height);
			long num2 = Math.Min(num, maxSide);
			targetWidth = (int)Math.Max(4L, width * num2 / num / 4 * 4);
			targetHeight = (int)Math.Max(4L, height * num2 / num / 4 * 4);
		}

		public static void Crop(FitMode mode, float pictureAspect, float canvasAspect, out float scaleX, out float scaleY, out float offsetX, out float offsetY)
		{
			scaleX = (scaleY = 1f);
			offsetX = (offsetY = 0f);
			if (mode == FitMode.Fill && !(pictureAspect <= 0f) && !(canvasAspect <= 0f))
			{
				if (pictureAspect > canvasAspect)
				{
					scaleX = canvasAspect / pictureAspect;
					offsetX = (1f - scaleX) / 2f;
				}
				else
				{
					scaleY = pictureAspect / canvasAspect;
					offsetY = (1f - scaleY) / 2f;
				}
			}
		}

		public static void FitSize(float pictureAspect, float canvasAspect, out float widthFraction, out float heightFraction)
		{
			widthFraction = (heightFraction = 1f);
			if (!(pictureAspect <= 0f) && !(canvasAspect <= 0f))
			{
				if (pictureAspect > canvasAspect)
				{
					heightFraction = canvasAspect / pictureAspect;
				}
				else
				{
					widthFraction = pictureAspect / canvasAspect;
				}
			}
		}

		public static void ParseColor(string hex, out float r, out float g, out float b)
		{
			r = (g = (b = 0f));
			if (hex != null && hex.Length == 7 && hex[0] == '#' && int.TryParse(hex.Substring(1), NumberStyles.HexNumber, null, out var result))
			{
				r = (float)((result >> 16) & 0xFF) / 255f;
				g = (float)((result >> 8) & 0xFF) / 255f;
				b = (float)(result & 0xFF) / 255f;
			}
		}
	}
	internal struct LibraryEntry
	{
		public string Path;

		public bool IsFolder;

		public string Name
		{
			get
			{
				if (!IsFolder)
				{
					return System.IO.Path.GetFileNameWithoutExtension(Path);
				}
				return System.IO.Path.GetFileName(Path.TrimEnd('\\', '/'));
			}
		}
	}
	internal static class PictureLibrary
	{
		public static List<string> ParseFolders(string setting)
		{
			List<string> list = new List<string>();
			if (string.IsNullOrEmpty(setting))
			{
				return list;
			}
			string[] array = setting.Split(new char[1] { '|' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim().Trim(new char[1] { '"' }).Trim();
				if (text.Length > 0 && !list.Contains(text))
				{
					list.Add(text);
				}
			}
			return list;
		}

		public static bool IsPicture(string path)
		{
			string extension = Path.GetExtension(path);
			if (!extension.Equals(".png", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".jpg", StringComparison.OrdinalIgnoreCase))
			{
				return extension.Equals(".jpeg", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		public static List<LibraryEntry> List(string folder, out string problem)
		{
			problem = null;
			List<LibraryEntry> list = new List<LibraryEntry>();
			if (!Directory.Exists(folder))
			{
				problem = "Folder not found: " + folder;
				return list;
			}
			try
			{
				List<string> list2 = new List<string>(Directory.GetDirectories(folder));
				List<string> list3 = new List<string>();
				string[] files = Directory.GetFiles(folder);
				foreach (string text in files)
				{
					if (IsPicture(text))
					{
						list3.Add(text);
					}
				}
				list2.Sort(NaturalCompare);
				list3.Sort(NaturalCompare);
				foreach (string item in list2)
				{
					list.Add(new LibraryEntry
					{
						Path = item,
						IsFolder = true
					});
				}
				foreach (string item2 in list3)
				{
					list.Add(new LibraryEntry
					{
						Path = item2
					});
				}
			}
			catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
			{
				problem = "This folder could not be read: " + ex.Message;
			}
			return list;
		}

		public static List<LibraryEntry> Filter(List<LibraryEntry> entries, string query)
		{
			query = query?.Trim();
			if (string.IsNullOrEmpty(query))
			{
				return entries;
			}
			List<LibraryEntry> list = new List<LibraryEntry>();
			foreach (LibraryEntry entry in entries)
			{
				if (entry.Name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					list.Add(entry);
				}
			}
			return list;
		}

		public static int PageCount(int count, int perPage)
		{
			return Math.Max(1, (count + perPage - 1) / perPage);
		}

		public static int NaturalCompare(string a, string b)
		{
			a = Path.GetFileName((a ?? "").TrimEnd('\\', '/'));
			b = Path.GetFileName((b ?? "").TrimEnd('\\', '/'));
			int i = 0;
			int j = 0;
			while (i < a.Length && j < b.Length)
			{
				if (char.IsDigit(a[i]) && char.IsDigit(b[j]))
				{
					int num = i;
					int num2 = j;
					for (; i < a.Length && char.IsDigit(a[i]); i++)
					{
					}
					for (; j < b.Length && char.IsDigit(b[j]); j++)
					{
					}
					string text = a.Substring(num, i - num).TrimStart(new char[1] { '0' });
					string text2 = b.Substring(num2, j - num2).TrimStart(new char[1] { '0' });
					if (text.Length != text2.Length)
					{
						return text.Length.CompareTo(text2.Length);
					}
					int num3 = string.CompareOrdinal(text, text2);
					if (num3 != 0)
					{
						return num3;
					}
				}
				else
				{
					int num4 = char.ToUpperInvariant(a[i]).CompareTo(char.ToUpperInvariant(b[j]));
					if (num4 != 0)
					{
						return num4;
					}
					i++;
					j++;
				}
			}
			int num5 = (a.Length - i).CompareTo(b.Length - j);
			if (num5 == 0)
			{
				return string.Compare(a, b, StringComparison.OrdinalIgnoreCase);
			}
			return num5;
		}
	}
	internal sealed class PicturePicker : MonoBehaviour
	{
		private sealed class Cell
		{
			public MenuButton Folder;

			public MenuButton Thumb;

			public Material ThumbMaterial;

			public TextMesh Caption;

			public TextMesh Status;

			public LibraryEntry Entry;

			public bool HasEntry;

			public bool WaitingForThumbnail;
		}

		private const float MaxButtonText = 0.0125f;

		private const int Columns = 5;

		private const int Rows = 4;

		private const int PerPage = 20;

		private const float CellPitch = 1f;

		private const float ThumbWidth = 0.88f;

		private const float ThumbHeight = 0.44f;

		private const float PaperWidth = 6.2f;

		private const float PaperHeight = 4.4f;

		private const float PaperScreenHeight = 1.07f;

		private const float PaperScreenWidth = 1.05f;

		private const float PageDistance = 2.5f;

		private static readonly float[] RowY = new float[4] { 0.86f, 0.24f, -0.38f, -1f };

		private static readonly Color Ink = new Color(0.06f, 0.035f, 0.02f, 1f);

		private static readonly Color MutedInk = new Color(0.27f, 0.21f, 0.17f, 1f);

		private static readonly Color MarkInk = new Color(0.45f, 0.08f, 0.03f, 1f);

		private static readonly Color Placeholder = new Color(0.85f, 0.8f, 0.7f, 1f);

		private static readonly FieldInfo ChoiceUiField = AccessTools.Field(typeof(StartMenu), "chooseIslandUI");

		private static readonly string[] Swatches = new string[8] { "#000000", "#FFFFFF", "#E8DCC0", "#7A7A7A", "#2B2B2B", "#1F2F4A", "#5A1E1E", "#3E2A1A" };

		private GameObject holder;

		private Transform root;

		private GameObject buttonTemplate;

		private TextMesh titleTemplate;

		private TextMesh fontTemplate;

		private Camera uiCamera;

		private Shader flatShader;

		private Material flatFallback;

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

		private readonly Dictionary<MenuButton, float> buttonWidths = new Dictionary<MenuButton, float>();

		private readonly Cell[] cells = new Cell[20];

		private TextMesh pathText;

		private TextMesh pageText;

		private TextMesh messageText;

		private TextMesh borderLabel;

		private MenuButton backButton;

		private MenuButton searchButton;

		private MenuButton clearButton;

		private MenuButton previousButton;

		private MenuButton nextButton;

		private MenuButton[] modeButtons;

		private MenuButton[] swatchButtons;

		private PaintingPicture target;

		private List<string> roots = new List<string>();

		private int rootIndex = -1;

		private string folder;

		private List<LibraryEntry> listing = new List<LibraryEntry>();

		private List<LibraryEntry> shown = new List<LibraryEntry>();

		private string problem;

		private string query = "";

		private bool searchFocused;

		private int page;

		private FitMode mode;

		private string background;

		private readonly List<string> wanted = new List<string>();

		internal static PicturePicker Instance { get; private set; }

		public bool IsOpen => ((Behaviour)this).enabled;

		public bool SearchFocused
		{
			get
			{
				if (((Behaviour)this).enabled)
				{
					return searchFocused;
				}
				return false;
			}
		}

		private bool AtTop => rootIndex < 0;

		private void Awake()
		{
			Instance = this;
			((Behaviour)this).enabled = false;
		}

		public void Open(PaintingPicture painting)
		{
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			if (EnsureBuilt())
			{
				target = painting;
				mode = painting.Mode;
				background = painting.Background;
				List<string> a = PictureLibrary.ParseFolders(Plugin.PictureFolders);
				if (!SameFolders(a, roots))
				{
					roots = a;
					rootIndex = -1;
					folder = null;
					page = 0;
				}
				if (rootIndex >= 0 && !Inside(folder, roots[rootIndex]))
				{
					folder = roots[rootIndex];
				}
				query = "";
				searchFocused = false;
				LoadListing(keepPage: true);
				bool flag = Settings.VRLookEnabled && (Object)(object)Refs.observerMirror != (Object)null;
				Transform val = (flag ? ((Component)Refs.observerMirror).transform : (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : null));
				holder.transform.SetParent(val, false);
				holder.transform.localPosition = (flag ? new Vector3(0f, 0f, 1.3f) : new Vector3(0f, -1.1f, 1.3f));
				holder.transform.localRotation = Quaternion.identity;
				holder.transform.localScale = Vector3.one * 0.5f;
				root.localPosition = new Vector3(0f, flag ? 0f : 2.2f, 2.4f);
				Camera val2 = (((Object)(object)uiCamera != (Object)null) ? uiCamera : Camera.main);
				float num = (((Object)(object)val2 != (Object)null) ? val2.fieldOfView : 60f);
				float num2 = (((Object)(object)val2 != (Object)null) ? val2.aspect : 1.7777778f);
				float num3 = 5f * Mathf.Tan(num * 0.5f * ((float)Math.PI / 180f));
				float num4 = Mathf.Min(1.07f * num3 / 2.2f, 1.05f * num3 * num2 / 3.1f);
				root.localScale = Vector3.one * num4;
				holder.SetActive(true);
				MouseLook.ToggleMouseLookAndCursor(false);
				Refs.SetPlayerControl(false);
				GameState.wasInSettingsMenu = true;
				((Behaviour)this).enabled = true;
				Refresh();
			}
		}

		public void Close()
		{
			if (((Behaviour)this).enabled)
			{
				((Behaviour)this).enabled = false;
				target = null;
				searchFocused = false;
				if ((Object)(object)holder != (Object)null)
				{
					holder.SetActive(false);
				}
				PictureTextures.Instance?.ClearThumbnails();
				MouseLook.ToggleMouseLookAndCursor(true);
				Refs.SetPlayerControl(true);
				((MonoBehaviour)this).StartCoroutine(ClearSettingsFlag());
			}
		}

		private static IEnumerator ClearSettingsFlag()
		{
			yield return (object)new WaitForEndOfFrame();
			GameState.wasInSettingsMenu = false;
		}

		public void Blur()
		{
			searchFocused = false;
			Refresh();
		}

		private void Update()
		{
			if ((Object)(object)target == (Object)null || GameState.currentlyLoading || (Object)(object)holder == (Object)null)
			{
				Close();
				return;
			}
			if (searchFocused)
			{
				ReadSearchInput();
			}
			float axis = Input.GetAxis("Mouse ScrollWheel");
			if (axis > 0f)
			{
				ChangePage(-1);
			}
			else if (axis < 0f)
			{
				ChangePage(1);
			}
			Cell[] array = cells;
			foreach (Cell cell in array)
			{
				if (cell.WaitingForThumbnail)
				{
					ShowThumbnail(cell);
				}
			}
		}

		private void ReadSearchInput()
		{
			string text = query;
			bool flag = searchFocused;
			string inputString = Input.inputString;
			for (int i = 0; i < inputString.Length; i++)
			{
				char c = inputString[i];
				switch (c)
				{
				case '\b':
					if (query.Length > 0)
					{
						query = query.Substring(0, query.Length - 1);
					}
					break;
				case '\n':
				case '\r':
					searchFocused = false;
					break;
				default:
					if (!char.IsControl(c) && query.Length < 40)
					{
						query += c;
					}
					break;
				}
			}
			if (Input.GetKeyDown((KeyCode)127))
			{
				query = "";
			}
			if (query != text)
			{
				page = 0;
				Filter();
			}
			if (query != text || searchFocused != flag)
			{
				Refresh();
			}
		}

		private void LoadListing(bool keepPage = false)
		{
			problem = null;
			if (rootIndex < 0)
			{
				listing = new List<LibraryEntry>();
				foreach (string root in roots)
				{
					listing.Add(new LibraryEntry
					{
						Path = root,
						IsFolder = true
					});
				}
			}
			else
			{
				listing = PictureLibrary.List(folder, out problem);
			}
			if (!keepPage)
			{
				page = 0;
			}
			Filter();
		}

		private void Filter()
		{
			shown = PictureLibrary.Filter(listing, query);
			page = Mathf.Clamp(page, 0, PictureLibrary.PageCount(shown.Count, 20) - 1);
		}

		private void Enter(LibraryEntry entry)
		{
			if (rootIndex < 0)
			{
				rootIndex = roots.IndexOf(entry.Path);
			}
			folder = entry.Path;
			query = "";
			searchFocused = false;
			LoadListing();
			Refresh();
		}

		private void Back()
		{
			if (rootIndex >= 0)
			{
				if (SamePath(folder, roots[rootIndex]))
				{
					rootIndex = -1;
					folder = null;
				}
				else
				{
					folder = Path.GetDirectoryName(Normalize(folder));
				}
				query = "";
				searchFocused = false;
				LoadListing();
				Refresh();
			}
		}

		private static string Normalize(string path)
		{
			return path?.TrimEnd('\\', '/');
		}

		private static bool SamePath(string a, string b)
		{
			return string.Equals(Normalize(a), Normalize(b), StringComparison.OrdinalIgnoreCase);
		}

		private static bool Inside(string path, string rootPath)
		{
			if (path != null && Directory.Exists(path))
			{
				return Normalize(path).StartsWith(Normalize(rootPath), StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool SameFolders(List<string> a, List<string> b)
		{
			if (a.Count != b.Count)
			{
				return false;
			}
			for (int i = 0; i < a.Count; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}

		private string PathLabel()
		{
			if (rootIndex < 0)
			{
				return "Picture folders";
			}
			string text = Normalize(roots[rootIndex]);
			string text2 = Path.GetFileName(text);
			if (string.IsNullOrEmpty(text2))
			{
				text2 = text;
			}
			string text3 = Normalize(folder);
			string text4 = ((text3.Length > text.Length) ? text3.Substring(text.Length).Trim('\\', '/') : "");
			if (text4.Length == 0)
			{
				return text2;
			}
			return text2 + " / " + string.Join(" / ", text4.Split(new char[2] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries));
		}

		private void ChangePage(int direction)
		{
			int num = Mathf.Clamp(page + direction, 0, PictureLibrary.PageCount(shown.Count, 20) - 1);
			if (num != page)
			{
				page = num;
				Refresh();
			}
		}

		private void OnCell(int index)
		{
			Cell cell = cells[index];
			if (!((Object)(object)target == (Object)null) && cell.HasEntry)
			{
				searchFocused = false;
				if (cell.Entry.IsFolder)
				{
					Enter(cell.Entry);
					return;
				}
				target.Choose(cell.Entry.Path, mode, background);
				Close();
			}
		}

		private void SetMode(FitMode next)
		{
			mode = next;
			target.SetFit(mode, background);
			Refresh();
		}

		private void SetBackground(string color)
		{
			background = color;
			target.SetFit(mode, background);
			Refresh();
		}

		private void Refresh()
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)holder == (Object)null || (Object)(object)target == (Object)null)
			{
				return;
			}
			pathText.text = Trim(PathLabel(), 70);
			SetVisible(backButton, !AtTop);
			SetText(searchButton, searchFocused ? (query + "_") : ((query.Length > 0) ? query : "Search this folder"), (searchFocused || query.Length > 0) ? Ink : MutedInk);
			SetVisible(clearButton, query.Length > 0);
			string text = null;
			if (roots.Count == 0)
			{
				text = "Add picture folders to the Picture folders setting\nin the config file or Configuration Manager.";
			}
			else if (shown.Count == 0)
			{
				text = problem ?? ((query.Length > 0) ? ("Nothing here matches \"" + query + "\".") : "No folders or PNG or JPG pictures here.");
			}
			messageText.text = text ?? "";
			wanted.Clear();
			for (int i = 0; i < 20; i++)
			{
				int num = page * 20 + i;
				Cell cell = cells[i];
				cell.HasEntry = text == null && num < shown.Count;
				cell.WaitingForThumbnail = false;
				if (!cell.HasEntry)
				{
					SetVisible(cell.Folder, visible: false);
					((Component)cell.Thumb).gameObject.SetActive(false);
					TextMesh caption = cell.Caption;
					string text2 = (cell.Status.text = "");
					caption.text = text2;
					continue;
				}
				cell.Entry = shown[num];
				if (cell.Entry.IsFolder)
				{
					SetVisible(cell.Folder, visible: true);
					((Component)cell.Thumb).gameObject.SetActive(false);
					SetText(cell.Folder, Wrap(cell.Entry.Name, 13, 3), Ink);
					cell.Caption.text = "folder";
					cell.Caption.color = MutedInk;
					cell.Status.text = "";
					continue;
				}
				SetVisible(cell.Folder, visible: false);
				cell.Caption.text = Trim(cell.Entry.Name, 22);
				cell.Caption.color = (SamePath(cell.Entry.Path, target.Path) ? MarkInk : Ink);
				ShowThumbnail(cell);
				if (cell.WaitingForThumbnail)
				{
					wanted.Add(cell.Entry.Path);
				}
			}
			PictureTextures.Instance?.WantThumbnails(wanted);
			int num2 = PictureLibrary.PageCount(shown.Count, 20);
			pageText.text = ((text == null && num2 > 1) ? ("Page " + (page + 1) + " of " + num2) : "");
			SetVisible(previousButton, text == null && page > 0);
			SetVisible(nextButton, text == null && page < num2 - 1);
			for (int j = 0; j < modeButtons.Length; j++)
			{
				MenuButton button = modeButtons[j];
				FitMode fitMode = (FitMode)j;
				SetText(button, fitMode.ToString(), (j == (int)mode) ? MarkInk : Ink);
			}
			bool flag = mode == FitMode.Fit;
			borderLabel.text = (flag ? "Border" : "");
			for (int k = 0; k < swatchButtons.Length; k++)
			{
				((Component)swatchButtons[k]).gameObject.SetActive(flag);
				float num3 = ((Swatches[k] == background) ? 0.22f : 0.17f);
				((Component)swatchButtons[k]).transform.localScale = new Vector3(num3, num3, 1f);
			}
		}

		private void ShowThumbnail(Cell cell)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			PictureTextures instance = PictureTextures.Instance;
			Texture2D val = instance?.Thumbnail(cell.Entry.Path);
			if ((Object)(object)val != (Object)null)
			{
				cell.ThumbMaterial.mainTexture = (Texture)(object)val;
				cell.ThumbMaterial.color = Color.white;
				float num = (float)((Texture)val).width / (float)((Texture)val).height;
				float num2 = 0.88f;
				float num3 = 0.88f / num;
				if (num3 > 0.44f)
				{
					num3 = 0.44f;
					num2 = 0.44f * num;
				}
				((Component)cell.Thumb).transform.localScale = new Vector3(num2, num3, 1f);
				((Component)cell.Thumb).gameObject.SetActive(true);
				cell.Status.text = "";
				cell.WaitingForThumbnail = false;
			}
			else
			{
				bool flag = (Object)(object)instance != (Object)null && instance.ThumbnailFailed(cell.Entry.Path);
				cell.ThumbMaterial.mainTexture = (Texture)(object)Texture2D.whiteTexture;
				cell.ThumbMaterial.color = Placeholder;
				((Component)cell.Thumb).transform.localScale = new Vector3(0.704f, 0.352f, 1f);
				((Component)cell.Thumb).gameObject.SetActive(true);
				cell.Status.text = (flag ? "can't read" : "loading");
				cell.WaitingForThumbnail = !flag;
			}
		}

		private static string Trim(string value, int maximum)
		{
			if (value.Length > maximum)
			{
				return value.Substring(0, maximum - 3) + "...";
			}
			return value;
		}

		private static string Wrap(string value, int width, int lines)
		{
			List<string> list = new List<string>();
			string text = value;
			while (text.Length > 0 && list.Count < lines)
			{
				if (text.Length <= width)
				{
					list.Add(text);
					text = "";
					break;
				}
				int num = text.LastIndexOf(' ', width);
				if (num <= 0)
				{
					num = width;
				}
				list.Add(text.Substring(0, num).TrimEnd(Array.Empty<char>()));
				text = text.Substring(num).TrimStart(Array.Empty<char>());
			}
			if (text.Length > 0)
			{
				list[list.Count - 1] = Trim(list[list.Count - 1] + " " + text, width);
			}
			return string.Join("\n", list.ToArray());
		}

		private bool EnsureBuilt()
		{
			if ((Object)(object)holder != (Object)null)
			{
				return true;
			}
			try
			{
				Build();
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Custom Paintings could not build its picture menu: " + ex));
				if ((Object)(object)holder != (Object)null)
				{
					Object.Destroy((Object)(object)holder);
				}
				holder = null;
				return false;
			}
		}

		private void Build()
		{
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: 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_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0356: Unknown result type (might be due to invalid IL or missing references)
			//IL_043c: Unknown result type (might be due to invalid IL or missing references)
			//IL_045c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0478: Unknown result type (might be due to invalid IL or missing references)
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0491: Unknown result type (might be due to invalid IL or missing references)
			//IL_056e: Unknown result type (might be due to invalid IL or missing references)
			//IL_060f: Unknown result type (might be due to invalid IL or missing references)
			//IL_06bb: Unknown result type (might be due to invalid IL or missing references)
			StartMenu val = Object.FindObjectOfType<StartMenu>();
			GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val2 == (Object)null)
			{
				throw new InvalidOperationException("Sailwind's menu panel was not found.");
			}
			Transform transform = val2.transform;
			Transform val3 = transform.Find("button confirm");
			Transform obj = transform.Find("text title");
			TextMesh val4 = ((obj != null) ? ((Component)obj).GetComponent<TextMesh>() : null);
			Transform val5 = transform.Find("bg");
			object obj2;
			if (!((Object)(object)val3 != (Object)null))
			{
				obj2 = null;
			}
			else
			{
				Transform obj3 = val3.Find("text");
				obj2 = ((obj3 != null) ? ((Component)obj3).GetComponent<TextMesh>() : null);
			}
			fontTemplate = (TextMesh)obj2;
			if ((Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null || (Object)(object)fontTemplate == (Object)null)
			{
				throw new InvalidOperationException("Sailwind's menu button, text or panel templates were not found.");
			}
			buttonTemplate = ((Component)val3).gameObject;
			titleTemplate = val4;
			GameObject obj4 = GameObject.Find("UI camera");
			uiCamera = ((obj4 != null) ? obj4.GetComponent<Camera>() : null);
			flatShader = Shader.Find("Sprites/Default");
			Renderer component = ((Component)val5).GetComponent<Renderer>();
			flatFallback = ((component != null) ? component.sharedMaterial : null);
			holder = new GameObject("Custom Paintings picture menu");
			holder.SetActive(false);
			holder.layer = val2.layer;
			root = new GameObject("page").transform;
			((Component)root).gameObject.layer = val2.layer;
			root.SetParent(holder.transform, false);
			GameObject obj5 = Object.Instantiate<GameObject>(((Component)val5).gameObject, root, false);
			((Object)obj5).name = "paper";
			obj5.transform.localPosition = new Vector3(0f, 0f, 0.06f);
			obj5.transform.localRotation = val5.localRotation;
			MeshFilter component2 = obj5.GetComponent<MeshFilter>();
			Mesh val6 = ((component2 != null) ? component2.sharedMesh : null);
			Vector3 val7;
			if (!((Object)(object)val6 != (Object)null))
			{
				val7 = Vector3.one;
			}
			else
			{
				Bounds bounds = val6.bounds;
				val7 = ((Bounds)(ref bounds)).size;
			}
			Vector3 val8 = val7;
			obj5.transform.localScale = new Vector3(val5.localScale.x, 6.2f / Mathf.Max(0.01f, val8.y), 4.4f / Mathf.Max(0.01f, val8.z));
			Label("Choose a Picture", 0f, 1.6f, 0.017f, Ink);
			pathText = Label("", 0f, 1.43f, 0.0095f, Ink);
			backButton = Button("Back", -2.25f, 1.27f, 0.5f, 0.17f, Back);
			searchButton = Button("Search this folder", 0.05f, 1.27f, 3.2f, 0.17f, delegate
			{
				searchFocused = true;
				Refresh();
			});
			clearButton = Button("Clear", 2.3f, 1.27f, 0.48f, 0.17f, delegate
			{
				query = "";
				page = 0;
				Filter();
				Refresh();
			});
			messageText = Label("", 0f, 0f, 0.01f, Ink);
			for (int num = 0; num < 20; num++)
			{
				float x = ((float)(num % 5) - 2f) * 1f;
				float num2 = RowY[num / 5];
				int index = num;
				Cell cell = new Cell();
				cell.Folder = Button("Folder", x, num2, 0.88f, 0.44f, delegate
				{
					OnCell(index);
				});
				cell.Thumb = Flat("thumbnail " + num, x, num2, 0.03f, out cell.ThumbMaterial);
				cell.Thumb.Clicked = delegate
				{
					OnCell(index);
				};
				cell.Caption = Label("", x, num2 - 0.22f - 0.075f, 0.0088f, Ink);
				cell.Status = Label("", x, num2, 0.0085f, Ink);
				Transform transform2 = ((Component)cell.Status).transform;
				transform2.localPosition += new Vector3(0f, 0f, -0.01f);
				cells[num] = cell;
			}
			modeButtons = new MenuButton[3];
			float[] array = new float[3] { -2.3f, -1.82f, -1.26f };
			float[] array2 = new float[3] { 0.44f, 0.44f, 0.58f };
			for (int num3 = 0; num3 < modeButtons.Length; num3++)
			{
				FitMode fitMode = (FitMode)num3;
				modeButtons[num3] = Button(fitMode.ToString(), array[num3], -1.48f, array2[num3], 0.15f, delegate
				{
					SetMode(fitMode);
				});
			}
			borderLabel = Label("", -0.72f, -1.48f, 0.009f, Ink);
			swatchButtons = new MenuButton[Swatches.Length];
			for (int num4 = 0; num4 < Swatches.Length; num4++)
			{
				string color = Swatches[num4];
				swatchButtons[num4] = Flat("border " + color, -0.38f + (float)num4 * 0.25f, -1.48f, 0.03f, out var material);
				PictureFit.ParseColor(color, out var r, out var g, out var b);
				material.color = new Color(r, g, b, 1f);
				swatchButtons[num4].Clicked = delegate
				{
					SetBackground(color);
				};
			}
			Button("Original Art", -2.15f, -1.72f, 0.78f, 0.17f, delegate
			{
				target.ChooseOriginal();
				Close();
			});
			previousButton = Button("<", -0.6f, -1.72f, 0.27f, 0.17f, delegate
			{
				ChangePage(-1);
			});
			pageText = Label("", 0f, -1.72f, 0.0095f, Ink);
			nextButton = Button(">", 0.6f, -1.72f, 0.27f, 0.17f, delegate
			{
				ChangePage(1);
			});
			Button("Close", 2.2f, -1.72f, 0.6f, 0.17f, Close);
		}

		private TextMesh Label(string value, float x, float y, float scale, Color color)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(((Component)titleTemplate).gameObject, root, false);
			((Object)obj).name = "label";
			obj.transform.localPosition = new Vector3(x, y, ((Component)titleTemplate).transform.localPosition.z);
			obj.transform.localRotation = ((Component)titleTemplate).transform.localRotation;
			obj.transform.localScale = Vector3.one * scale;
			TextMesh component = obj.GetComponent<TextMesh>();
			component.font = fontTemplate.font;
			component.fontStyle = fontTemplate.fontStyle;
			component.fontSize = fontTemplate.fontSize;
			component.characterSize = fontTemplate.characterSize;
			((Component)component).GetComponent<Renderer>().sharedMaterial = ((Component)fontTemplate).GetComponent<Renderer>().sharedMaterial;
			component.anchor = (TextAnchor)4;
			component.alignment = (TextAlignment)1;
			component.color = color;
			component.text = value;
			return component;
		}

		private MenuButton Button(string label, float x, float y, float width, float height, Action onClick)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(buttonTemplate, root, false);
			((Object)obj).name = "button " + label;
			obj.transform.localPosition = new Vector3(x, y, buttonTemplate.transform.localPosition.z);
			obj.transform.localRotation = buttonTemplate.transform.localRotation;
			obj.transform.localScale = Vector3.one;
			StartMenuButton componentInChildren = obj.GetComponentInChildren<StartMenuButton>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				throw new InvalidOperationException("The cloned menu button has no pointer target.");
			}
			Transform transform = ((Component)componentInChildren).transform;
			Object.DestroyImmediate((Object)(object)componentInChildren);
			MeshFilter component = ((Component)transform).GetComponent<MeshFilter>();
			Mesh val = ((component != null) ? component.sharedMesh : null);
			Vector3 val2;
			if (!((Object)(object)val != (Object)null))
			{
				val2 = Vector3.one;
			}
			else
			{
				Bounds bounds = val.bounds;
				val2 = ((Bounds)(ref bounds)).size;
			}
			Vector3 val3 = val2;
			transform.localScale = new Vector3(width / Mathf.Max(0.01f, val3.x), height / Mathf.Max(0.01f, val3.y), transform.localScale.z);
			TextMesh[] componentsInChildren = obj.GetComponentsInChildren<TextMesh>(true);
			for (int i = 1; i < componentsInChildren.Length; i++)
			{
				((Component)componentsInChildren[i]).gameObject.SetActive(false);
			}
			MenuButton button = ((Component)transform).gameObject.AddComponent<MenuButton>();
			button.Clicked = delegate
			{
				if ((Object)(object)button != (Object)(object)searchButton)
				{
					searchFocused = false;
				}
				onClick();
			};
			((GoPointerButton)button).lookText = "";
			((GoPointerButton)button).description = "";
			buttonWidths[button] = width;
			SetText(button, label, Ink);
			return button;
		}

		private MenuButton Flat(string name, float x, float y, float z, out Material material)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name)
			{
				layer = ((Component)root).gameObject.layer
			};
			val.transform.SetParent(root, false);
			val.transform.localPosition = new Vector3(x, y, z);
			val.AddComponent<MeshFilter>().sharedMesh = Quad.Mesh;
			MeshRenderer obj = val.AddComponent<MeshRenderer>();
			((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)obj).receiveShadows = false;
			material = (((Object)(object)flatShader != (Object)null) ? new Material(flatShader) : new Material(flatFallback));
			material.mainTexture = (Texture)(object)Texture2D.whiteTexture;
			material.mainTextureScale = Vector2.one;
			material.mainTextureOffset = Vector2.zero;
			ownedMaterials.Add(material);
			((Renderer)obj).sharedMaterial = material;
			val.AddComponent<BoxCollider>().size = new Vector3(1f, 1f, 0.02f);
			return val.AddComponent<MenuButton>();
		}

		private static TextMesh ButtonText(MenuButton button)
		{
			if (!((Object)(object)button != (Object)null))
			{
				return null;
			}
			return ((Component)((Component)button).transform.parent).GetComponentInChildren<TextMesh>(true);
		}

		private void SetText(MenuButton button, string value, Color color)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			TextMesh val = ButtonText(button);
			if (!((Object)(object)val == (Object)null))
			{
				val.text = value;
				val.color = color;
				int num = 1;
				string[] array = value.Split(new char[1] { '\n' });
				foreach (string text in array)
				{
					num = Math.Max(num, text.Length);
				}
				buttonWidths.TryGetValue(button, out var value2);
				((Component)val).transform.localScale = Vector3.one * Mathf.Min(0.0125f, value2 * 0.85f / (3.85f * (float)num));
			}
		}

		private static void SetVisible(MenuButton button, bool visible)
		{
			if ((Object)(object)button != (Object)null)
			{
				((Component)((Component)button).transform.parent).gameObject.SetActive(visible);
			}
		}

		private void OnDestroy()
		{
			foreach (Material ownedMaterial in ownedMaterials)
			{
				if ((Object)(object)ownedMaterial != (Object)null)
				{
					Object.Destroy((Object)(object)ownedMaterial);
				}
			}
			if ((Object)(object)holder != (Object)null)
			{
				Object.Destroy((Object)(object)holder);
			}
		}
	}
	[DataContract]
	internal sealed class PictureRecord
	{
		[DataMember]
		public int InstanceId;

		[DataMember]
		public int PrefabIndex;

		[DataMember]
		public string Path;

		[DataMember(EmitDefaultValue = false)]
		public string Mode;

		[DataMember(EmitDefaultValue = false)]
		public string Background;

		public const string DefaultBackground = "#000000";

		public FitMode FitMode
		{
			get
			{
				if (Mode == null || !Enum.IsDefined(typeof(FitMode), Mode))
				{
					return FitMode.Fill;
				}
				return (FitMode)Enum.Parse(typeof(FitMode), Mode);
			}
		}

		public string BackgroundColor => Background ?? "#000000";
	}
	[DataContract]
	internal sealed class PictureSaveDocument
	{
		[DataMember(IsRequired = true)]
		public int Schema = 1;

		[DataMember(IsRequired = true)]
		public List<PictureRecord> Pictures = new List<PictureRecord>();
	}
	internal sealed class PictureStore
	{
		public const string Key = "com.skeptic043.sailwind.custompaintings";

		private readonly Dictionary<int, PictureRecord> records = new Dictionary<int, PictureRecord>();

		public int Count => records.Count;

		public bool Writable { get; private set; } = true;

		public string Problem { get; private set; } = "";

		public void Reset()
		{
			records.Clear();
			Writable = true;
			Problem = "";
		}

		public bool Load(string json)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Reset();
			if (string.IsNullOrEmpty(json))
			{
				return true;
			}
			try
			{
				PictureSaveDocument pictureSaveDocument;
				using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
				{
					pictureSaveDocument = (PictureSaveDocument)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(PictureSaveDocument))).ReadObject((Stream)memoryStream);
				}
				if (pictureSaveDocument == null || pictureSaveDocument.Schema != 1 || pictureSaveDocument.Pictures == null)
				{
					throw new SerializationException("unsupported picture save schema");
				}
				foreach (PictureRecord picture in pictureSaveDocument.Pictures)
				{
					if (picture == null || picture.InstanceId == 0 || string.IsNullOrEmpty(picture.Path))
					{
						throw new SerializationException("invalid picture record");
					}
					if (records.ContainsKey(picture.InstanceId))
					{
						throw new SerializationException("duplicate picture identity");
					}
					records.Add(picture.InstanceId, picture);
				}
				return true;
			}
			catch (Exception ex) when (ex is SerializationException || ex is ArgumentException || ex is XmlException || ex is FormatException || ex is InvalidCastException)
			{
				records.Clear();
				Writable = false;
				Problem = ex.Message;
				return false;
			}
		}

		public string Serialize()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			PictureSaveDocument pictureSaveDocument = new PictureSaveDocument();
			pictureSaveDocument.Pictures.AddRange(records.Values);
			pictureSaveDocument.Pictures.Sort((PictureRecord a, PictureRecord b) => a.InstanceId.CompareTo(b.InstanceId));
			using MemoryStream memoryStream = new MemoryStream();
			((XmlObjectSerializer)new DataContractJsonSerializer(typeof(PictureSaveDocument))).WriteObject((Stream)memoryStream, (object)pictureSaveDocument);
			return Encoding.UTF8.GetString(memoryStream.ToArray());
		}

		public bool TryGet(int instanceId, int prefabIndex, out PictureRecord record)
		{
			if (records.TryGetValue(instanceId, out record) && record.PrefabIndex == prefabIndex)
			{
				return true;
			}
			record = null;
			return false;
		}

		public void Set(int instanceId, int prefabIndex, string path, FitMode mode, string background)
		{
			Writable = true;
			records[instanceId] = new PictureRecord
			{
				InstanceId = instanceId,
				PrefabIndex = prefabIndex,
				Path = path,
				Mode = ((mode == FitMode.Fill) ? null : mode.ToString()),
				Background = ((background == null || background == "#000000") ? null : background)
			};
		}

		public void Remove(int instanceId)
		{
			Writable = true;
			records.Remove(instanceId);
		}
	}
	internal sealed class PictureTextures : MonoBehaviour
	{
		private sealed class Entry
		{
			public Texture2D Texture;

			public float Aspect;

			public bool Loading;

			public bool Failed;

			public bool ReloadQueued;

			public string Stamp;

			public int MaxSize;

			public bool Compressed;

			public readonly HashSet<PaintingPicture> Owners = new HashSet<PaintingPicture>();
		}

		public const int ThumbnailSide = 192;

		private const long KeptBytes = 16777216L;

		private const int KeptThumbnails = 60;

		private readonly Dictionary<string, Entry> pictures = new Dictionary<string, Entry>(StringComparer.OrdinalIgnoreCase);

		private readonly List<KeyValuePair<string, Entry>> kept = new List<KeyValuePair<string, Entry>>();

		private readonly Queue<string> pictureQueue = new Queue<string>();

		private readonly Dictionary<string, Texture2D> thumbnails = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> failedThumbnails = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly List<string> wantedThumbnails = new List<string>();

		private string loadingThumbnail;

		private bool working;

		internal static PictureTextures Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		public void Acquire(string path, PaintingPicture owner)
		{
			if (!pictures.TryGetValue(path, out var value))
			{
				value = TakeKept(path) ?? new Entry();
				pictures.Add(path, value);
			}
			value.Owners.Add(owner);
			if ((Object)(object)value.Texture != (Object)null)
			{
				owner.OnPictureReady(value.Texture, value.Aspect);
				return;
			}
			value.Failed = false;
			Enqueue(path, value);
		}

		public void Release(string path, PaintingPicture owner)
		{
			if (path == null || !pictures.TryGetValue(path, out var value))
			{
				return;
			}
			value.Owners.Remove(owner);
			if (value.Owners.Count <= 0 && !value.Loading)
			{
				pictures.Remove(path);
				if ((Object)(object)value.Texture != (Object)null)
				{
					Keep(path, value);
				}
			}
		}

		private void Keep(string path, Entry entry)
		{
			kept.Add(new KeyValuePair<string, Entry>(path, entry));
			long num = 0L;
			foreach (KeyValuePair<string, Entry> item in kept)
			{
				num += Bytes(item.Value.Texture);
			}
			while (num > 16777216 && kept.Count > 0)
			{
				num -= Bytes(kept[0].Value.Texture);
				Object.Destroy((Object)(object)kept[0].Value.Texture);
				kept.RemoveAt(0);
			}
		}

		private Entry TakeKept(string path)
		{
			int num = kept.FindIndex((KeyValuePair<string, Entry> pair) => string.Equals(pair.Key, path, StringComparison.OrdinalIgnoreCase));
			if (num < 0)
			{
				return null;
			}
			Entry value = kept[num].Value;
			kept.RemoveAt(num);
			if ((Object)(object)value.Texture != (Object)null && value.MaxSize == Plugin.MaxSize && value.Compressed == Plugin.Compress && value.Stamp != null && value.Stamp == Stamp(path))
			{
				return value;
			}
			if ((Object)(object)value.Texture != (Object)null)
			{
				Object.Destroy((Object)(object)value.Texture);
			}
			return null;
		}

		private void DropKept()
		{
			foreach (KeyValuePair<string, Entry> item in kept)
			{
				if ((Object)(object)item.Value.Texture != (Object)null)
				{
					Object.Destroy((Object)(object)item.Value.Texture);
				}
			}
			kept.Clear();
		}

		private static long Bytes(Texture2D texture)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			if ((Object)(object)texture == (Object)null)
			{
				return 0L;
			}
			long num = (long)((Texture)texture).width * (long)((Texture)texture).height;
			long num2 = (((int)texture.format == 10) ? (num / 2) : (((int)texture.format == 12) ? num : (num * 3)));
			if (texture.mipmapCount <= 1)
			{
				return num2;
			}
			return num2 * 4 / 3;
		}

		private static string Stamp(string path)
		{
			try
			{
				FileInfo fileInfo = new FileInfo(path);
				return fileInfo.Exists ? (fileInfo.LastWriteTimeUtc.Ticks + ":" + fileInfo.Length) : null;
			}
			catch
			{
				return null;
			}
		}

		public void ReloadAll()
		{
			DropKept();
			foreach (KeyValuePair<string, Entry> picture in pictures)
			{
				ReloadEntry(picture.Key, picture.Value);
			}
		}

		private void ReloadEntry(string path, Entry entry)
		{
			entry.Failed = false;
			if (entry.Loading)
			{
				entry.ReloadQueued = true;
			}
			else
			{
				Enqueue(path, entry);
			}
		}

		public Texture2D Thumbnail(string path)
		{
			thumbnails.TryGetValue(path, out var value);
			return value;
		}

		public bool ThumbnailFailed(string path)
		{
			return failedThumbnails.Contains(path);
		}

		public void ClearThumbnails()
		{
			wantedThumbnails.Clear();
			foreach (Texture2D value in thumbnails.Values)
			{
				Object.Destroy((Object)(object)value);
			}
			thumbnails.Clear();
			failedThumbnails.Clear();
		}

		public void WantThumbnails(List<string> paths)
		{
			wantedThumbnails.Clear();
			foreach (string path in paths)
			{
				if (!thumbnails.ContainsKey(path) && !failedThumbnails.Contains(path) && !string.Equals(path, loadingThumbnail, StringComparison.OrdinalIgnoreCase))
				{
					wantedThumbnails.Add(path);
				}
			}
			if (thumbnails.Count > 60)
			{
				HashSet<string> hashSet = new HashSet<string>(paths, StringComparer.OrdinalIgnoreCase);
				foreach (string item in new List<string>(thumbnails.Keys))
				{
					if (thumbnails.Count <= 60)
					{
						break;
					}
					if (!hashSet.Contains(item))
					{
						Object.Destroy((Object)(object)thumbnails[item]);
						thumbnails.Remove(item);
					}
				}
			}
			Pump();
		}

		private void Enqueue(string path, Entry entry)
		{
			if (!entry.Loading)
			{
				entry.Loading = true;
				pictureQueue.Enqueue(path);
				Pump();
			}
		}

		private void Pump()
		{
			if (!working && (pictureQueue.Count > 0 || wantedThumbnails.Count > 0))
			{
				((MonoBehaviour)this).StartCoroutine(Work());
			}
		}

		private IEnumerator Work()
		{
			working = true;
			try
			{
				while (true)
				{
					if (pictureQueue.Count > 0)
					{
						string path = pictureQueue.Dequeue();
						yield return LoadPicture(path);
					}
					else
					{
						if (wantedThumbnails.Count <= 0)
						{
							break;
						}
						string path2 = wantedThumbnails[0];
						wantedThumbnails.RemoveAt(0);
						yield return LoadThumbnail(path2);
					}
					yield return null;
				}
			}
			finally
			{
				working = false;
			}
		}

		private IEnumerator LoadPicture(string path)
		{
			if (!pictures.TryGetValue(path, out var entry))
			{
				yield break;
			}
			Texture2D result = null;
			float aspect = 0f;
			string stamp = Stamp(path);
			int maxSize = Plugin.MaxSize;
			bool compressed = Plugin.Compress;
			yield return Decode(path, maxSize, mipmaps: true, compressed, delegate(Texture2D val, float sourceAspect)
			{
				result = val;
				aspect = sourceAspect;
			});
			entry.Loading = false;
			if (entry.Owners.Count == 0)
			{
				if ((Object)(object)result != (Object)null)
				{
					Object.Destroy((Object)(object)result);
				}
				if ((Object)(object)entry.Texture != (Object)null)
				{
					Object.Destroy((Object)(object)entry.Texture);
				}
				pictures.Remove(path);
				yield break;
			}
			bool reloadQueued = entry.ReloadQueued;
			entry.ReloadQueued = false;
			if ((Object)(object)result == (Object)null)
			{
				if ((Object)(object)entry.Texture == (Object)null)
				{
					entry.Failed = true;
					foreach (PaintingPicture item in new List<PaintingPicture>(entry.Owners))
					{
						item.OnPictureFailed();
					}
				}
			}
			else
			{
				Texture2D texture = entry.Texture;
				entry.Texture = result;
				entry.Aspect = aspect;
				entry.Stamp = stamp;
				entry.MaxSize = maxSize;
				entry.Compressed = compressed;
				foreach (PaintingPicture item2 in new List<PaintingPicture>(entry.Owners))
				{
					item2.OnPictureReady(result, aspect);
				}
				if ((Object)(object)texture != (Object)null)
				{
					Object.Destroy((Object)(object)texture);
				}
			}
			if (reloadQueued)
			{
				ReloadEntry(path, entry);
			}
		}

		private IEnumerator LoadThumbnail(string path)
		{
			Texture2D result = null;
			loadingThumbnail = path;
			yield return Decode(path, 192, mipmaps: false, compress: true, delegate(Texture2D texture, float _)
			{
				result = texture;
			});
			loadingThumbnail = null;
			if ((Object)(object)result != (Object)null && thumbnails.ContainsKey(path))
			{
				Object.Destroy((Object)(object)result);
			}
			else if ((Object)(object)result != (Object)null && ((Object)(object)PicturePicker.Instance == (Object)null || !PicturePicker.Instance.IsOpen))
			{
				Object.Destroy((Object)(object)result);
			}
			else if ((Object)(object)result != (Object)null)
			{
				thumbnails[path] = result;
			}
			else
			{
				failedThumbnails.Add(path);
			}
		}

		private static IEnumerator Decode(string path, int maxSide, bool mipmaps, bool compress, Action<Texture2D, float> done)
		{
			string absoluteUri;
			try
			{
				absoluteUri = new Uri(Path.GetFullPath(path)).AbsoluteUri;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Not a usable picture path " + path + ": " + ex.Message));
				done(null, 0f);
				yield break;
			}
			UnityWebRequest request = UnityWebRequestTexture.GetTexture(absoluteUri, true);
			Texture2D texture;
			float aspect;
			try
			{
				yield return request.SendWebRequest();
				texture = ReadBack(path, request, maxSide, mipmaps, out aspect);
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
			if ((Object)(object)texture == (Object)null)
			{
				done(null, 0f);
				yield break;
			}
			bool finished = false;
			try
			{
				yield return null;
				if (!Step(path, delegate
				{
					texture.Apply(mipmaps, !compress);
				}))
				{
					yield break;
				}
				if (compress)
				{
					yield return null;
					if (!Step(path, delegate
					{
						texture.Compress(false);
						texture.Apply(false, true);
					}))
					{
						yield break;
					}
				}
				finished = true;
			}
			finally
			{
				if (!finished && (Object)(object)texture != (Object)null)
				{
					Object.Destroy((Object)(object)texture);
				}
			}
			done(texture, aspect);
		}

		private static bool Step(string path, Action action)
		{
			try
			{
				action();
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not prepare picture " + path + ": " + ex.Message));
				return false;
			}
		}

		private static Texture2D ReadBack(string path, UnityWebRequest request, int maxSide, bool mipmaps, out float aspect)
		{
			aspect = 0f;
			Texture2D val = null;
			try
			{
				if (request.isNetworkError || request.isHttpError)
				{
					Plugin.Log.LogWarning((object)("Could not read picture " + path + ": " + request.error));
					return null;
				}
				val = DownloadHandlerTexture.GetContent(request);
				if ((Object)(object)val == (Object)null || ((Texture)val).width <= 0 || ((Texture)val).height <= 0)
				{
					Plugin.Log.LogWarning((object)("Could not decode picture " + path));
					return null;
				}
				aspect = (float)((Texture)val).width / (float)((Texture)val).height;
				PictureFit.TargetSize(((Texture)val).width, ((Texture)val).height, maxSide, out var targetWidth, out var targetHeight);
				return Resample((Texture)(object)val, targetWidth, targetHeight, mipmaps);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not prepare picture " + path + ": " + ex.Message));
				return null;
			}
			finally
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}

		private static Texture2D Resample(Texture source, int width, int height, bool mipmaps)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			RenderTexture val = null;
			RenderTexture val2 = null;
			Texture2D val3 = null;
			RenderTexture active = RenderTexture.active;
			try
			{
				Texture val4 = source;
				int num = source.width;
				int num2 = source.height;
				while (num / 2 >= width && num2 / 2 >= height)
				{
					num /= 2;
					num2 /= 2;
					RenderTexture temporary = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
					((Texture)temporary).filterMode = (FilterMode)1;
					Graphics.Blit(val4, temporary);
					if ((Object)(object)val != (Object)null)
					{
						RenderTexture.ReleaseTemporary(val);
					}
					val4 = (Texture)(object)(val = temporary);
				}
				if ((Object)(object)val != (Object)null && num == width && num2 == height)
				{
					val2 = val;
					val = null;
				}
				else
				{
					val2 = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
					Graphics.Blit(val4, val2);
				}
				val3 = new Texture2D(width, height, (TextureFormat)3, mipmaps, false);
				RenderTexture.active = val2;
				val3.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0, false);
				RenderTexture.active = active;
				((Texture)val3).wrapMode = (TextureWrapMode)1;
				((Texture)val3).filterMode = (FilterMode)((!mipmaps) ? 1 : 2);
				((Texture)val3).anisoLevel = (mipmaps ? 4 : 0);
				Texture2D result = val3;
				val3 = null;
				return result;
			}
			finally
			{
				RenderTexture.active = active;
				if ((Object)(object)val != (Object)null)
				{
					RenderTexture.ReleaseTemporary(val);
				}
				if ((Object)(object)val2 != (Object)null)
				{
					RenderTexture.ReleaseTemporary(val2);
				}
				if ((Object)(object)val3 != (Object)null)
				{
					Object.Destroy((Object)(object)val3);
				}
			}
		}
	}
	[BepInPlugin("com.skeptic043.sailwind.custompaintings", "Custom Paintings", "0.1.7")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ShipItem), "OnLoad")]
		private static class AttachPatch
		{
			private static void Postfix(ShipItem __instance)
			{
				Guard("attach", delegate
				{
					if (PaintingPicture.IsPainting(__instance) && (Object)(object)Picture((Component)(object)__instance) == (Object)null)
					{
						((Component)__instance).gameObject.AddComponent<PaintingPicture>().Init(__instance);
					}
				});
			}
		}

		[HarmonyPatch(typeof(ShipItem), "OnAltActivate", new Type[] { })]
		private static class AltActivatePatch
		{
			private static bool Prefix(ShipItem __instance)
			{
				bool consumed = false;
				Guard("open picker", delegate
				{
					PaintingPicture paintingPicture = Picture((Component)(object)__instance);
					if (!((Object)(object)paintingPicture == (Object)null) && paintingPicture.CanChoose && !((Object)(object)PicturePicker.Instance == (Object)null) && !PicturePicker.Instance.IsOpen)
					{
						PicturePicker.Instance.Open(paintingPicture);
						consumed = true;
					}
				});
				return !consumed;
			}
		}

		[HarmonyPatch(typeof(StartMenu), "LateUpdate")]
		private static class EscapePatch
		{
			private static bool Prefix()
			{
				PicturePicker picker = PicturePicker.Instance;
				if ((Object)(object)picker == (Object)null || !picker.IsOpen)
				{
					return true;
				}
				if (!Input.GetKeyDown("escape") && !Input.GetKeyDown("f10") && !Input.GetKeyDown((KeyCode)336))
				{
					return true;
				}
				Guard("escape", delegate
				{
					if (picker.SearchFocused)
					{
						picker.Blur();
					}
					else
					{
						picker.Close();
					}
				});
				return false;
			}
		}

		private static class PointerAccess
		{
			public static readonly FieldRef<GoPointer, RaycastHit> Hit = AccessTools.FieldRefAccess<GoPointer, RaycastHit>("hit");

			public static readonly FieldRef<GoPointer, GoPointerButton> PointedAt = AccessTools.FieldRefAccess<GoPointer, GoPointerButton>("pointedAtButton");

			public static readonly FieldRef<GoPointer, float> LookDistance = AccessTools.FieldRefAccess<GoPointer, float>("currentLookDistance");
		}

		[HarmonyPatch(typeof(GoPointer), "DoRaycast")]
		private static class PointerTargetPatch
		{
			private static void Postfix(GoPointer __instance)
			{
				try
				{
					Target(__instance);
				}
				catch (Exception exception)
				{
					Failed("pointer target", exception);
				}
			}

			private static void Target(GoPointer __instance)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				//IL_005b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				if (((int)__instance.type == 2 && GameState.inCursorMenu) || (Object)(object)PointerAccess.PointedAt.Invoke(__instance) != (Object)null || (Object)(object)__instance.GetHeldItem() != (Object)null || GameState.sleeping || (Object)(object)GameState.inBed != (Object)null || BoatCamera.on)
				{
					return;
				}
				RaycastHit val = PointerAccess.Hit.Invoke(__instance);
				Collider val2 = ((RaycastHit)(ref val)).collider;
				if (!((Object)(object)val2 == (Object)null))
				{
					if (((Component)val2).CompareTag("ItemSubcollider"))
					{
						val2 = ((Component)((Component)val2).transform.parent).GetComponent<Collider>();
					}
					ShipItem val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<ShipItem>() : null);
					if (!((Object)(object)val3 == (Object)null) && val3.nailed && !((GoPointerButton)val3).unclickable && !((Object)(object)Picture((Component)(object)val3) == (Object)null))
					{
						PointerAccess.PointedAt.Invoke(__instance) = (GoPointerButton)(object)val3;
						PointerAccess.LookDistance.Invoke(__instance) = ((RaycastHit)(ref val)).distance;
					}
				}
			}
		}

		private static class LookUiAccess
		{
			public static readonly FieldRef<LookUI, TextMesh> ControlsText = AccessTools.FieldRefAccess<LookUI, TextMesh>("controlsText");

			public static readonly FieldRef<LookUI, GoPointer> Pointer = AccessTools.FieldRefAccess<LookUI, GoPointer>("pointer");

			public static readonly Action<LookUI> ShowRicon = AccessTools.MethodDelegate<Action<LookUI>>(AccessTools.Method(typeof(LookUI), "ShowRicon", (Type[])null, (Type[])null), (object)null, true);

			public static readonly Action<LookUI> HideIcons = AccessTools.MethodDelegate<Action<LookUI>>(AccessTools.Method(typeof(LookUI), "HideIcons", (Type[])null, (Type[])null), (object)null, true);
		}

		[HarmonyPatch(typeof(LookUI), "ShowLookText")]
		private static class LookPromptPatch
		{
			private static void Postfix(LookUI __instance, GoPointerButton button)
			{
				try
				{
					Prompt(__instance, button);
				}
				catch (Exception exception)
				{
					Failed("look prompt", exception);
				}
			}

			private static void Prompt(LookUI __instance, GoPointerButton button)
			{
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				if (button.IsStickyClicked())
				{
					return;
				}
				PaintingPicture paintingPicture = Picture((Component)(object)button);
				if ((Object)(object)paintingPicture == (Object)null || !paintingPicture.CanChoose)
				{
					return;
				}
				GoPointer val = LookUiAccess.Pointer.Invoke(__instance);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.GetHeldItem() != (Object)null))
				{
					if (((ShipItem)button).nailed)
					{
						__instance.ClearText();
					}
					else if (Settings.controlsTextEnabled)
					{
						LookUiAccess.ShowRicon(__instance);
						TextMesh obj = LookUiAccess.ControlsText.Invoke(__instance);
						obj.text = obj.text.Split(new char[1] { '\n' })[0] + "\nchange picture";
					}
				}
			}
		}

		[HarmonyPatch(typeof(LookUI), "ShowHoldText")]
		private static class HoldPromptPatch
		{
			private static void Postfix(LookUI __instance, PickupableItem item)
			{
				try
				{
					PaintingPicture paintingPicture = Picture((Component)(object)item);
					if (Settings.controlsTextEnabled && !((Object)(object)paintingPicture == (Object)null) && paintingPicture.CanChoose)
					{
						LookUiAccess.ShowRicon(__instance);
						LookUiAccess.ControlsText.Invoke(__instance).text = "change picture";
					}
				}
				catch (Exception exception)
				{
					Failed("hold prompt", exception);
				}
			}
		}

		[HarmonyPatch(typeof(SaveLoadManager), "SaveModData")]
		private static class SavePatch
		{
			private static void Postfix()
			{
				Guard("save", delegate
				{
					if (Store.Writable)
					{
						if (Store.Count > 0)
						{
							GameState.modData["com.skeptic043.sailwind.custompaintings"] = Store.Serialize();
						}
						else
						{
							GameState.modData.Remove("com.skeptic043.sailwind.custompaintings");
						}
					}
				});
			}
		}

		[HarmonyPatch(typeof(SaveLoadManager), "LoadGame")]
		private static class LoadGamePatch
		{
			private static void Prefix()
			{
				Guard("load reset", Reset);
			}
		}

		[HarmonyPatch(typeof(SaveLoadManager), "LoadModData")]
		private static class LoadModDataPatch
		{
			private static void Postfix()
			{
				Guard("load", delegate
				{
					GameState.modData.TryGetValue("com.skeptic043.sailwind.custompaintings", out var value);
					if (!Store.Load(value))
					{
						Log.LogWarning((object)("Saved pictures could not be read and were left unchanged in the save: " + Store.Problem));
					}
				});
			}
		}

		[HarmonyPatch(typeof(StartMenu), "StartNewGame")]
		private static class NewGamePatch
		{
			private static void Prefix()
			{
				Guard("new game reset", Reset);
			}
		}

		public const string Id = "com.skeptic043.sailwind.custompaintings";

		private const string ChangePrompt = "change picture";

		private static ConfigEntry<string> pictureFolders;

		private static ConfigEntry<int> maxSize;

		private static ConfigEntry<bool> compress;

		private Harmony harmony;

		internal static ManualLogSource Log { get; private set; }

		internal static PictureStore Store { get; } = new PictureStore();

		internal static string PictureFolders => pictureFolders.Value;

		internal static int MaxSize => maxSize.Value;

		internal static bool Compress => compress.Value;

		private void Awake()
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			pictureFolders = ((BaseUnityPlugin)this).Config.Bind<string>("Pictures", "Picture folders", "", "Folders to choose pictures from, separated by |. Each one is listed when the menu opens, and its subfolders can be browsed. PNG and JPG are supported.");
			maxSize = ((BaseUnityPlugin)this).Config.Bind<int>("Pictures", "Maximum size", 1024, new ConfigDescription("Pictures larger than this on their longer side are shrunk to it. Smaller pictures keep their size. 1024 uses a quarter of the memory of 2048. 2048 matches the game's own paintings and looks sharper up close.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[3] { 1024, 2048, 4096 }), Array.Empty<object>()));
			compress = ((BaseUnityPlugin)this).Config.Bind<bool>("Pictures", "Compress pictures", true, "Store pictures compressed in video memory, using about a sixth of the memory with a slight loss of quality.");
			try
			{
				((Component)this).gameObject.AddComponent<PictureTextures>();
				((Component)this).gameObject.AddComponent<PicturePicker>();
				maxSize.SettingChanged += delegate
				{
					PictureTextures.Instance?.ReloadAll();
				};
				compress.SettingChanged += delegate
				{
					PictureTextures.Instance?.ReloadAll();
				};
				harmony = new Harmony("com.skeptic043.sailwind.custompaintings");
				harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom Paintings loaded.");
			}
			catch (Exception ex)
			{
				((Behaviour)this).enabled = false;
				try
				{
					Harmony obj = harmony;
					if (obj != null)
					{
						obj.UnpatchSelf();
					}
				}
				catch (Exception ex2)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Custom Paintings could not remove its partial patches: " + ex2));
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("Custom Paintings disabled after initialization failed. Check game and mod compatibility: " + ex));
			}
		}

		private static void Guard(string patch, Action action)
		{
			try
			{
				action();
			}
			catch (Exception exception)
			{
				Failed(patch, exception);
			}
		}

		private static void Failed(string patch, Exception exception)
		{
			Log.LogError((object)$"Custom Paintings {patch} failed: {exception}");
		}

		private static PaintingPicture Picture(Component item)
		{
			if (!((Object)(object)item != (Object)null))
			{
				return null;
			}
			return item.GetComponent<PaintingPicture>();
		}

		private static void Reset()
		{
			PicturePicker.Instance?.Close();
			Store.Reset();
			GameState.modData.Remove("com.skeptic043.sailwind.custompaintings");
		}
	}
}