Decompiled source of RestlessStorage v0.2.0

RestlessStorage.dll

Decompiled 6 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using RestlessQoL.Api;
using RestlessQoL.Core;
using RestlessQoL.Storage;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("RestlessStorage")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+fc26578074e80bad4acba7539fea34f12d89a478")]
[assembly: AssemblyProduct("RestlessStorage")]
[assembly: AssemblyTitle("RestlessStorage")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 RestlessStorage
{
	[BepInPlugin("restless.storage", "RestlessStorage", "0.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "restless.storage";

		public const string PluginName = "RestlessStorage";

		public const string PluginVersion = "0.2.0";

		private Harmony? _harmony;

		private IDisposable? _page;

		private IDisposable? _settings;

		internal static ConfigEntry<bool> WindowEnabled;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			WindowEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "StorageWindow", true, "Open the shared storage browser when using a Storekeeper's Table. Local visual preference.");
			_settings = SettingsPageApi.RegisterSettings("restless.storage", (SettingsSection[])(object)new SettingsSection[1]
			{
				new SettingsSection("Storage window", (SettingsOption[])(object)new SettingsOption[1]
				{
					new SettingsOption("Open storage browser", (ConfigEntryBase)(object)WindowEnabled, false, "", false)
				})
			});
			_page = SettingsPageApi.Register(new SettingsPage("restless.storage", "RestlessStorage", "A table for the stores around you.", "Build a Storekeeper's Table at the workbench. Browse nearby stores, then take a stack into your inventory. The chest owner moves the items. Whatever does not fit goes back.", (Sprite)null, (string)null, (Action)null));
			StorekeeperPiece.Load();
			_harmony = new Harmony("restless.storage");
			_harmony.PatchAll();
			Log.LogInfo((object)"RestlessStorage 0.2.0 loaded.");
		}

		private void OnDestroy()
		{
			Storekeeper.CloseWindow();
			_settings?.Dispose();
			_page?.Dispose();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal sealed class StorageSource : IStorageWindowSource
	{
		private sealed class Group
		{
			internal readonly ItemData Item;

			internal long Count;

			internal readonly List<StorageLocation> Locations = new List<StorageLocation>();

			internal readonly Dictionary<string, long> Pending = new Dictionary<string, long>(StringComparer.Ordinal);

			internal Group(ItemData item)
			{
				Item = item;
			}
		}

		private readonly Storekeeper _table;

		private readonly Player _player;

		private string _signature = "";

		private long _revision;

		private bool _busy;

		public bool IsAvailable
		{
			get
			{
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_006c: Unknown result type (might be due to invalid IL or missing references)
				if (Plugin.WindowEnabled.Value && (Object)(object)_table != (Object)null && (Object)(object)_player != (Object)null && (Object)(object)Player.m_localPlayer == (Object)(object)_player && !((Character)_player).IsDead())
				{
					Vector3 val = ((Component)_table).transform.position - ((Component)_player).transform.position;
					return ((Vector3)(ref val)).sqrMagnitude < 25f;
				}
				return false;
			}
		}

		internal StorageSource(Storekeeper table, Player player)
		{
			_table = table;
			_player = player;
		}

		public StorageSnapshot Capture()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Expected O, but got Unknown
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0364: Expected O, but got Unknown
			//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d2: Expected O, but got Unknown
			Dictionary<string, Group> groups = new Dictionary<string, Group>(StringComparer.Ordinal);
			int num = 0;
			foreach (Container item2 in NearbyStorage.ForLocalPlayer())
			{
				Inventory inventory = item2.GetInventory();
				if (inventory == null || inventory == ((Humanoid)_player).GetInventory())
				{
					continue;
				}
				num++;
				string location = Local(inventory.GetName()) + " · " + Mathf.RoundToInt(Vector3.Distance(((Component)_player).transform.position, ((Component)item2).transform.position)) + " m";
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (allItem?.m_shared != null && allItem.m_stack > 0)
					{
						Add(allItem, allItem.m_stack, location);
					}
				}
			}
			List<NearbyLot> list = new List<NearbyLot>();
			NearbyLots.Collect(_player, ModConfig.StorageRange.Value, list);
			foreach (NearbyLot item3 in list)
			{
				if (item3.Item?.m_shared != null && item3.Count > 0)
				{
					num++;
					Add(item3.Item, item3.Count, item3.Place);
				}
			}
			foreach (Group value4 in groups.Values)
			{
				foreach (KeyValuePair<string, long> item4 in value4.Pending)
				{
					value4.Locations.Add(new StorageLocation(item4.Key, item4.Value));
				}
				value4.Pending.Clear();
			}
			List<string> list2 = new List<string>(groups.Keys);
			list2.Sort(StringComparer.Ordinal);
			List<StorageResource> list3 = new List<StorageResource>();
			StringBuilder stringBuilder = new StringBuilder().Append(num).Append('|').Append(ModConfig.StorageEnabled.Value);
			foreach (string item5 in list2)
			{
				Group obj = groups[item5];
				ItemData item = obj.Item;
				obj.Locations.Sort((StorageLocation a, StorageLocation b) => StringComparer.CurrentCulture.Compare(a.Name, b.Name));
				string text = Local(item.m_shared.m_name);
				if (item.m_quality > 1)
				{
					text = text + " · " + item.m_quality;
				}
				StorageResource val = new StorageResource(item5, text, Category(item), Local(item.m_shared.m_description), item.GetIcon(), obj.Count, item.m_shared.m_maxStackSize, item.GetWeight(-1) / (float)Math.Max(1, item.m_stack), obj.Locations.ToArray());
				list3.Add(val);
				stringBuilder.Append(item5).Append(':').Append(obj.Count)
					.Append(':')
					.Append(text)
					.Append(val.Description)
					.Append(val.Category)
					.Append(val.UnitWeight)
					.Append(val.StackSize);
				foreach (StorageLocation location2 in obj.Locations)
				{
					stringBuilder.Append(location2.Name).Append(':').Append(location2.Count);
				}
			}
			Touch(_player, groups.Values);
			string text2 = stringBuilder.ToString();
			if (text2 != _signature)
			{
				_signature = text2;
				_revision++;
			}
			bool value = ModConfig.StorageEnabled.Value;
			return new StorageSnapshot(_revision, "Storage network", "Storekeeper's Table · nearby storage", num, ((Humanoid)_player).GetInventory().GetTotalWeight(), _player.GetMaxCarryWeight(), value, value ? "Take from nearby stores" : "Nearby storage is disabled by the host", list3.ToArray());
			void Add(ItemData val2, int count, string key2)
			{
				if (val2?.m_shared != null && count > 0)
				{
					string key = ItemKey.Of(val2);
					if (!groups.TryGetValue(key, out Group value2))
					{
						groups.Add(key, value2 = new Group(val2));
					}
					value2.Count += count;
					value2.Pending.TryGetValue(key2, out var value3);
					value2.Pending[key2] = value3 + count;
				}
			}
		}

		public void Withdraw(string resourceId, int amount, Action<string> completed)
		{
			//IL_0090: 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)
			if (completed == null || _busy)
			{
				return;
			}
			if (!IsAvailable)
			{
				completed("The table is out of reach.");
				return;
			}
			if (!ModConfig.StorageEnabled.Value)
			{
				completed("Nearby storage is disabled by the host.");
				return;
			}
			if (amount < 1 || string.IsNullOrEmpty(resourceId))
			{
				completed("Nothing nearby.");
				return;
			}
			List<Container> list = new List<Container>(NearbyStorage.ForLocalPlayer());
			Vector3 origin = ((Component)_player).transform.position;
			list.Sort(delegate(Container a, Container b)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)a).transform.position - origin;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				val = ((Component)b).transform.position - origin;
				return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
			});
			string label = LabelFor(list, resourceId);
			_busy = true;
			Walk(list, resourceId, 0, amount, 0, blocked: false, timedOut: false, label, delegate(string message)
			{
				_busy = false;
				completed(message);
			});
		}

		private void Walk(List<Container> chests, string resourceId, int index, int left, int moved, bool blocked, bool timedOut, string label, Action<string> completed)
		{
			if (blocked || timedOut || left <= 0 || index > chests.Count)
			{
				completed(Result(label, moved, blocked, timedOut));
				return;
			}
			if (index == chests.Count)
			{
				NearbyLots.Take(_player, resourceId, left, (Action<int>)delegate(int taken)
				{
					int num = left - taken;
					bool blocked2 = num > 0 && ((Humanoid)_player).GetInventory().GetEmptySlots() <= 0;
					Walk(chests, resourceId, index + 1, num, moved + taken, blocked2, timedOut: false, label, completed);
				});
				return;
			}
			Container chest = chests[index];
			StorageWithdraw.Request(chest, _player.GetPlayerID(), resourceId, left, (Action<Batch>)delegate(Batch batch)
			{
				if (batch.TimedOut)
				{
					Walk(chests, resourceId, index + 1, left, moved, blocked, timedOut: true, label, completed);
				}
				else
				{
					Inventory val = (((Object)(object)_player != (Object)null) ? ((Humanoid)_player).GetInventory() : null);
					if (val == null)
					{
						if (batch.Id == 0)
						{
							StorageWithdraw.ReturnTo(chest, batch.Items);
						}
						else if (batch.Items.Count > 0)
						{
							StorageWithdraw.Acknowledge(chest, batch.Id, batch.Items);
						}
						completed("The table is out of reach.");
					}
					else
					{
						List<ItemData> list = new List<ItemData>();
						int num = StorageWithdraw.Give(val, batch.Items, list);
						if (batch.Id == 0)
						{
							StorageWithdraw.ReturnTo(chest, list);
						}
						else if (batch.Items.Count > 0)
						{
							StorageWithdraw.Acknowledge(chest, batch.Id, list);
						}
						Walk(chests, resourceId, index + 1, left - num, moved + num, list.Count > 0, timedOut: false, label, completed);
					}
				}
			});
		}

		private string LabelFor(List<Container> chests, string resourceId)
		{
			foreach (Container chest in chests)
			{
				Inventory inventory = chest.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (allItem?.m_shared != null && !(ItemKey.Of(allItem) != resourceId))
					{
						string text = Local(allItem.m_shared.m_name);
						return (allItem.m_quality > 1) ? (text + " " + allItem.m_quality) : text;
					}
				}
			}
			List<NearbyLot> list = new List<NearbyLot>();
			NearbyLots.Collect(_player, ModConfig.StorageRange.Value, list);
			foreach (NearbyLot item in list)
			{
				if (item.Item?.m_shared != null && !(ItemKey.Of(item.Item) != resourceId))
				{
					string text2 = Local(item.Item.m_shared.m_name);
					return (item.Item.m_quality > 1) ? (text2 + " " + item.Item.m_quality) : text2;
				}
			}
			return "items";
		}

		private static string Result(string label, int moved, bool blocked, bool timedOut)
		{
			if (timedOut && moved <= 0)
			{
				return "The chest did not answer. Nothing was taken.";
			}
			if (timedOut)
			{
				return "Took " + moved.ToString("N0") + " " + label + ". A later chest did not answer.";
			}
			if (moved <= 0 && blocked)
			{
				return "No room in your inventory.";
			}
			if (moved <= 0)
			{
				return "Nothing nearby.";
			}
			if (blocked)
			{
				return "Took " + moved.ToString("N0") + " " + label + ". No room for the rest.";
			}
			return "Took " + moved.ToString("N0") + " " + label + ".";
		}

		private static void Touch(Player player, Dictionary<string, Group>.ValueCollection groups)
		{
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			bool flag = false;
			foreach (Group group in groups)
			{
				string text = group.Item?.m_shared?.m_name;
				if (!string.IsNullOrEmpty(text) && !player.m_knownMaterial.Contains(text))
				{
					player.m_knownMaterial.Add(text);
					flag = true;
				}
			}
			if (flag)
			{
				player.UpdateKnownRecipesList();
				player.UpdateEvents();
			}
		}

		private static string Local(string value)
		{
			if (Localization.instance == null)
			{
				return value;
			}
			return Localization.instance.Localize(value);
		}

		private static string Category(ItemData item)
		{
			if (item.m_shared.m_food > 0f || item.m_shared.m_foodStamina > 0f)
			{
				return "Food";
			}
			switch (((object)Unsafe.As<ItemType, ItemType>(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString())
			{
			case "Material":
				return "Materials";
			case "Consumable":
				return "Food";
			case "Ammo":
			case "AmmoNonEquipable":
				return "Equipment";
			case "Shoulder":
			case "Legs":
			case "Tool":
			case "OneHandedWeapon":
			case "TwoHandedWeapon":
			case "Helmet":
			case "Shield":
			case "TwoHandedWeaponLeft":
			case "Bow":
			case "Chest":
			case "Utility":
				return "Equipment";
			default:
				return "Other";
			}
		}
	}
	public class Storekeeper : MonoBehaviour, Hoverable, Interactable
	{
		[HarmonyPatch(typeof(Menu), "Update")]
		private static class MenuInput
		{
			[HarmonyPrefix]
			private static bool Prefix()
			{
				return !StorageWindowApi.BlocksMenuInput;
			}
		}

		private static StorageSource? _openSource;

		private StorageSource? _source;

		public string GetHoverName()
		{
			return Localization.instance.Localize("Storekeeper's Table");
		}

		public string GetHoverText()
		{
			return Localization.instance.Localize("[<color=yellow><b>$KEY_Use</b></color>] Storage network");
		}

		public float GetHoverOffset()
		{
			return 0.4f;
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (!(hold || alt))
			{
				Player val = (Player)(object)((user is Player) ? user : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer))
				{
					if (!Plugin.WindowEnabled.Value)
					{
						((Character)val).Message((MessageType)2, "Enable the storage browser in F8 · RestlessStorage.", 0, (Sprite)null, false);
						return true;
					}
					_source = new StorageSource(this, val);
					if (StorageWindowApi.Open((IStorageWindowSource)(object)_source))
					{
						_openSource = _source;
					}
					return true;
				}
			}
			return false;
		}

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

		private void OnDisable()
		{
			if (_source != null)
			{
				StorageWindowApi.Close((IStorageWindowSource)(object)_source);
			}
		}

		internal static void CloseWindow()
		{
			if (_openSource != null)
			{
				StorageWindowApi.Close((IStorageWindowSource)(object)_openSource);
			}
			_openSource = null;
		}
	}
	internal static class StorekeeperPiece
	{
		public const string PrefabName = "piece_restless_storekeeper";

		public const string DisplayName = "Storekeeper's Table";

		public const string Blurb = "A ledger for the chests you can reach.";

		private const string Donor = "piece_chest_wood";

		private static bool _added;

		public static void Load()
		{
			PrefabManager.OnVanillaPrefabsAvailable += Add;
		}

		private static void Add()
		{
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: 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_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Expected O, but got Unknown
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Expected O, but got Unknown
			PrefabManager.OnVanillaPrefabsAvailable -= Add;
			if (_added)
			{
				return;
			}
			_added = true;
			if ((Object)(object)PrefabManager.Instance.GetPrefab("piece_chest_wood") == (Object)null)
			{
				Plugin.Log.LogWarning((object)"RestlessStorage skipped the table (no piece_chest_wood).");
				return;
			}
			GameObject val = PrefabManager.Instance.CreateClonedPrefab("piece_restless_storekeeper", "piece_chest_wood");
			if (!((Object)(object)val == (Object)null))
			{
				Container component = val.GetComponent<Container>();
				if ((Object)(object)component != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)component);
				}
				TableVisual.Apply(val);
				Piece val2 = val.GetComponent<Piece>() ?? val.AddComponent<Piece>();
				Sprite icon = (val2.m_icon = TableArt.Icon() ?? val2.m_icon);
				val2.m_name = "Storekeeper's Table";
				val2.m_description = "A ledger for the chests you can reach.";
				val2.m_groundOnly = false;
				val2.m_groundPiece = false;
				val2.m_clipGround = false;
				val2.m_category = (PieceCategory)4;
				SitAsFurniture(val);
				if ((Object)(object)val.GetComponent<Storekeeper>() == (Object)null)
				{
					val.AddComponent<Storekeeper>();
				}
				PieceConfig val3 = new PieceConfig
				{
					Name = "Storekeeper's Table",
					Description = "A ledger for the chests you can reach.",
					PieceTable = PieceTables.Hammer,
					Category = PieceCategories.Furniture,
					CraftingStation = "piece_workbench",
					Icon = icon
				};
				val3.AddRequirement("Wood", 20, true);
				val3.AddRequirement("FineWood", 10, true);
				val3.AddRequirement("BronzeNails", 10, true);
				PieceManager.Instance.AddPiece(new CustomPiece(val, true, val3));
				Plugin.Log.LogInfo((object)"RestlessStorage added the Storekeeper's Table.");
			}
		}

		private static void SitAsFurniture(GameObject prefab)
		{
			WearNTear component = prefab.GetComponent<WearNTear>();
			if (!((Object)(object)component == (Object)null))
			{
				component.m_noSupportWear = false;
				component.m_supports = true;
				AccessTools.Method(typeof(WearNTear), "SetupColliders", (Type[])null, (Type[])null)?.Invoke(component, null);
			}
		}
	}
	internal static class TableArt
	{
		public const string Id = "table";

		private static Mesh? _mesh;

		private static Texture2D? _albedo;

		private static Texture2D? _surface;

		private static Sprite? _icon;

		private static string? _folder;

		private static MethodInfo? _loadImage;

		private static bool _searched;

		public static Mesh? Mesh()
		{
			if ((Object)(object)_mesh != (Object)null)
			{
				return _mesh;
			}
			string text = Path.Combine(Folder(), "table.rcm");
			if (!File.Exists(text))
			{
				Plugin.Log.LogWarning((object)("table mesh missing " + text));
				return null;
			}
			_mesh = ReadRcm(File.ReadAllBytes(text));
			return _mesh;
		}

		public static Texture2D? Albedo()
		{
			return Picture(ref _albedo, "table.png", linear: false);
		}

		public static Texture2D? Surface()
		{
			return Picture(ref _surface, "table.metal.png", linear: true);
		}

		public static Sprite? Icon()
		{
			//IL_00ac: 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)
			if ((Object)(object)_icon != (Object)null)
			{
				return _icon;
			}
			Stream manifestResourceStream = typeof(TableArt).Assembly.GetManifestResourceStream("RestlessStorage.Assets.table.png");
			if (manifestResourceStream == null)
			{
				Plugin.Log.LogWarning((object)"table icon missing");
				return null;
			}
			using (manifestResourceStream)
			{
				byte[] array = new byte[manifestResourceStream.Length];
				int num;
				for (int i = 0; i < array.Length; i += num)
				{
					num = manifestResourceStream.Read(array, i, array.Length - i);
					if (num <= 0)
					{
						break;
					}
				}
				Texture2D val = DecodePng(array, "table.icon");
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				_icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
				((Object)_icon).name = "table";
				return _icon;
			}
		}

		private static Texture2D? Picture(ref Texture2D? slot, string file, bool linear)
		{
			if ((Object)(object)slot != (Object)null)
			{
				return slot;
			}
			string text = Path.Combine(Folder(), file);
			if (!File.Exists(text))
			{
				Plugin.Log.LogWarning((object)("table picture missing " + text));
				return null;
			}
			slot = DecodePng(File.ReadAllBytes(text), file, linear);
			return slot;
		}

		private static string Folder()
		{
			if (_folder != null)
			{
				return _folder;
			}
			List<string> list = new List<string>();
			Plugin instance = Plugin.Instance;
			object file;
			if (instance == null)
			{
				file = null;
			}
			else
			{
				PluginInfo info = ((BaseUnityPlugin)instance).Info;
				file = ((info != null) ? info.Location : null);
			}
			AddRoot(list, (string?)file);
			AddRoot(list, typeof(TableArt).Assembly.Location);
			foreach (string item in list)
			{
				string text = Path.Combine(item, "mesh");
				if (File.Exists(Path.Combine(text, "table.rcm")))
				{
					return Remember(text);
				}
				if (File.Exists(Path.Combine(item, "table.rcm")))
				{
					return Remember(item);
				}
			}
			_folder = Path.Combine((list.Count > 0) ? list[0] : ".", "mesh");
			Plugin.Log.LogWarning((object)("table mesh folder missing, looking in " + _folder));
			return _folder;
		}

		private static void AddRoot(List<string> roots, string? file)
		{
			string text = (string.IsNullOrEmpty(file) ? "" : (Path.GetDirectoryName(file) ?? ""));
			if (text.Length == 0)
			{
				return;
			}
			foreach (string root in roots)
			{
				if (string.Equals(root, text, StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
			}
			roots.Add(text);
		}

		private static string Remember(string dir)
		{
			_folder = dir;
			Plugin.Log.LogInfo((object)("table mesh folder " + dir));
			return dir;
		}

		private static Mesh? ReadRcm(byte[] data)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: 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)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			if (data.Length < 12 || data[0] != 82 || data[1] != 67 || data[2] != 77 || (data[3] != 49 && data[3] != 50))
			{
				Plugin.Log.LogWarning((object)"table mesh bad header");
				return null;
			}
			bool flag = data[3] == 49;
			int num = 4;
			int num2 = BitConverter.ToInt32(data, num);
			num += 4;
			int num3 = BitConverter.ToInt32(data, num);
			num += 4;
			if (num2 <= 0 || num3 <= 0 || num + num2 * 32 + num3 * 4 > data.Length)
			{
				Plugin.Log.LogWarning((object)"table mesh truncated");
				return null;
			}
			Vector3[] array = (Vector3[])(object)new Vector3[num2];
			Vector3[] array2 = (Vector3[])(object)new Vector3[num2];
			Vector2[] array3 = (Vector2[])(object)new Vector2[num2];
			for (int i = 0; i < num2; i++)
			{
				array[i] = new Vector3(BitConverter.ToSingle(data, num), BitConverter.ToSingle(data, num + 4), BitConverter.ToSingle(data, num + 8));
				array2[i] = new Vector3(BitConverter.ToSingle(data, num + 12), BitConverter.ToSingle(data, num + 16), BitConverter.ToSingle(data, num + 20));
				float num4 = BitConverter.ToSingle(data, num + 28);
				array3[i] = new Vector2(BitConverter.ToSingle(data, num + 24), flag ? (1f - num4) : num4);
				num += 32;
			}
			int[] array4 = new int[num3];
			for (int j = 0; j < num3; j++)
			{
				array4[j] = BitConverter.ToInt32(data, num);
				num += 4;
			}
			Mesh val = new Mesh
			{
				name = "table",
				indexFormat = (IndexFormat)(num2 > 65000)
			};
			val.SetVertices(array);
			val.SetNormals(array2);
			val.SetUVs(0, array3);
			val.SetTriangles(array4, 0);
			val.RecalculateBounds();
			return val;
		}

		private static Texture2D? DecodePng(byte[] bytes, string name, bool linear = false)
		{
			//IL_0013: 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)
			//IL_0019: Expected O, but got Unknown
			Texture2D val = (linear ? new Texture2D(2, 2, (TextureFormat)4, true, true) : new Texture2D(2, 2, (TextureFormat)4, true));
			MethodInfo methodInfo = LoadImage();
			if (methodInfo == null || !(bool)methodInfo.Invoke(null, new object[2] { val, bytes }))
			{
				Object.Destroy((Object)(object)val);
				Plugin.Log.LogWarning((object)("table png decode failed " + name));
				return null;
			}
			((Object)val).name = name;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).anisoLevel = 4;
			val.Apply(true, false);
			return val;
		}

		private static MethodInfo? LoadImage()
		{
			if (_searched)
			{
				return _loadImage;
			}
			_searched = true;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType("UnityEngine.ImageConversion");
				if (!(type == null))
				{
					_loadImage = type.GetMethod("LoadImage", new Type[2]
					{
						typeof(Texture2D),
						typeof(byte[])
					});
					if (_loadImage != null)
					{
						return _loadImage;
					}
				}
			}
			return null;
		}
	}
	internal static class TableVisual
	{
		public const string ChildName = "RestlessStorekeeperMesh";

		public static void Apply(GameObject prefab)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_00d2: 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_00f4: 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_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			Mesh val = TableArt.Mesh();
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"RestlessStorage: no custom model, leaving the chest look.");
				return;
			}
			Material mat = CopyLit(prefab);
			LODGroup[] componentsInChildren = prefab.GetComponentsInChildren<LODGroup>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			int layer = prefab.layer;
			Collider componentInChildren = prefab.GetComponentInChildren<Collider>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				layer = ((Component)componentInChildren).gameObject.layer;
			}
			Collider[] componentsInChildren2 = prefab.GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren2)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val2);
				}
			}
			GameObject val3 = new GameObject("RestlessStorekeeperMesh");
			val3.layer = layer;
			val3.transform.SetParent(prefab.transform, false);
			val3.transform.localPosition = Vector3.zero;
			val3.transform.localRotation = Quaternion.identity;
			val3.transform.localScale = Vector3.one;
			val3.AddComponent<MeshFilter>().sharedMesh = val;
			MeshRenderer val4 = val3.AddComponent<MeshRenderer>();
			Paint((Renderer)(object)val4, TableArt.Albedo(), TableArt.Surface(), mat);
			Renderer[] componentsInChildren3 = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val5 in componentsInChildren3)
			{
				if (!((Object)(object)val5 == (Object)null) && !((Object)(object)val5 == (Object)(object)val4) && ((val5 is MeshRenderer || val5 is SkinnedMeshRenderer) ? true : false))
				{
					val5.enabled = false;
				}
			}
			BoxCollider obj = val3.AddComponent<BoxCollider>();
			Bounds bounds = val.bounds;
			obj.center = ((Bounds)(ref bounds)).center;
			bounds = val.bounds;
			obj.size = Vector3.Max(((Bounds)(ref bounds)).size, new Vector3(0.2f, 0.2f, 0.2f));
		}

		private static void Paint(Renderer renderer, Texture2D? albedo, Texture2D? surface, Material? mat)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mat == (Object)null)
			{
				return;
			}
			mat.color = Color.white;
			SetColor(mat, "_Color", Color.white);
			SetColor(mat, "_BaseColor", Color.white);
			if ((Object)(object)albedo != (Object)null)
			{
				if (mat.HasProperty("_MainTex"))
				{
					mat.SetTexture("_MainTex", (Texture)(object)albedo);
				}
				if (mat.HasProperty("_BaseMap"))
				{
					mat.SetTexture("_BaseMap", (Texture)(object)albedo);
				}
			}
			if ((Object)(object)surface != (Object)null)
			{
				if (mat.HasProperty("_MetallicTex"))
				{
					mat.SetTexture("_MetallicTex", (Texture)(object)surface);
				}
				if (mat.HasProperty("_MetallicGlossMap"))
				{
					mat.SetTexture("_MetallicGlossMap", (Texture)(object)surface);
				}
				SetFloat(mat, "_Metallic", 1f);
				SetFloat(mat, "_MetallicAlphaGloss", 1f);
				SetFloat(mat, "_Glossiness", 1f);
				SetFloat(mat, "_Smoothness", 1f);
				mat.EnableKeyword("_METALLICGLOSSMAP");
			}
			renderer.sharedMaterial = mat;
		}

		private static Material? CopyLit(GameObject prefab)
		{
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Expected O, but got Unknown
			Renderer val = null;
			float num = -1f;
			Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val2 in componentsInChildren)
			{
				if ((Object)(object)val2 == (Object)null || (!(val2 is MeshRenderer) && !(val2 is SkinnedMeshRenderer)))
				{
					continue;
				}
				Material sharedMaterial = val2.sharedMaterial;
				if ((Object)(object)((sharedMaterial != null) ? sharedMaterial.shader : null) == (Object)null || ((Object)sharedMaterial.shader).name == "Standard")
				{
					continue;
				}
				string text = ((Object)sharedMaterial).name + " " + ((Object)sharedMaterial.shader).name;
				if (!Has(text, "snow") && !Has(text, "particle") && !Has(text, "glow"))
				{
					Bounds bounds = val2.bounds;
					float x = ((Bounds)(ref bounds)).size.x;
					bounds = val2.bounds;
					float num2 = x * ((Bounds)(ref bounds)).size.y;
					bounds = val2.bounds;
					float num3 = num2 * ((Bounds)(ref bounds)).size.z;
					if (!(num3 <= num))
					{
						num = num3;
						val = val2;
					}
				}
			}
			if ((Object)(object)((val != null) ? val.sharedMaterial : null) != (Object)null)
			{
				Plugin.Log.LogInfo((object)("table lit " + ((Object)val).name + " / " + ((Object)val.sharedMaterial.shader).name));
				return new Material(val.sharedMaterial);
			}
			PrefabManager instance = PrefabManager.Instance;
			GameObject val3 = ((instance != null) ? instance.GetPrefab("piece_chest_wood") : null);
			MeshRenderer val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponentInChildren<MeshRenderer>(true) : null);
			if ((Object)(object)((val4 != null) ? ((Renderer)val4).sharedMaterial : null) != (Object)null && ((Object)((Renderer)val4).sharedMaterial.shader).name != "Standard")
			{
				return new Material(((Renderer)val4).sharedMaterial);
			}
			Plugin.Log.LogWarning((object)"table shader missing");
			return null;
		}

		private static bool Has(string text, string token)
		{
			return text.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static void SetColor(Material mat, string prop, Color color)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (mat.HasProperty(prop))
			{
				mat.SetColor(prop, color);
			}
		}

		private static void SetFloat(Material mat, string prop, float value)
		{
			if (mat.HasProperty(prop))
			{
				mat.SetFloat(prop, value);
			}
		}
	}
}