Decompiled source of DrakeModsLibs v0.9.1

DrakeModsLibs.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.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DrakeModsLibs.API;
using DrakeModsLibs.Data;
using DrakeModsLibs.Display;
using DrakeModsLibs.Patches;
using DrakeModsLibs.Runtime;
using DrakeModsLibs.Stack;
using DrakeModsLibs.Sync;
using DrakeModsLibs.Tags;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("DrakeModsLibs")]
[assembly: AssemblyDescription("Shared customization library for Drake mods.")]
[assembly: AssemblyCompany("DrakesWorkshop")]
[assembly: AssemblyProduct("DrakeModsLibs")]
[assembly: ComVisible(false)]
[assembly: Guid("a1111111-1111-4111-8111-111111111101")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.3.0.0")]
[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 DrakeModsLibs
{
	[BepInPlugin("com.drakemods.libs", "DrakeModsLibs", "0.9.1")]
	public class CustomizeLibsPlugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("drakemods.DrakeModsLibs");

		public const string CompanyName = "DrakeMods";

		public const string ModName = "DrakeModsLibs";

		public const string Version = "0.9.1";

		public const string GUID = "com.drakemods.libs";

		private void Awake()
		{
			HarmonyPatchHub.ApplyAll(_harmony, ((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"DrakeModsLibs 0.9.1 loaded (display patches, DrakeConfigSync API, shared API).");
		}
	}
	internal static class HarmonyPatchHub
	{
		internal static void ApplyAll(Harmony harmony, ManualLogSource log)
		{
			InventoryStackPatches.Apply(harmony, log);
			ItemTooltipPatches.Apply(harmony, log);
			DropHudMessagePatches.ApplyDropItemPendingCapture(harmony, log);
			harmony.PatchAll(typeof(HarmonyPatchHub).Assembly);
			DropHudMessagePatches.ApplyMessageHudShowMessage(harmony, log);
			log.LogInfo((object)"[DrakeModsLibs] Display Harmony patches applied.");
		}
	}
}
namespace DrakeModsLibs.Tags
{
	public delegate bool CustomizeEditValidator(ItemData? item, Player? player);
	public static class CustomizationGatekeeper
	{
		private static readonly Dictionary<CustomizeOperation, List<CustomizeEditValidator>> Validators = new Dictionary<CustomizeOperation, List<CustomizeEditValidator>>();

		private static readonly List<TagBlockRule> TagBlockRules = new List<TagBlockRule>();

		private static bool _defaultTagRulesRegistered;

		public static Func<Player?, bool>? TagBypass { get; set; }

		private static void EnsureDefaultTagRules()
		{
			if (!_defaultTagRulesRegistered)
			{
				_defaultTagRulesRegistered = true;
				RegisterTagBlockRule("Drake_NoRename", CustomizeOperation.RenameName);
				RegisterTagBlockRule("Drake_NoDesc", CustomizeOperation.RenameDescription);
				RegisterTagBlockRule("Drake_NoCraftedByEdit", CustomizeOperation.EditCraftedBy);
				RegisterTagBlockRule("Drake_QuestItem", CustomizeOperation.AllEdits);
			}
		}

		public static void RegisterTagBlockRule(string tagKey, CustomizeOperation blockedOperations)
		{
			if (!string.IsNullOrEmpty(tagKey))
			{
				TagBlockRules.Add(new TagBlockRule(tagKey, blockedOperations));
			}
		}

		public static void RegisterValidator(CustomizeOperation operation, CustomizeEditValidator validator)
		{
			if (!Validators.TryGetValue(operation, out List<CustomizeEditValidator> value))
			{
				value = new List<CustomizeEditValidator>();
				Validators[operation] = value;
			}
			value.Add(validator);
		}

		public static bool IsBlockedByTag(CustomizeOperation operation, ItemData? item)
		{
			if (item == null)
			{
				return true;
			}
			EnsureDefaultTagRules();
			foreach (TagBlockRule tagBlockRule in TagBlockRules)
			{
				if ((tagBlockRule.BlockedOperations & operation) == 0 || !DrakeTagManager.HasTag(item, tagBlockRule.TagKey))
				{
					continue;
				}
				return true;
			}
			return false;
		}

		public static bool CanPerform(CustomizeOperation operation, ItemData? item, Player? player)
		{
			if (item == null)
			{
				return false;
			}
			if (IsBlockedByTag(operation, item))
			{
				Func<Player?, bool>? tagBypass = TagBypass;
				if (tagBypass == null || !tagBypass(player))
				{
					return false;
				}
			}
			if (!Validators.TryGetValue(operation, out List<CustomizeEditValidator> value) || value.Count == 0)
			{
				return true;
			}
			foreach (CustomizeEditValidator item2 in value)
			{
				if (!item2(item, player))
				{
					return false;
				}
			}
			return true;
		}
	}
	public static class DrakeTagManager
	{
		public static bool HasTag(ItemData? item, string tagKey)
		{
			if (item?.m_customData == null || string.IsNullOrEmpty(tagKey))
			{
				return false;
			}
			string value;
			return item.m_customData.TryGetValue(tagKey, out value) && (value == "1" || string.Equals(value, "true", StringComparison.OrdinalIgnoreCase));
		}

		public static void SetTag(ItemData item, string tagKey)
		{
			if (!string.IsNullOrEmpty(tagKey))
			{
				if (item.m_customData == null)
				{
					item.m_customData = new Dictionary<string, string>();
				}
				item.m_customData[tagKey] = "1";
			}
		}

		public static void ClearTag(ItemData item, string tagKey)
		{
			if (item.m_customData != null && !string.IsNullOrEmpty(tagKey))
			{
				item.m_customData.Remove(tagKey);
			}
		}
	}
}
namespace DrakeModsLibs.Stack
{
	internal static class StackIdentity
	{
		internal static string GetFingerprint(ItemData? item)
		{
			if (item?.m_customData == null)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			Append(stringBuilder, item, "Drake_Rename");
			Append(stringBuilder, item, "Drake_Rename_Desc");
			Append(stringBuilder, item, "Drake_CraftedByDisplay");
			Append(stringBuilder, item, "Drake_CraftedByLineLabel");
			return stringBuilder.ToString();
		}

		private static void Append(StringBuilder sb, ItemData item, string key)
		{
			if (item.m_customData.TryGetValue(key, out var value) && !string.IsNullOrEmpty(value))
			{
				sb.Append('|').Append(value);
			}
			else
			{
				sb.Append('|');
			}
		}

		internal static bool SameDrakeStackIdentity(ItemData? a, ItemData? b)
		{
			if (a == null || b == null)
			{
				return false;
			}
			return string.Equals(GetFingerprint(a), GetFingerprint(b), StringComparison.Ordinal);
		}
	}
}
namespace DrakeModsLibs.Sync
{
	public sealed class DrakeConfigSync
	{
		private readonly ConfigSync _inner;

		private int _syncedEntryCount;

		private bool _lockingConfigRegistered;

		public bool IsSourceOfTruth => _inner.IsSourceOfTruth;

		public event Action<bool> SourceOfTruthChanged
		{
			add
			{
				_inner.SourceOfTruthChanged += value;
			}
			remove
			{
				_inner.SourceOfTruthChanged -= value;
			}
		}

		private DrakeConfigSync(ConfigSync inner)
		{
			_inner = inner;
		}

		public static DrakeConfigSync Create(string modId, string displayName, string currentVersion, string minimumRequiredVersion = null)
		{
			ConfigSync inner = new ConfigSync(modId)
			{
				DisplayName = displayName,
				CurrentVersion = currentVersion,
				MinimumRequiredVersion = (minimumRequiredVersion ?? currentVersion)
			};
			return new DrakeConfigSync(inner);
		}

		public ConfigEntry<T> BindSynced<T>(ConfigFile config, string section, string configurationManagerCategory, string key, T defaultValue, string description)
		{
			return BindSynced(config, section, configurationManagerCategory, key, defaultValue, description, null);
		}

		public ConfigEntry<T> BindSynced<T>(ConfigFile config, string section, string configurationManagerCategory, string key, T defaultValue, string description, AcceptableValueBase? acceptableValues)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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)
			//IL_0035: Expected O, but got Unknown
			ConfigEntry<T> val = config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, acceptableValues, new object[1] { (object)(string.IsNullOrEmpty(configurationManagerCategory) ? ((ConfigurationManagerAttributes)null) : new ConfigurationManagerAttributes
			{
				Category = configurationManagerCategory
			}) }));
			SyncedConfigEntry<T> syncedConfigEntry = _inner.AddConfigEntry<T>(val);
			syncedConfigEntry.SynchronizedConfig = true;
			_syncedEntryCount++;
			return val;
		}

		public ConfigEntry<T> BindClientOnly<T>(ConfigFile config, string section, string configurationManagerCategory, string key, T defaultValue, string description)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			return config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)(string.IsNullOrEmpty(configurationManagerCategory) ? ((ConfigurationManagerAttributes)null) : new ConfigurationManagerAttributes
			{
				Category = configurationManagerCategory
			}) }));
		}

		public void AddLockingConfigEntry(ConfigEntry<bool> lockEntry)
		{
			_inner.AddLockingConfigEntry<bool>(lockEntry);
			_lockingConfigRegistered = true;
		}

		public void FinalizeBinding(ManualLogSource log, int expectedSyncedEntryCount, Func<bool> getLockSyncedConfig)
		{
			if (!_lockingConfigRegistered)
			{
				ManualLogSource obj = log;
				if (obj != null)
				{
					obj.LogError((object)"[DrakeConfigSync] LockSyncedConfig was not registered via AddLockingConfigEntry.");
				}
			}
			if (_syncedEntryCount != expectedSyncedEntryCount)
			{
				ManualLogSource obj2 = log;
				if (obj2 != null)
				{
					obj2.LogError((object)$"[DrakeConfigSync] Expected {expectedSyncedEntryCount} synced entries, registered {_syncedEntryCount}.");
				}
			}
			SourceOfTruthChanged += delegate(bool authoritative)
			{
				OnSourceOfTruthChanged(log, authoritative, getLockSyncedConfig);
			};
			OnSourceOfTruthChanged(log, IsSourceOfTruth, getLockSyncedConfig);
		}

		private static void OnSourceOfTruthChanged(ManualLogSource log, bool localIsAuthoritative, Func<bool> getLockSyncedConfig)
		{
			if (localIsAuthoritative)
			{
				if (!getLockSyncedConfig() && log != null)
				{
					log.LogWarning((object)"[DrakeConfigSync] LockSyncedConfig is false on the host. Non-admin clients can change synced gameplay settings.");
				}
			}
			else if (!getLockSyncedConfig() && log != null)
			{
				log.LogInfo((object)"[DrakeConfigSync] Connected to a host with config lock disabled; server values still apply until changed.");
			}
		}
	}
}
namespace DrakeModsLibs.Runtime
{
	internal static class CustomizeLibsRuntime
	{
		internal static readonly List<IDisplayNameModifier> DisplayNameModifiers = new List<IDisplayNameModifier>();

		internal static readonly List<IItemDisplayNameLayer> DisplayNameLayers = new List<IItemDisplayNameLayer>();

		internal static bool DefaultDisplayLayersRegistered;

		internal static bool ShowItemStandItemNameWhenNoAccess { get; set; } = true;

		internal static IStackMergePolicy? StackMergePolicy { get; set; }
	}
}
namespace DrakeModsLibs.Patches
{
	[HarmonyPatch]
	internal static class ItemDataDecoratedNameLikePatch
	{
		private static bool _loggedMissing;

		private static MethodBase? TargetMethod()
		{
			Type typeFromHandle = typeof(ItemData);
			BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			string[] array = new string[3] { "GetDecoratedName", "GetDecoratedNameWithQuality", "GetName" };
			foreach (string name in array)
			{
				MethodInfo methodInfo = TryBindInstanceStringMethod(typeFromHandle, name, bindingFlags);
				if (methodInfo != null)
				{
					return methodInfo;
				}
			}
			MethodInfo[] methods = typeFromHandle.GetMethods(bindingFlags);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (!methodInfo2.IsStatic && !(methodInfo2.ReturnType != typeof(string)))
				{
					string text = methodInfo2.Name ?? "";
					if (text.IndexOf("Decor", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Name", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return methodInfo2;
					}
				}
			}
			if (!_loggedMissing)
			{
				_loggedMissing = true;
				Debug.LogWarning((object)"[DrakeModsLibs] Upgrade list rename: no suitable ItemData decorated-name method found; upgrade tab names may remain vanilla.");
			}
			return null;
		}

		private static MethodInfo? TryBindInstanceStringMethod(Type t, string name, BindingFlags flags)
		{
			MethodInfo methodInfo = null;
			MethodInfo[] methods = t.GetMethods(flags);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (!(methodInfo2.Name != name) && !methodInfo2.IsStatic && !(methodInfo2.ReturnType != typeof(string)))
				{
					if (methodInfo2.GetParameters().Length == 0)
					{
						return methodInfo2;
					}
					if ((object)methodInfo == null)
					{
						methodInfo = methodInfo2;
					}
				}
			}
			return methodInfo;
		}

		[HarmonyPostfix]
		private static void Postfix(ItemData __instance, ref string __result)
		{
			if (__instance?.m_shared == null || string.IsNullOrEmpty(__result) || (!ItemDisplayService.HasCustomName(__instance) && !DisplayNameModifierHub.AffectsDisplay(__instance)))
			{
				return;
			}
			string text = ((Localization.instance != null) ? Localization.instance.Localize(__instance.m_shared.m_name) : __instance.m_shared.m_name);
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			string displayNameForUi = ItemDisplayService.GetDisplayNameForUi(__instance, localize: true);
			if (!string.IsNullOrEmpty(displayNameForUi))
			{
				if (__result.Contains(text))
				{
					__result = __result.Replace(text, displayNameForUi);
				}
				else if (!string.IsNullOrEmpty(__instance.m_shared.m_name) && __result.Contains(__instance.m_shared.m_name))
				{
					__result = __result.Replace(__instance.m_shared.m_name, displayNameForUi);
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "CreateItemTooltip")]
	[HarmonyPriority(500)]
	public static class InventoryGridDisplayTooltipPatch
	{
		[HarmonyPostfix]
		private static void ApplyDisplayTooltip(InventoryGrid __instance, ItemData? item, UITooltip tooltip)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (item?.m_shared != null && !((Object)(object)tooltip == (Object)null))
			{
				string displayNameForUi = ItemDisplayService.GetDisplayNameForUi(item, localize: false);
				string tooltip2 = item.GetTooltip(-1);
				tooltip2 = ItemTooltipPatches.ApplyCraftedByDisplayToTooltipText(tooltip2, item);
				tooltip2 = UpdateDescription(item, tooltip2);
				tooltip.Set(displayNameForUi, tooltip2, __instance.m_tooltipAnchor, default(Vector2));
			}
		}

		private static string UpdateDescription(ItemData? item, string currentText)
		{
			if (item?.m_shared == null)
			{
				return currentText;
			}
			if (!ItemDisplayService.HasCustomDescription(item))
			{
				return currentText;
			}
			string newValue = TooltipRichText.EnsureRichTextTagsClosedForTooltip(ItemDisplayService.GetProperDescription(item, item.m_shared.m_description));
			string description = item.m_shared.m_description;
			if (!string.IsNullOrEmpty(description) && currentText.Contains(description))
			{
				currentText = currentText.Replace(description, newValue);
			}
			return currentText;
		}
	}
	internal static class InventoryStackPatches
	{
		internal static ItemData? IncomingStackItem;

		internal static void Apply(Harmony harmony, ManualLogSource log)
		{
			//IL_0072: 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_0094: Expected O, but got Unknown
			//IL_0094: Expected O, but got Unknown
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Expected O, but got Unknown
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Expected O, but got Unknown
			Type typeFromHandle = typeof(Inventory);
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeFromHandle, "AddItem", new Type[1] { typeof(ItemData) }, (Type[])null) ?? AccessTools.Method(typeFromHandle, "AddItem", new Type[1] { typeof(ItemData) }, (Type[])null);
			if (methodInfo != null)
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(InventoryStackPatches), "AddItem_IncomingPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(InventoryStackPatches), "AddItem_IncomingCleanup", (Type[])null), (HarmonyMethod)null);
			}
			else
			{
				log.LogWarning((object)"[DrakeModsLibs] SeparateStacks: AddItem(ItemData) not found — incoming stack tracking disabled.");
			}
			MethodInfo methodInfo2 = null;
			MethodInfo[] methods = typeFromHandle.GetMethods(bindingAttr);
			foreach (MethodInfo methodInfo3 in methods)
			{
				if (!(methodInfo3.Name != "FindFreeStackItem"))
				{
					ParameterInfo[] parameters = methodInfo3.GetParameters();
					if (parameters.Length >= 2 && !(parameters[0].ParameterType != typeof(string)) && !(parameters[1].ParameterType != typeof(int)))
					{
						methodInfo2 = methodInfo3;
						break;
					}
				}
			}
			if (methodInfo2 != null)
			{
				harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(InventoryStackPatches), "FindFreeStackItem_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				log.LogWarning((object)"[DrakeModsLibs] SeparateStacks: FindFreeStackItem not found — merge-from-pickup may ignore identity.");
			}
			MethodInfo methodInfo4 = null;
			MethodInfo[] methods2 = typeFromHandle.GetMethods(bindingAttr);
			foreach (MethodInfo methodInfo5 in methods2)
			{
				if (!(methodInfo5.Name != "AddItem"))
				{
					ParameterInfo[] parameters2 = methodInfo5.GetParameters();
					if (parameters2.Length >= 4 && !(parameters2[0].ParameterType != typeof(ItemData)) && !(parameters2[1].ParameterType != typeof(int)) && !(parameters2[2].ParameterType != typeof(int)) && !(parameters2[3].ParameterType != typeof(int)))
					{
						methodInfo4 = methodInfo5;
						break;
					}
				}
			}
			if (methodInfo4 != null)
			{
				harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(InventoryStackPatches), "AddItemAtCell_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				log.LogWarning((object)"[DrakeModsLibs] SeparateStacks: AddItem(ItemData,int,int,int...) not found — cell merge guard disabled.");
			}
		}

		internal static void AddItem_IncomingPrefix(ItemData item)
		{
			IncomingStackItem = item;
		}

		internal static void AddItem_IncomingCleanup(Exception? __exception)
		{
			IncomingStackItem = null;
		}

		internal static bool FindFreeStackItem_Prefix(Inventory __instance, string name, int quality, ref ItemData? __result)
		{
			IStackMergePolicy stackMergePolicy = CustomizeLibsRuntime.StackMergePolicy;
			if (stackMergePolicy == null || !stackMergePolicy.SeparateStacksEnabled || IncomingStackItem == null)
			{
				return true;
			}
			__result = null;
			foreach (ItemData allItem in __instance.GetAllItems())
			{
				if (TryPickStackSlot(IncomingStackItem, name, quality, allItem, ref __result))
				{
					continue;
				}
				break;
			}
			return false;
		}

		private static bool TryPickStackSlot(ItemData incoming, string name, int quality, ItemData? itemData, ref ItemData? __result)
		{
			if (itemData?.m_shared == null)
			{
				return true;
			}
			if (itemData.m_shared.m_name != name || itemData.m_quality != quality)
			{
				return true;
			}
			if (itemData.m_stack >= itemData.m_shared.m_maxStackSize)
			{
				return true;
			}
			if (!StackIdentity.SameDrakeStackIdentity(incoming, itemData))
			{
				return true;
			}
			__result = itemData;
			return false;
		}

		internal static bool AddItemAtCell_Prefix(ItemData item, int amount, int x, int y, Inventory __instance, ref bool __result)
		{
			IStackMergePolicy stackMergePolicy = CustomizeLibsRuntime.StackMergePolicy;
			if (stackMergePolicy == null || !stackMergePolicy.SeparateStacksEnabled)
			{
				return true;
			}
			ItemData itemAt = __instance.GetItemAt(x, y);
			if (itemAt == null)
			{
				return true;
			}
			if (itemAt.m_shared.m_name != item.m_shared.m_name)
			{
				return true;
			}
			if (itemAt.m_shared.m_maxQuality > 1 && itemAt.m_quality != item.m_quality)
			{
				return true;
			}
			if (!StackIdentity.SameDrakeStackIdentity(item, itemAt))
			{
				if (!stackMergePolicy.SeparateStacksHardLock)
				{
					return true;
				}
				__result = false;
				return false;
			}
			return true;
		}
	}
	internal static class HoverRenameHelper
	{
		internal static void ApplyRenameToHoverResult(ref string __result, ItemData item)
		{
			if (item?.m_shared == null || string.IsNullOrEmpty(__result))
			{
				return;
			}
			if (ItemDisplayService.HasCustomName(item))
			{
				string displayNameForUi = ItemDisplayService.GetDisplayNameForUi(item, localize: false);
				if (displayNameForUi == null || item.m_shared.m_name == null)
				{
					return;
				}
				if (Localization.instance == null)
				{
					__result = __result.Replace(item.m_shared.m_name, displayNameForUi);
					return;
				}
				string text = Localization.instance.Localize(item.m_shared.m_name);
				string newValue = Localization.instance.Localize(displayNameForUi);
				if (__result.Contains(text))
				{
					__result = __result.Replace(text, newValue);
				}
			}
			else
			{
				if (!DisplayNameModifierHub.AffectsDisplay(item))
				{
					return;
				}
				string text2 = ((Localization.instance != null) ? Localization.instance.Localize(item.m_shared.m_name) : item.m_shared.m_name);
				if (!string.IsNullOrEmpty(text2))
				{
					string prefixRaw = DisplayNameModifierHub.GetPrefixRaw(item);
					string text3 = ((Localization.instance != null) ? Localization.instance.Localize(prefixRaw) : prefixRaw);
					string newValue2 = TooltipRichText.EnsureRichTextTagsClosedForTooltip(text3 + " " + text2);
					if (__result.Contains(text2))
					{
						__result = __result.Replace(text2, newValue2);
					}
					else if (!string.IsNullOrEmpty(item.m_shared.m_name) && __result.Contains(item.m_shared.m_name))
					{
						string newValue3 = TooltipRichText.EnsureRichTextTagsClosedForTooltip(text3 + " " + item.m_shared.m_name);
						__result = __result.Replace(item.m_shared.m_name, newValue3);
					}
				}
			}
		}
	}
	internal static class PickupHudMessageHelper
	{
		internal static bool TryGetLocalizedCustomNameForHud(ItemData? item, out string localizedName)
		{
			localizedName = "";
			if (item?.m_shared == null)
			{
				return false;
			}
			if (!ItemDisplayService.HasCustomName(item) && !DisplayNameModifierHub.AffectsDisplay(item))
			{
				return false;
			}
			localizedName = ItemDisplayService.GetDisplayNameForUi(item, localize: true);
			if (string.IsNullOrEmpty(localizedName))
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Character), "ShowPickupMessage")]
	internal static class CharacterShowPickupMessagePatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Character __instance, ItemData item, int amount)
		{
			if (!PickupHudMessageHelper.TryGetLocalizedCustomNameForHud(item, out string localizedName))
			{
				return true;
			}
			__instance.Message((MessageType)1, "$msg_added " + localizedName, amount, item.GetIcon());
			return false;
		}
	}
	[HarmonyPatch(typeof(Character), "ShowRemovedMessage")]
	internal static class CharacterShowRemovedMessagePatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Character __instance, ItemData item, int amount)
		{
			if (!PickupHudMessageHelper.TryGetLocalizedCustomNameForHud(item, out string localizedName))
			{
				return true;
			}
			__instance.Message((MessageType)1, "$msg_removed " + localizedName, amount, item.GetIcon());
			return false;
		}
	}
	public static class Patches
	{
		[HarmonyPatch(typeof(ItemDrop))]
		public static class HoverTextPatch
		{
			[HarmonyPatch("GetHoverText")]
			[HarmonyPostfix]
			private static void FixHoverText(ItemDrop __instance, ref string __result)
			{
				ItemData itemData = __instance.m_itemData;
				if (itemData != null && __instance?.m_itemData?.m_shared != null && !string.IsNullOrEmpty(__result))
				{
					HoverRenameHelper.ApplyRenameToHoverResult(ref __result, itemData);
				}
			}

			[HarmonyPatch(typeof(ItemDrop), "GetHoverName")]
			[HarmonyPostfix]
			private static void FixHoverName(ItemDrop __instance, ref string __result)
			{
				ItemData val = __instance?.m_itemData;
				if (val == null)
				{
					return;
				}
				if (ItemDisplayService.HasCustomName(val))
				{
					string displayNameForUi = ItemDisplayService.GetDisplayNameForUi(val, localize: false);
					if (!string.IsNullOrEmpty(displayNameForUi))
					{
						__result = displayNameForUi;
					}
				}
				else if (DisplayNameModifierHub.AffectsDisplay(val))
				{
					string prefixRaw = DisplayNameModifierHub.GetPrefixRaw(val);
					string text = ((Localization.instance != null) ? Localization.instance.Localize(prefixRaw) : prefixRaw);
					__result = TooltipRichText.EnsureRichTextTagsClosedForTooltip(text + " " + __result);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ItemStand))]
	public static class ItemStandPatch
	{
		private const int StandItemDataZdoIndex = -1;

		internal static void RefreshAllItemStandDisplayNames()
		{
			ItemStand[] array;
			try
			{
				array = Object.FindObjectsByType<ItemStand>((FindObjectsSortMode)0);
			}
			catch
			{
				try
				{
					array = Object.FindObjectsOfType<ItemStand>();
				}
				catch
				{
					return;
				}
			}
			if (array == null || array.Length == 0)
			{
				return;
			}
			ItemStand[] array2 = array;
			foreach (ItemStand val in array2)
			{
				if (!((Object)(object)val == (Object)null))
				{
					try
					{
						ApplyLiveDisplayNameToStand(val);
					}
					catch
					{
					}
				}
			}
		}

		private static ZDO? TryGetStandZdo(ItemStand stand)
		{
			object? obj = AccessTools.Field(typeof(ItemStand), "m_nview")?.GetValue(stand);
			ZNetView val = (ZNetView)((obj is ZNetView) ? obj : null);
			return (val != null) ? val.GetZDO() : null;
		}

		private static int TryGetAttachedPrefabHash(ItemStand stand)
		{
			if ((Object)(object)stand == (Object)null)
			{
				return 0;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(ItemStand), "GetAttachedItem", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return 0;
			}
			object obj;
			try
			{
				obj = methodInfo.Invoke(stand, null);
			}
			catch
			{
				return 0;
			}
			if (obj is int result)
			{
				return result;
			}
			if (obj is string s && int.TryParse(s, out var result2))
			{
				return result2;
			}
			return 0;
		}

		private static byte[]? TryGetStandItemBytes(ZDO zdo)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(ZDOVars), "s_itemData");
			if (fieldInfo != null)
			{
				object value = fieldInfo.GetValue(null);
				if (value is int num)
				{
					return zdo.GetByteArray(num, (byte[])null);
				}
				if (value is string text && !string.IsNullOrEmpty(text))
				{
					return zdo.GetByteArray(text, (byte[])null);
				}
			}
			return zdo.GetByteArray("-1_itemData", (byte[])null);
		}

		private static bool TryLoadItemFromZdo(ItemData item, ZDO zdo, int index)
		{
			MethodInfo[] methods = typeof(ItemDrop).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo methodInfo = methods.FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "LoadFromZDO")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 3 && parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType == typeof(ZDO) && parameters[2].ParameterType == typeof(int);
			});
			if (methodInfo != null)
			{
				methodInfo.Invoke(null, new object[3] { item, zdo, index });
				return true;
			}
			MethodInfo methodInfo2 = methods.FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "LoadFromZDO")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 3 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(ItemData) && parameters[2].ParameterType == typeof(ZDO);
			});
			if (methodInfo2 != null)
			{
				methodInfo2.Invoke(null, new object[3] { index, item, zdo });
				return true;
			}
			MethodInfo methodInfo3 = methods.FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "LoadFromZDO")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 2 && parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType == typeof(ZDO);
			});
			if (methodInfo3 == null)
			{
				return false;
			}
			methodInfo3.Invoke(null, new object[2] { item, zdo });
			return true;
		}

		private static ItemData? TryGetStandItemForDisplay(ItemStand stand, out bool loadedInstance)
		{
			loadedInstance = false;
			if ((Object)(object)stand == (Object)null)
			{
				return null;
			}
			int num = 0;
			try
			{
				num = TryGetAttachedPrefabHash(stand);
			}
			catch
			{
			}
			if (num != 0 && (Object)(object)ObjectDB.instance != (Object)null)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(num);
				ItemData val = ((!((Object)(object)itemPrefab != (Object)null)) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData);
				if (val == null)
				{
					return null;
				}
				ItemData val2 = val.Clone();
				ZDO val3 = TryGetStandZdo(stand);
				if (val3 == null)
				{
					return val2;
				}
				try
				{
					byte[] array = TryGetStandItemBytes(val3);
					if (array != null && array.Length > 2 && TryLoadItemFromZdo(val2, val3, -1))
					{
						loadedInstance = true;
					}
				}
				catch
				{
				}
				return val2;
			}
			ItemData val4 = TryGetFirstContainerItem(stand);
			if (val4?.m_shared != null)
			{
				loadedInstance = true;
				return val4;
			}
			return null;
		}

		private static void ApplyDisplayNameFromItemInstance(ItemStand stand, ItemData item)
		{
			if ((Object)(object)stand == (Object)null || item?.m_shared == null)
			{
				return;
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(ItemStand), "m_currentItemName");
			if (!(fieldInfo == null))
			{
				if (ItemDisplayService.HasCustomName(item) || DisplayNameModifierHub.AffectsDisplay(item))
				{
					string displayNameForUi = ItemDisplayService.GetDisplayNameForUi(item, localize: false);
					fieldInfo.SetValue(stand, TooltipRichText.EnsureRichTextTagsClosedForTooltip(displayNameForUi));
				}
				else
				{
					fieldInfo.SetValue(stand, item.m_shared.m_name);
				}
			}
		}

		private static string ResolveStandBaseLabel(ItemStand stand, ZDO? zdo, ItemData? item)
		{
			if (item != null && ItemDisplayService.HasCustomName(item))
			{
				return ItemDisplayService.GetProperName(item);
			}
			string text = ((zdo != null) ? zdo.GetString("DrakeRenameIt_CustomName", "") : "");
			if (!string.IsNullOrWhiteSpace(text))
			{
				return StripLegacyDurabilityPrefix(item, text.Trim());
			}
			if (item?.m_shared != null && !string.IsNullOrEmpty(item.m_shared.m_name))
			{
				return item.m_shared.m_name;
			}
			string text2 = AccessTools.Field(typeof(ItemStand), "m_currentItemName")?.GetValue(stand) as string;
			return string.IsNullOrWhiteSpace(text2) ? "" : text2.Trim();
		}

		private static void ApplyLiveDisplayNameToStand(ItemStand stand)
		{
			if ((Object)(object)stand == (Object)null)
			{
				return;
			}
			bool loadedInstance;
			ItemData val = TryGetStandItemForDisplay(stand, out loadedInstance);
			if (val?.m_shared == null)
			{
				return;
			}
			ZDO val2 = TryGetStandZdo(stand);
			string text = ResolveStandBaseLabel(stand, val2, val);
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			if (val2 != null)
			{
				if (ItemDisplayService.HasCustomName(val))
				{
					string properName = ItemDisplayService.GetProperName(val);
					val2.Set("DrakeRenameIt_CustomName", TooltipRichText.EnsureRichTextTagsClosedForTooltip(properName));
				}
				else
				{
					string text2 = val2.GetString("DrakeRenameIt_CustomName", "");
					if (!string.IsNullOrWhiteSpace(text2))
					{
						string text3 = StripLegacyDurabilityPrefix(val, text2.Trim());
						if (!string.Equals(text3, text2.Trim(), StringComparison.Ordinal))
						{
							val2.Set("DrakeRenameIt_CustomName", TooltipRichText.EnsureRichTextTagsClosedForTooltip(text3));
						}
					}
					else if (loadedInstance)
					{
						val2.Set("DrakeRenameIt_CustomName", string.Empty);
					}
				}
			}
			string text4;
			if (loadedInstance && (ItemDisplayService.HasCustomName(val) || DisplayNameModifierHub.AffectsDisplay(val)))
			{
				text4 = ItemDisplayService.GetDisplayNameForUi(val, localize: false);
			}
			else
			{
				text4 = text;
				if (DisplayNameModifierHub.AffectsDisplay(val))
				{
					string prefixRaw = DisplayNameModifierHub.GetPrefixRaw(val);
					if (!string.IsNullOrEmpty(prefixRaw))
					{
						string text5 = StripLegacyDurabilityPrefix(val, text);
						text4 = prefixRaw.TrimEnd(Array.Empty<char>()) + " " + text5;
					}
				}
			}
			AccessTools.Field(typeof(ItemStand), "m_currentItemName")?.SetValue(stand, TooltipRichText.EnsureRichTextTagsClosedForTooltip(text4));
		}

		private static string StripLegacyDurabilityPrefix(ItemData? item, string cached)
		{
			if (string.IsNullOrEmpty(cached))
			{
				return cached;
			}
			if (item != null)
			{
				string prefixRaw = DisplayNameModifierHub.GetPrefixRaw(item);
				cached = StripOnePrefix(cached, prefixRaw);
			}
			cached = StripOnePrefix(cached, "Pristine");
			cached = StripOnePrefix(cached, "Worn");
			cached = StripOnePrefix(cached, "Rusty");
			cached = StripOnePrefix(cached, "Tarnished");
			cached = StripOnePrefix(cached, "Broken");
			return cached;
		}

		private static string StripOnePrefix(string cached, string? prefix)
		{
			if (string.IsNullOrWhiteSpace(prefix) || string.IsNullOrEmpty(cached))
			{
				return cached;
			}
			foreach (string item in UniquePrefixCandidates(prefix))
			{
				string text = item.TrimEnd(Array.Empty<char>()) + " ";
				if (cached.StartsWith(text, StringComparison.OrdinalIgnoreCase))
				{
					return cached.Substring(text.Length).TrimStart(Array.Empty<char>());
				}
			}
			return cached;
		}

		private static List<string> UniquePrefixCandidates(string prefix)
		{
			List<string> list = new List<string>();
			consider(prefix);
			if (Localization.instance != null)
			{
				consider(Localization.instance.Localize(prefix));
			}
			return list;
			void consider(string? s)
			{
				if (!string.IsNullOrWhiteSpace(s))
				{
					s = s.Trim();
					if (!list.Exists((string x) => string.Equals(x, s, StringComparison.Ordinal)))
					{
						list.Add(s);
						if (s.IndexOf('<') >= 0)
						{
							string plain = Regex.Replace(s, "<[^>]+>", "").Trim();
							if (plain.Length > 0 && !list.Exists((string x) => string.Equals(x, plain, StringComparison.Ordinal)))
							{
								list.Add(plain);
							}
						}
					}
				}
			}
		}

		private static bool ItemStandHadVisualBeforeUse(ItemStand stand)
		{
			if ((Object)(object)stand == (Object)null)
			{
				return false;
			}
			try
			{
				if (stand.HaveAttachment())
				{
					return true;
				}
			}
			catch
			{
			}
			string value = AccessTools.Field(typeof(ItemStand), "m_visualName")?.GetValue(stand) as string;
			return !string.IsNullOrEmpty(value);
		}

		private static string TryGetStandCustomNameFromZdo(ItemStand stand)
		{
			if ((Object)(object)stand == (Object)null)
			{
				return "";
			}
			ZDO val = TryGetStandZdo(stand);
			if (val == null)
			{
				return "";
			}
			bool loadedInstance;
			ItemData val2 = TryGetStandItemForDisplay(stand, out loadedInstance);
			if (val2 != null && loadedInstance && (ItemDisplayService.HasCustomName(val2) || DisplayNameModifierHub.AffectsDisplay(val2)))
			{
				string displayNameForUi = ItemDisplayService.GetDisplayNameForUi(val2, localize: true);
				if (!string.IsNullOrEmpty(displayNameForUi))
				{
					return displayNameForUi;
				}
			}
			string text = val.GetString("DrakeRenameIt_CustomName", "");
			if (string.IsNullOrWhiteSpace(text))
			{
				return "";
			}
			string text2 = StripLegacyDurabilityPrefix(val2, text.Trim());
			string text3 = text2;
			if (val2 != null && DisplayNameModifierHub.AffectsDisplay(val2))
			{
				string prefixRaw = DisplayNameModifierHub.GetPrefixRaw(val2);
				if (!string.IsNullOrEmpty(prefixRaw))
				{
					text3 = prefixRaw.TrimEnd(Array.Empty<char>()) + " " + text2;
				}
			}
			string text4 = TooltipRichText.EnsureRichTextTagsClosedForTooltip(text3);
			return (Localization.instance != null) ? Localization.instance.Localize(text4) : text4;
		}

		private static string TryGetStandCurrentItemName(ItemStand stand)
		{
			if ((Object)(object)stand == (Object)null)
			{
				return "";
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(ItemStand), "m_currentItemName");
			if (fieldInfo == null)
			{
				return "";
			}
			string text = fieldInfo.GetValue(stand) as string;
			if (string.IsNullOrWhiteSpace(text))
			{
				return "";
			}
			string text2 = TooltipRichText.EnsureRichTextTagsClosedForTooltip(text);
			return (Localization.instance != null) ? Localization.instance.Localize(text2) : text2;
		}

		private static bool HoverTextContainsNoAccess(string hoverText)
		{
			if (string.IsNullOrEmpty(hoverText))
			{
				return false;
			}
			if (hoverText.Contains("$piece_noaccess"))
			{
				return true;
			}
			if (Localization.instance == null)
			{
				return false;
			}
			string value = Localization.instance.Localize("$piece_noaccess");
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			return hoverText.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static ItemData? TryGetFirstContainerItem(ItemStand stand)
		{
			Container component = ((Component)stand).GetComponent<Container>();
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			Inventory inventory = component.GetInventory();
			if (inventory == null)
			{
				return null;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null || allItems.Count == 0)
			{
				return null;
			}
			return allItems[0];
		}

		private static string TryGetBestStandLabel(ItemStand stand)
		{
			string text = TryGetStandCurrentItemName(stand);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			text = TryGetStandCustomNameFromZdo(stand);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			ItemData val = TryGetFirstContainerItem(stand);
			if (val?.m_shared == null)
			{
				return "";
			}
			return ItemDisplayService.GetDisplayNameForUi(val, localize: true);
		}

		[HarmonyPatch("GetHoverText")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void FixItemStandHoverText(ItemStand __instance, ref string __result)
		{
			if ((Object)(object)__instance == (Object)null || string.IsNullOrEmpty(__result))
			{
				return;
			}
			if (HoverTextContainsNoAccess(__result))
			{
				if (CustomizeLibsRuntime.ShowItemStandItemNameWhenNoAccess)
				{
					string text = TryGetBestStandLabel(__instance);
					if (!string.IsNullOrEmpty(text) && __result.IndexOf(text, StringComparison.Ordinal) < 0)
					{
						__result = text + "\n" + __result;
					}
				}
				return;
			}
			try
			{
				if (TryGetAttachedPrefabHash(__instance) != 0)
				{
					return;
				}
			}
			catch
			{
			}
			ItemData val = TryGetFirstContainerItem(__instance);
			if (val?.m_shared != null && (ItemDisplayService.HasCustomName(val) || DisplayNameModifierHub.AffectsDisplay(val)))
			{
				HoverRenameHelper.ApplyRenameToHoverResult(ref __result, val);
			}
		}

		[HarmonyPatch("UseItem")]
		[HarmonyPrefix]
		private static void UseItem_Prefix(ItemStand __instance, out bool __state)
		{
			__state = ItemStandHadVisualBeforeUse(__instance);
		}

		[HarmonyPatch("UseItem")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void GrabItem(ItemStand __instance, Humanoid user, ItemData? item, bool __state)
		{
			if (item?.m_shared == null)
			{
				return;
			}
			ZDO val = TryGetStandZdo(__instance);
			if (val == null)
			{
				return;
			}
			Inventory val2 = ((user != null) ? user.GetInventory() : null);
			if (__state && val2 != null && val2.ContainsItem(item))
			{
				val.Set("DrakeRenameIt_CustomName", string.Empty);
				return;
			}
			if (ItemDisplayService.HasCustomName(item))
			{
				string properName = ItemDisplayService.GetProperName(item);
				val.Set("DrakeRenameIt_CustomName", TooltipRichText.EnsureRichTextTagsClosedForTooltip(properName));
			}
			else
			{
				val.Set("DrakeRenameIt_CustomName", string.Empty);
			}
			ApplyDisplayNameFromItemInstance(__instance, item);
		}

		[HarmonyPatch("SetVisualItem")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void FixStandText(ItemStand __instance, int itemHash, int variant, int quality, int orientation)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				ApplyLiveDisplayNameToStand(__instance);
			}
		}
	}
	internal static class DropHudMessagePatches
	{
		private static ItemData? PendingDroppedItem;

		private static float PendingDroppedItemSetAt;

		private const float PendingDroppedItemTtlSeconds = 2.5f;

		internal static void ApplyDropItemPendingCapture(Harmony harmony, ManualLogSource log)
		{
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Expected O, but got Unknown
			try
			{
				Type[] array = new Type[3]
				{
					typeof(Inventory),
					typeof(ItemData),
					typeof(int)
				};
				MethodInfo methodInfo = AccessTools.Method(typeof(Humanoid), "DropItem", array, (Type[])null) ?? AccessTools.DeclaredMethod(typeof(Humanoid), "DropItem", array, (Type[])null);
				if (methodInfo == null)
				{
					MethodInfo[] methods = typeof(Humanoid).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (MethodInfo methodInfo2 in methods)
					{
						if (!(methodInfo2.Name != "DropItem"))
						{
							ParameterInfo[] parameters = methodInfo2.GetParameters();
							if (parameters.Length == 3 && parameters[0].ParameterType == typeof(Inventory) && parameters[1].ParameterType == typeof(ItemData) && parameters[2].ParameterType == typeof(int))
							{
								methodInfo = methodInfo2;
								break;
							}
						}
					}
				}
				if (methodInfo == null)
				{
					log.LogWarning((object)"[DrakeModsLibs] Drop HUD: Humanoid.DropItem(Inventory,ItemData,int) not found.");
					return;
				}
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(DropHudMessagePatches), "HumanoidDropItemTypedPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				log.LogInfo((object)("[DrakeModsLibs] Drop HUD: patched typed " + methodInfo.DeclaringType?.Name + "." + methodInfo.Name));
			}
			catch (Exception ex)
			{
				log.LogError((object)("[DrakeModsLibs] Drop HUD: typed DropItem patch failed: " + ex));
			}
		}

		private static void HumanoidDropItemTypedPrefix(Inventory inventory, ItemData item, int amount)
		{
			PendingDroppedItem = item;
			PendingDroppedItemSetAt = Time.time;
		}

		internal static void TryRewriteDroppedMessage(ref string msg)
		{
			ItemData pendingDroppedItem = PendingDroppedItem;
			if (pendingDroppedItem?.m_shared == null || string.IsNullOrEmpty(msg))
			{
				return;
			}
			float num = Time.time - PendingDroppedItemSetAt;
			if (num > 2.5f)
			{
				PendingDroppedItem = null;
				return;
			}
			string text = "$msg_dropped";
			string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text);
			bool flag = msg.IndexOf(text, StringComparison.Ordinal) >= 0;
			bool flag2 = msg.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0;
			bool flag3 = ItemDisplayService.HasCustomName(pendingDroppedItem) || DisplayNameModifierHub.AffectsDisplay(pendingDroppedItem);
			bool flag4 = flag3 && msg.IndexOf("drop", StringComparison.OrdinalIgnoreCase) >= 0 && msg.Length < 280;
			if (!flag && !flag2 && !flag4)
			{
				return;
			}
			string text3 = ((Localization.instance != null) ? Localization.instance.Localize(pendingDroppedItem.m_shared.m_name) : pendingDroppedItem.m_shared.m_name);
			string displayNameLocalized = ItemDisplayService.GetDisplayNameForUi(pendingDroppedItem, localize: true);
			if (string.IsNullOrEmpty(displayNameLocalized))
			{
				return;
			}
			if (flag3 && msg.StartsWith("$msg_dropped ", StringComparison.Ordinal) && !string.IsNullOrEmpty(pendingDroppedItem.m_shared.m_name))
			{
				string a = msg.Substring("$msg_dropped ".Length).TrimStart(Array.Empty<char>());
				if (string.Equals(a, pendingDroppedItem.m_shared.m_name, StringComparison.Ordinal))
				{
					msg = "$msg_dropped " + displayNameLocalized;
					PendingDroppedItem = null;
					return;
				}
			}
			if (!string.IsNullOrEmpty(text3))
			{
				try
				{
					Regex regex = new Regex(Regex.Escape(text3), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(250.0));
					if (regex.IsMatch(msg))
					{
						msg = regex.Replace(msg, (Match _) => displayNameLocalized, 1);
						PendingDroppedItem = null;
						return;
					}
				}
				catch (RegexMatchTimeoutException)
				{
				}
			}
			if (!string.IsNullOrEmpty(pendingDroppedItem.m_shared.m_name))
			{
				try
				{
					Regex regex2 = new Regex(Regex.Escape(pendingDroppedItem.m_shared.m_name), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(250.0));
					if (regex2.IsMatch(msg))
					{
						msg = regex2.Replace(msg, (Match _) => displayNameLocalized, 1);
						PendingDroppedItem = null;
						return;
					}
				}
				catch (RegexMatchTimeoutException)
				{
				}
			}
			if (string.Equals(msg, text, StringComparison.Ordinal) || string.Equals(msg, text2, StringComparison.OrdinalIgnoreCase))
			{
				msg = (string.Equals(msg, text, StringComparison.Ordinal) ? (text + " " + displayNameLocalized) : (text2 + " " + displayNameLocalized));
				PendingDroppedItem = null;
			}
		}

		internal static void ApplyMessageHudShowMessage(Harmony harmony, ManualLogSource log)
		{
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Expected O, but got Unknown
			try
			{
				Type typeFromHandle = typeof(MessageHud);
				Type type = AccessTools.Inner(typeFromHandle, "MessageType") ?? typeFromHandle.GetNestedType("MessageType", BindingFlags.Public | BindingFlags.NonPublic);
				if (type == null)
				{
					log.LogWarning((object)"[DrakeModsLibs] Drop HUD: MessageHud.MessageType nested type not found.");
					return;
				}
				MethodBase methodBase = null;
				MethodInfo[] methods = typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (methodInfo.Name != "ShowMessage")
					{
						continue;
					}
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length >= 2 && !(parameters[0].ParameterType != type) && !(parameters[1].ParameterType != typeof(string)))
					{
						if (parameters.Length == 4 && parameters[2].ParameterType == typeof(int) && parameters[3].ParameterType == typeof(Sprite))
						{
							methodBase = methodInfo;
							break;
						}
						if ((object)methodBase == null)
						{
							methodBase = methodInfo;
						}
					}
				}
				if (methodBase == null)
				{
					log.LogWarning((object)"[DrakeModsLibs] Drop HUD: no suitable MessageHud.ShowMessage overload found.");
					return;
				}
				int num = -1;
				ParameterInfo[] parameters2 = methodBase.GetParameters();
				for (int j = 0; j < parameters2.Length; j++)
				{
					if (parameters2[j].ParameterType == typeof(string))
					{
						num = j;
						break;
					}
				}
				if (num < 0)
				{
					log.LogWarning((object)("[DrakeModsLibs] Drop HUD: ShowMessage overload has no string parameter: " + methodBase));
					return;
				}
				HarmonyMethod val = ((num == 0) ? new HarmonyMethod(typeof(DropHudMessagePatches), "MessageHudShowMessageStringArg0", (Type[])null) : new HarmonyMethod(typeof(DropHudMessagePatches), "MessageHudShowMessageStringArg1", (Type[])null));
				harmony.Patch(methodBase, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				log.LogInfo((object)("[DrakeModsLibs] Drop HUD: patched MessageHud." + methodBase.Name + " stringArg=" + num + " :: " + methodBase));
			}
			catch (Exception ex)
			{
				log.LogError((object)("[DrakeModsLibs] Drop HUD: MessageHud patch failed: " + ex));
			}
		}

		private static void MessageHudShowMessageStringArg0(ref string __0)
		{
			TryRewriteDroppedMessage(ref __0);
		}

		private static void MessageHudShowMessageStringArg1(ref string __1)
		{
			TryRewriteDroppedMessage(ref __1);
		}

		[HarmonyPatch(typeof(Character), "Message", new Type[]
		{
			typeof(MessageType),
			typeof(string),
			typeof(int),
			typeof(Sprite)
		})]
		[HarmonyPrefix]
		private static void CharacterMessagePrefix(MessageType type, ref string msg)
		{
			TryRewriteDroppedMessage(ref msg);
		}

		[HarmonyPatch(typeof(Player), "Message", new Type[]
		{
			typeof(MessageType),
			typeof(string),
			typeof(int),
			typeof(Sprite)
		})]
		[HarmonyPrefix]
		private static void PlayerMessagePrefix(MessageType type, ref string msg)
		{
			TryRewriteDroppedMessage(ref msg);
		}
	}
	internal static class ItemTooltipPatches
	{
		private const BindingFlags DeclaredTooltipFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static void Apply(Harmony harmony, ManualLogSource log)
		{
			MethodInfo methodInfo = ResolveGetTooltipPatchTarget(typeof(ItemData));
			if (methodInfo == null)
			{
				log.LogDebug((object)"[DrakeModsLibs] Crafted-by display: no patchable GetTooltip overload on ItemData hierarchy; grid tooltips still apply crafted-by via CreateItemTooltip.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, SelectPostfix(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static MethodInfo? ResolveGetTooltipPatchTarget(Type leafType)
		{
			MethodInfo methodInfo = null;
			MethodInfo methodInfo2 = null;
			MethodInfo methodInfo3 = null;
			Type type = leafType;
			while (type != null && type != typeof(object))
			{
				MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo4 in methods)
				{
					if (methodInfo4.Name != "GetTooltip" || methodInfo4.ReturnType != typeof(string))
					{
						continue;
					}
					if (methodInfo4.IsStatic)
					{
						ParameterInfo[] parameters = methodInfo4.GetParameters();
						if (parameters.Length == 3 && parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(bool) && (object)methodInfo == null)
						{
							methodInfo = methodInfo4;
						}
						continue;
					}
					ParameterInfo[] parameters2 = methodInfo4.GetParameters();
					if (parameters2.Length == 0)
					{
						if ((object)methodInfo2 == null)
						{
							methodInfo2 = methodInfo4;
						}
					}
					else if (parameters2.Length == 2 && parameters2[0].ParameterType == typeof(int) && parameters2[1].ParameterType == typeof(bool) && (object)methodInfo3 == null)
					{
						methodInfo3 = methodInfo4;
					}
				}
				type = type.BaseType;
			}
			return methodInfo ?? methodInfo2 ?? methodInfo3;
		}

		private static HarmonyMethod SelectPostfix(MethodInfo target)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			if (target.IsStatic)
			{
				return new HarmonyMethod(typeof(ItemTooltipPatches), "CraftedByDisplayStaticPostfix", (Type[])null);
			}
			ParameterInfo[] parameters = target.GetParameters();
			if (parameters.Length == 0)
			{
				return new HarmonyMethod(typeof(ItemTooltipPatches), "CraftedByDisplayInstancePostfix", (Type[])null);
			}
			if (parameters.Length == 2 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(bool))
			{
				return new HarmonyMethod(typeof(ItemTooltipPatches), "CraftedByDisplayInstanceQualityCraftingPostfix", (Type[])null);
			}
			throw new InvalidOperationException("ResolveGetTooltipPatchTarget and SelectPostfix are out of sync.");
		}

		internal static void CraftedByDisplayStaticPostfix(ItemData item, int qualityLevel, bool crafting, ref string __result)
		{
			__result = ApplyCraftedByDisplayToTooltipText(__result, item);
		}

		internal static void CraftedByDisplayInstancePostfix(ItemData __instance, ref string __result)
		{
			__result = ApplyCraftedByDisplayToTooltipText(__result, __instance);
		}

		internal static void CraftedByDisplayInstanceQualityCraftingPostfix(ItemData __instance, int qualityLevel, bool crafting, ref string __result)
		{
			__result = ApplyCraftedByDisplayToTooltipText(__result, __instance);
		}

		internal static string ApplyCraftedByDisplayToTooltipText(string text, ItemData item)
		{
			if (string.IsNullOrEmpty(text) || item?.m_customData == null)
			{
				return text;
			}
			if (item.m_crafterID == 0)
			{
				return text;
			}
			string text2 = item.m_crafterName ?? "";
			if (string.IsNullOrEmpty(text2))
			{
				return text;
			}
			string value;
			bool flag = item.m_customData.TryGetValue("Drake_CraftedByDisplay", out value) && !string.IsNullOrEmpty(value);
			string value2;
			bool flag2 = item.m_customData.TryGetValue("Drake_CraftedByLineLabel", out value2) && !string.IsNullOrEmpty(value2);
			if (!flag && !flag2)
			{
				return text;
			}
			string text3 = (flag ? value : text2);
			if (flag)
			{
				text3 = TooltipRichText.EnsureRichTextTagsClosedForTooltip(text3);
				text3 = TooltipRichText.WrapCraftedByDisplayWithDefaultStatColorIfNeeded(text3);
			}
			string lineLabelOverride = (flag2 ? value2 : null);
			return ReplaceCraftedBySegment(text, text2, text3, lineLabelOverride);
		}

		internal static string ReplaceCraftedBySegment(string text, string oldName, string newDisplay, string? lineLabelOverride = null)
		{
			if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(oldName))
			{
				return text;
			}
			if (!string.IsNullOrEmpty(lineLabelOverride))
			{
				string text2 = TryReplaceCraftedByWithCustomLineLabel(text, oldName, newDisplay, lineLabelOverride);
				if (text2 != null)
				{
					return text2;
				}
			}
			string text3 = "\n$item_crafter: " + oldName + " ";
			if (text.Contains(text3))
			{
				return text.Replace(text3, "\n$item_crafter: " + newDisplay + " ");
			}
			string text4 = "\n$item_crafter: " + oldName;
			if (text.Contains(text4))
			{
				return text.Replace(text4, "\n$item_crafter: " + newDisplay);
			}
			string text5 = "Crafted by";
			if (Localization.instance != null)
			{
				string text6 = Localization.instance.Localize("$item_crafter");
				if (!string.IsNullOrEmpty(text6))
				{
					text5 = text6;
				}
			}
			string[] array = new string[2] { " ", "" };
			foreach (string text7 in array)
			{
				string text8 = "\n" + text5 + ": " + oldName + text7;
				if (text.Contains(text8))
				{
					return text.Replace(text8, "\n" + text5 + ": " + newDisplay + ((text7 == " ") ? " " : ""));
				}
			}
			try
			{
				string text9 = Regex.Escape(text5);
				string text10 = Regex.Escape(oldName);
				Regex regex = new Regex("(?<prefix>\\n[^\\n]*?" + text9 + "\\s*:\\s*)(?:<color[^>]*>)?" + text10 + "(?:</color>)?", RegexOptions.None, TimeSpan.FromMilliseconds(250.0));
				if (regex.IsMatch(text))
				{
					return regex.Replace(text, (Match m) => m.Groups["prefix"].Value + newDisplay);
				}
			}
			catch (RegexMatchTimeoutException)
			{
			}
			try
			{
				Regex regex2 = new Regex("(?<prefix>\\n[^\\n]*?\\$item_crafter\\s*:\\s*)(?:<color[^>]*>)?" + Regex.Escape(oldName) + "(?:</color>)?", RegexOptions.None, TimeSpan.FromMilliseconds(250.0));
				if (regex2.IsMatch(text))
				{
					return regex2.Replace(text, (Match m) => m.Groups["prefix"].Value + newDisplay);
				}
			}
			catch (RegexMatchTimeoutException)
			{
			}
			string[] array2 = text.Split(new string[2] { "\r\n", "\n" }, StringSplitOptions.None);
			for (int num = 0; num < array2.Length; num++)
			{
				string text11 = array2[num];
				if ((text11.IndexOf("$item_crafter", StringComparison.OrdinalIgnoreCase) >= 0 || text11.IndexOf(text5, StringComparison.OrdinalIgnoreCase) >= 0) && text11.IndexOf(oldName, StringComparison.Ordinal) >= 0)
				{
					int startIndex = text11.IndexOf(oldName, StringComparison.Ordinal);
					array2[num] = text11.Remove(startIndex, oldName.Length).Insert(startIndex, newDisplay);
					return string.Join("\n", array2);
				}
			}
			return text;
		}

		private static string? TryReplaceCraftedByWithCustomLineLabel(string text, string oldName, string newDisplay, string lineLabelOverride)
		{
			string text2 = "\n$item_crafter: " + oldName + " ";
			if (text.Contains(text2))
			{
				return text.Replace(text2, "\n" + lineLabelOverride + ": " + newDisplay + " ");
			}
			string text3 = "\n$item_crafter: " + oldName;
			if (text.Contains(text3))
			{
				return text.Replace(text3, "\n" + lineLabelOverride + ": " + newDisplay);
			}
			string text4 = "Crafted by";
			if (Localization.instance != null)
			{
				string text5 = Localization.instance.Localize("$item_crafter");
				if (!string.IsNullOrEmpty(text5))
				{
					text4 = text5;
				}
			}
			string[] array = new string[2] { " ", "" };
			foreach (string text6 in array)
			{
				string text7 = "\n" + text4 + ": " + oldName + text6;
				if (text.Contains(text7))
				{
					return text.Replace(text7, "\n" + lineLabelOverride + ": " + newDisplay + ((text6 == " ") ? " " : ""));
				}
			}
			try
			{
				string text8 = Regex.Escape(text4);
				string text9 = Regex.Escape(oldName);
				Regex regex = new Regex("(?<whole>\\n[^\\n]*?" + text8 + "\\s*:\\s*)(?:<color[^>]*>)?" + text9 + "(?:</color>)?", RegexOptions.None, TimeSpan.FromMilliseconds(250.0));
				if (regex.IsMatch(text))
				{
					return regex.Replace(text, (Match _) => "\n" + lineLabelOverride + ": " + newDisplay);
				}
			}
			catch (RegexMatchTimeoutException)
			{
			}
			try
			{
				Regex regex2 = new Regex("(?<whole>\\n[^\\n]*?\\$item_crafter\\s*:\\s*)(?:<color[^>]*>)?" + Regex.Escape(oldName) + "(?:</color>)?", RegexOptions.None, TimeSpan.FromMilliseconds(250.0));
				if (regex2.IsMatch(text))
				{
					return regex2.Replace(text, (Match _) => "\n" + lineLabelOverride + ": " + newDisplay);
				}
			}
			catch (RegexMatchTimeoutException)
			{
			}
			string[] array2 = text.Split(new string[2] { "\r\n", "\n" }, StringSplitOptions.None);
			for (int num = 0; num < array2.Length; num++)
			{
				string text10 = array2[num];
				if ((text10.IndexOf("$item_crafter", StringComparison.OrdinalIgnoreCase) >= 0 || text10.IndexOf(text4, StringComparison.OrdinalIgnoreCase) >= 0) && text10.IndexOf(oldName, StringComparison.Ordinal) >= 0)
				{
					array2[num] = lineLabelOverride + ": " + newDisplay;
					return string.Join("\n", array2);
				}
			}
			return null;
		}
	}
}
namespace DrakeModsLibs.Input
{
	public sealed class MenuBindingRegistration
	{
		public string Id { get; }

		public string Scope { get; }

		public int Priority { get; }

		public Func<string?> GetBindingString { get; }

		public string ModLabel { get; }

		internal MenuBindingRegistration(string id, string scope, int priority, Func<string?> getBindingString, string modLabel)
		{
			Id = id;
			Scope = scope;
			Priority = priority;
			GetBindingString = getBindingString;
			ModLabel = modLabel;
		}
	}
	public static class MenuBindingRegistry
	{
		public const string InventoryContextScope = "inventory.context";

		private static readonly List<MenuBindingRegistration> Registrations = new List<MenuBindingRegistration>();

		private static readonly HashSet<string> LoggedConflicts = new HashSet<string>();

		private static ManualLogSource? Log;

		internal static void SetLogger(ManualLogSource log)
		{
			Log = log;
		}

		public static void Register(string id, string scope, int priority, Func<string?> getBindingString, string modLabel)
		{
			MenuBindingRegistration menuBindingRegistration = new MenuBindingRegistration(id, scope, priority, getBindingString, modLabel);
			Registrations.Add(menuBindingRegistration);
			WarnOnBindingConflict(menuBindingRegistration);
		}

		public static bool IsHeld(string scope, string id)
		{
			MenuBindingRegistration menuBindingRegistration = Registrations.FirstOrDefault((MenuBindingRegistration r) => r.Scope == scope && r.Id == id);
			return menuBindingRegistration != null && MenuKeyBinding.IsHeld(menuBindingRegistration.GetBindingString());
		}

		public static string? GetActiveBindingId(string scope)
		{
			foreach (MenuBindingRegistration item in from r in Registrations
				where r.Scope == scope
				orderby r.Priority descending
				select r)
			{
				if (MenuKeyBinding.IsHeld(item.GetBindingString()))
				{
					return item.Id;
				}
			}
			return null;
		}

		private static void WarnOnBindingConflict(MenuBindingRegistration added)
		{
			string text = Normalize(added.GetBindingString());
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			foreach (MenuBindingRegistration registration in Registrations)
			{
				if (registration.Id == added.Id || registration.Scope != added.Scope || Normalize(registration.GetBindingString()) != text)
				{
					continue;
				}
				string item = registration.Id + "|" + added.Id + "|" + text;
				if (LoggedConflicts.Add(item))
				{
					ManualLogSource? log = Log;
					if (log != null)
					{
						log.LogWarning((object)("[DrakeModsLibs] Menu binding conflict in scope '" + added.Scope + "': " + registration.ModLabel + " (" + registration.Id + ") and " + added.ModLabel + " (" + added.Id + ") both use '" + text + "'."));
					}
				}
			}
		}

		private static string Normalize(string? binding)
		{
			if (string.IsNullOrWhiteSpace(binding))
			{
				return "";
			}
			return binding.Trim().ToLowerInvariant();
		}
	}
	public static class MenuKeyBinding
	{
		public static bool IsHeld(string? binding)
		{
			if (string.IsNullOrWhiteSpace(binding))
			{
				return false;
			}
			string text = binding.Trim();
			if (string.Equals(text, "None", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			foreach (string item in SplitTokens(text))
			{
				if (!IsTokenHeld(item))
				{
					return false;
				}
			}
			return true;
		}

		public static string FormatForDisplay(string? binding, string emptyFallback = "Key")
		{
			if (string.IsNullOrWhiteSpace(binding))
			{
				return emptyFallback;
			}
			string text = binding.Trim();
			if (string.Equals(text, "None", StringComparison.OrdinalIgnoreCase))
			{
				return "";
			}
			List<string> list = SplitTokens(text).ToList();
			return (list.Count == 0) ? text : string.Join(" + ", list.Select(FormatToken));
		}

		private static IEnumerable<string> SplitTokens(string binding)
		{
			return from t in binding.Split(new char[4] { '+', ',', '&', ';' }, StringSplitOptions.RemoveEmptyEntries)
				select t.Trim() into t
				where t.Length > 0
				select t;
		}

		private static bool IsTokenHeld(string token)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			switch (token.ToLowerInvariant())
			{
			case "shift":
				return Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			case "ctrl":
			case "control":
				return Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
			case "alt":
				return Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307);
			default:
			{
				KeyCode result;
				return Enum.TryParse<KeyCode>(token, ignoreCase: true, out result) && Input.GetKey(result);
			}
			}
		}

		private unsafe static string FormatToken(string token)
		{
			switch (token.ToLowerInvariant())
			{
			case "shift":
				return "Shift";
			case "ctrl":
			case "control":
				return "Ctrl";
			case "alt":
				return "Alt";
			default:
			{
				KeyCode result;
				return Enum.TryParse<KeyCode>(token, ignoreCase: true, out result) ? ((object)(*(KeyCode*)(&result))/*cast due to .constrained prefix*/).ToString() : token;
			}
			}
		}
	}
}
namespace DrakeModsLibs.Display
{
	internal sealed class BuiltInRenameItNameLayer : IItemDisplayNameLayer
	{
		public int Priority => 100;

		public bool TryApply(ItemData? item, ref string displayName)
		{
			if (item?.m_customData == null)
			{
				return false;
			}
			if (!item.m_customData.TryGetValue("Drake_Rename", out var value) || string.IsNullOrEmpty(value))
			{
				return false;
			}
			displayName = value;
			return true;
		}
	}
	internal static class DisplayNameModifierHub
	{
		public static bool AffectsDisplay(ItemData? item)
		{
			foreach (IDisplayNameModifier displayNameModifier in CustomizeLibsRuntime.DisplayNameModifiers)
			{
				if (displayNameModifier.AffectsDisplay(item))
				{
					return true;
				}
			}
			return false;
		}

		public static string GetPrefixRaw(ItemData? item)
		{
			foreach (IDisplayNameModifier displayNameModifier in CustomizeLibsRuntime.DisplayNameModifiers)
			{
				string prefixRaw = displayNameModifier.GetPrefixRaw(item);
				if (!string.IsNullOrEmpty(prefixRaw))
				{
					return prefixRaw;
				}
			}
			return "";
		}
	}
	internal static class DisplayNameResolutionPipeline
	{
		internal static void EnsureDefaultLayers()
		{
			if (!CustomizeLibsRuntime.DefaultDisplayLayersRegistered)
			{
				CustomizeLibsRuntime.DefaultDisplayLayersRegistered = true;
				RegisterLayer(new BuiltInRenameItNameLayer());
			}
		}

		public static void RegisterLayer(IItemDisplayNameLayer layer)
		{
			if (layer != null && !CustomizeLibsRuntime.DisplayNameLayers.Contains(layer))
			{
				CustomizeLibsRuntime.DisplayNameLayers.Add(layer);
			}
		}

		internal static string Resolve(ItemData? item, bool localize)
		{
			EnsureDefaultLayers();
			if (item?.m_shared == null)
			{
				return "";
			}
			string displayName = item.m_shared.m_name;
			foreach (IItemDisplayNameLayer item2 in CustomizeLibsRuntime.DisplayNameLayers.OrderBy((IItemDisplayNameLayer l) => l.Priority))
			{
				item2.TryApply(item, ref displayName);
			}
			string prefixRaw = DisplayNameModifierHub.GetPrefixRaw(item);
			string text = (string.IsNullOrEmpty(prefixRaw) ? displayName : (prefixRaw + " " + displayName));
			string text2 = TooltipRichText.EnsureRichTextTagsClosedForTooltip(text);
			if (!localize || Localization.instance == null)
			{
				return text2;
			}
			return Localization.instance.Localize(text2);
		}
	}
	public static class ItemDisplayService
	{
		public static string GetProperName(ItemData? item)
		{
			if (item?.m_shared == null)
			{
				return "";
			}
			return GetProperName(item, item.m_shared.m_name);
		}

		public static string GetProperName(ItemData? item, string defaultName)
		{
			if (item == null)
			{
				return defaultName;
			}
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			string value;
			return item.m_customData.TryGetValue("Drake_Rename", out value) ? value : defaultName;
		}

		public static string GetProperDescription(ItemData? item)
		{
			if (item?.m_shared == null)
			{
				return "";
			}
			return GetProperDescription(item, item.m_shared.m_description);
		}

		public static string GetProperDescription(ItemData? item, string defaultDesc)
		{
			if (item == null)
			{
				return defaultDesc;
			}
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			string value;
			return item.m_customData.TryGetValue("Drake_Rename_Desc", out value) ? value : defaultDesc;
		}

		public static string GetDisplayNameForUi(ItemData? item, bool localize)
		{
			return DisplayNameResolutionPipeline.Resolve(item, localize);
		}

		public static bool HasCustomName(ItemData? item)
		{
			return item?.m_customData != null && item.m_customData.ContainsKey("Drake_Rename");
		}

		public static bool HasCustomDescription(ItemData? item)
		{
			return item?.m_customData != null && item.m_customData.ContainsKey("Drake_Rename_Desc");
		}

		public static bool HasCraftedByDisplayOverride(ItemData? item)
		{
			string value;
			return item?.m_customData != null && item.m_customData.TryGetValue("Drake_CraftedByDisplay", out value) && !string.IsNullOrEmpty(value);
		}

		public static bool HasCraftedByLineLabelOverride(ItemData? item)
		{
			string value;
			return item?.m_customData != null && item.m_customData.TryGetValue("Drake_CraftedByLineLabel", out value) && !string.IsNullOrEmpty(value);
		}

		public static bool HasAnyCustomization(ItemData? item)
		{
			if (item?.m_customData == null)
			{
				return false;
			}
			if (HasCustomName(item) || HasCustomDescription(item))
			{
				return true;
			}
			return HasCraftedByDisplayOverride(item) || HasCraftedByLineLabelOverride(item);
		}

		public static void SetCustomName(ItemData item, string? name)
		{
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			if (string.IsNullOrEmpty(name))
			{
				item.m_customData.Remove("Drake_Rename");
			}
			else
			{
				item.m_customData["Drake_Rename"] = name;
			}
		}

		public static void SetCustomDescription(ItemData item, string? desc)
		{
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			if (string.IsNullOrEmpty(desc))
			{
				item.m_customData.Remove("Drake_Rename_Desc");
			}
			else
			{
				item.m_customData["Drake_Rename_Desc"] = desc;
			}
		}

		public static string GetCraftedByDisplay(ItemData? item)
		{
			if (item?.m_customData == null)
			{
				return item?.m_crafterName ?? "";
			}
			if (item.m_customData.TryGetValue("Drake_CraftedByDisplay", out var value) && !string.IsNullOrEmpty(value))
			{
				return value;
			}
			return item.m_crafterName ?? "";
		}

		public static void SetCraftedByDisplay(ItemData item, string? display)
		{
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			if (string.IsNullOrEmpty(display))
			{
				item.m_customData.Remove("Drake_CraftedByDisplay");
			}
			else
			{
				item.m_customData["Drake_CraftedByDisplay"] = display;
			}
		}

		public static void SetCraftedByLineLabel(ItemData item, string? lineLabel)
		{
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			if (string.IsNullOrEmpty(lineLabel))
			{
				item.m_customData.Remove("Drake_CraftedByLineLabel");
			}
			else
			{
				item.m_customData["Drake_CraftedByLineLabel"] = lineLabel;
			}
		}

		public static void ClearCraftedByOverrides(ItemData item)
		{
			if (item.m_customData != null)
			{
				item.m_customData.Remove("Drake_CraftedByDisplay");
				item.m_customData.Remove("Drake_CraftedByLineLabel");
			}
		}
	}
	internal static class TooltipRichText
	{
		private enum RtKind
		{
			Color,
			Size
		}

		private static readonly Regex HexColorRegex = new Regex("^[0-9a-fA-F]{3,8}$", RegexOptions.Compiled);

		internal static string EnsureRichTextTagsClosedForTooltip(string? text)
		{
			if (text == null || text.Length == 0)
			{
				return "";
			}
			Stack<RtKind> stack = new Stack<RtKind>();
			int num = 0;
			while (num < text.Length)
			{
				if (text[num] != '<')
				{
					num++;
					continue;
				}
				int num2 = text.IndexOf('>', num);
				if (num2 < 0)
				{
					break;
				}
				string text2 = text.Substring(num, num2 - num + 1);
				if (text2.Length >= 2 && text2[1] == '/')
				{
					if (text2.StartsWith("</color", StringComparison.OrdinalIgnoreCase))
					{
						if (stack.Count > 0 && stack.Peek() == RtKind.Color)
						{
							stack.Pop();
						}
					}
					else if (text2.StartsWith("</size", StringComparison.OrdinalIgnoreCase) && stack.Count > 0 && stack.Peek() == RtKind.Size)
					{
						stack.Pop();
					}
				}
				else if (text2.StartsWith("<color", StringComparison.OrdinalIgnoreCase))
				{
					stack.Push(RtKind.Color);
				}
				else if (IsHashColorOpenTag(text2))
				{
					stack.Push(RtKind.Color);
				}
				else if (text2.StartsWith("<size", StringComparison.OrdinalIgnoreCase))
				{
					stack.Push(RtKind.Size);
				}
				num = num2 + 1;
			}
			if (stack.Count == 0)
			{
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder(text, text.Length + stack.Count * 10);
			while (stack.Count > 0)
			{
				RtKind rtKind = stack.Pop();
				stringBuilder.Append((rtKind == RtKind.Color) ? "</color>" : "</size>");
			}
			return stringBuilder.ToString();
		}

		internal static string WrapCraftedByDisplayWithDefaultStatColorIfNeeded(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			if (HasExplicitColorMarkup(text))
			{
				return text;
			}
			return GetValheimTooltipStatColorOpenTag() + text + "</color>";
		}

		internal static bool HasExplicitColorMarkup(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			if (text.IndexOf("<color", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			for (int i = 0; i < text.Length - 3; i++)
			{
				if (text[i] == '<' && text[i + 1] == '#')
				{
					int j;
					for (j = i + 2; j < text.Length && IsHex(text[j]); j++)
					{
					}
					if (j != i + 2 && j - (i + 2) >= 3 && j - (i + 2) <= 8 && j < text.Length && text[j] == '>')
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool IsHex(char c)
		{
			switch (c)
			{
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case 'A':
			case 'B':
			case 'C':
			case 'D':
			case 'E':
			case 'F':
			case 'a':
			case 'b':
			case 'c':
			case 'd':
			case 'e':
			case 'f':
				return true;
			default:
				return false;
			}
		}

		private static string GetValheimTooltipStatColorOpenTag()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GUIManager instance = GUIManager.Instance;
				if (instance != null)
				{
					Color valheimOrange = instance.ValheimOrange;
					return "<color=#" + ColorUtility.ToHtmlStringRGB(valheimOrange) + ">";
				}
			}
			catch
			{
			}
			return "<color=#ff8800>";
		}

		private static bool IsHashColorOpenTag(string tag)
		{
			if (tag.Length < 5 || tag[0] != '<' || tag[1] != '#' || tag[tag.Length - 1] != '>')
			{
				return false;
			}
			string input = tag.Substring(2, tag.Length - 3);
			return HexColorRegex.IsMatch(input);
		}
	}
}
namespace DrakeModsLibs.Data
{
	public static class DrakeCustomDataCatalog
	{
		public const string ModWorkshopLibs = "DrakeModsLibs";

		public const string ModRenameIt = "DrakesRenameIt";

		public const string ModQuestItems = "DrakesQuestItems";

		public const string ModItemShop = "DrakesItemShop";

		private static readonly Dictionary<string, DrakeCustomDataField> ByKey;

		private static readonly Dictionary<string, List<DrakeCustomDataField>> ByMod;

		static DrakeCustomDataCatalog()
		{
			ByKey = new Dictionary<string, DrakeCustomDataField>(StringComparer.Ordinal);
			ByMod = new Dictionary<string, List<DrakeCustomDataField>>(StringComparer.Ordinal);
			RegisterBuiltInFields();
		}

		public static void RegisterModFields(string modId, IEnumerable<DrakeCustomDataField> fields)
		{
			if (string.IsNullOrEmpty(modId) || fields == null)
			{
				return;
			}
			if (!ByMod.TryGetValue(modId, out List<DrakeCustomDataField> value))
			{
				value = new List<DrakeCustomDataField>();
				ByMod[modId] = value;
			}
			foreach (DrakeCustomDataField field in fields)
			{
				if (field != null && !string.IsNullOrEmpty(field.Key))
				{
					ByKey[field.Key] = field;
					value.RemoveAll((DrakeCustomDataField f) => string.Equals(f.Key, field.Key, StringComparison.Ordinal));
					value.Add(field);
				}
			}
		}

		public static bool TryGetField(string key, out DrakeCustomDataField field)
		{
			return ByKey.TryGetValue(key, out field);
		}

		public static IReadOnlyList<DrakeCustomDataField> GetFieldsForMod(string modId)
		{
			if (string.IsNullOrEmpty(modId) || !ByMod.TryGetValue(modId, out List<DrakeCustomDataField> value))
			{
				return Array.Empty<DrakeCustomDataField>();
			}
			return value.ToArray();
		}

		public static IReadOnlyList<DrakeCustomDataField> GetAllFields()
		{
			return ByKey.Values.ToArray();
		}

		public static bool IsDrakeKey(string key)
		{
			return !string.IsNullOrEmpty(key) && (key.StartsWith("Drake_", StringComparison.Ordinal) || ByKey.ContainsKey(key));
		}

		private static void RegisterBuiltInFields()
		{
			RegisterModFields("DrakesRenameIt", new DrakeCustomDataField[7]
			{
				new DrakeCustomDataField("Drake_Rename", "DrakesRenameIt", DrakeCustomDataKind.Text, "Custom name"),
				new DrakeCustomDataField("Drake_Rename_Desc", "DrakesRenameIt", DrakeCustomDataKind.Text, "Custom description"),
				new DrakeCustomDataField("Drake_CraftedByDisplay", "DrakesRenameIt", DrakeCustomDataKind.Text, "Crafted-by display"),
				new DrakeCustomDataField("Drake_CraftedByLineLabel", "DrakesRenameIt", DrakeCustomDataKind.Text, "Crafted-by line label"),
				new DrakeCustomDataField("Drake_RenameUnlocked", "DrakesRenameIt", DrakeCustomDataKind.Tag, "Rename unlocked"),
				new DrakeCustomDataField("Drake_PublicRewrite", "DrakesRenameIt", DrakeCustomDataKind.Tag, "Anyone can rewrite name/desc"),
				new DrakeCustomDataField("DrakeRenameIt_CustomName", "DrakesRenameIt", DrakeCustomDataKind.Text, "Item stand hover name (ZDO)")
			});
			RegisterModFields("DrakeModsLibs", new DrakeCustomDataField[3]
			{
				new DrakeCustomDataField("Drake_NoRename", "DrakeModsLibs", DrakeCustomDataKind.Tag, "Block rename"),
				new DrakeCustomDataField("Drake_NoDesc", "DrakeModsLibs", DrakeCustomDataKind.Tag, "Block description edit"),
				new DrakeCustomDataField("Drake_NoCraftedByEdit", "DrakeModsLibs", DrakeCustomDataKind.Tag, "Block crafted-by edit")
			});
			RegisterModFields("DrakesQuestItems", new DrakeCustomDataField[1]
			{
				new DrakeCustomDataField("Drake_QuestItem", "DrakesQuestItems", DrakeCustomDataKind.Tag, "Quest item")
			});
			RegisterModFields("DrakesItemShop", new DrakeCustomDataField[1]
			{
				new DrakeCustomDataField("Drake_MarketPrice", "DrakesItemShop", DrakeCustomDataKind.Text, "Market price")
			});
		}
	}
	public static class DrakeCustomDataKeys
	{
		public const string Prefix = "Drake_";

		public const string Rename = "Drake_Rename";

		public const string RenameDescription = "Drake_Rename_Desc";

		public const string CraftedByDisplay = "Drake_CraftedByDisplay";

		public const string CraftedByLineLabel = "Drake_CraftedByLineLabel";

		public const string RenameUnlocked = "Drake_RenameUnlocked";

		public const string PublicRewrite = "Drake_PublicRewrite";

		public const string ItemStandHoverName = "DrakeRenameIt_CustomName";

		public const string NoRename = "Drake_NoRename";

		public const string NoDescription = "Drake_NoDesc";

		public const string NoCraftedByEdit = "Drake_NoCraftedByEdit";

		public const string QuestItem = "Drake_QuestItem";

		public const string MarketPrice = "Drake_MarketPrice";
	}
	internal static class ItemCustomDataReader
	{
		private readonly struct DumpEntry
		{
			public string Key { get; }

			public string Display { get; }

			public string ModId { get; }

			public string? Label { get; }

			public DumpEntry(string key, string display, string modId, string? label)
			{
				Key = key;
				Display = display;
				ModId = modId;
				Label = label;
			}
		}

		public static bool TryGetRawValue(ItemData? item, string key, out string? value)
		{
			value = null;
			if (item?.m_customData == null || string.IsNullOrEmpty(key))
			{
				return false;
			}
			if (!item.m_customData.TryGetValue(key, out var value2))
			{
				return false;
			}
			value = value2;
			return true;
		}

		public static string? GetDisplayValue(ItemData? item, string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return null;
			}
			if (DrakeCustomDataCatalog.TryGetField(key, out DrakeCustomDataField field))
			{
				if (field.Kind == DrakeCustomDataKind.Tag)
				{
					return DrakeTagManager.HasTag(item, key) ? "true" : null;
				}
				string value;
				return TryGetRawValue(item, key, out value) ? value : null;
			}
			if (DrakeTagManager.HasTag(item, key))
			{
				return "true";
			}
			string value2;
			return TryGetRawValue(item, key, out value2) ? value2 : null;
		}

		public static string Dump(ItemData? item, ItemCustomDataDumpOptions? options)
		{
			if (options == null)
			{
				options = new ItemCustomDataDumpOptions();
			}
			List<DumpEntry> entries = CollectEntries(item, options);
			return (options.Format == ItemCustomDataDumpFormat.Json) ? DumpJson(item, entries, options) : DumpNeat(item, entries, options);
		}

		private static List<DumpEntry> CollectEntries(ItemData? item, ItemCustomDataDumpOptions options)
		{
			List<DumpEntry> entries = new List<DumpEntry>();
			HashSet<string> seen = new HashSet<string>(StringComparer.Ordinal);
			foreach (DrakeCustomDataField allField in DrakeCustomDataCatalog.GetAllFields())
			{
				Add(allField.Key, GetDisplayValue(item, allField.Key), allField.ModId, allField.Label);
			}
			if (item?.m_customData != null && options.IncludeUnknownKeys)
			{
				foreach (KeyValuePair<string, string> customDatum in item.m_customData)
				{
					if (!seen.Contains(customDatum.Key) && PassesFilter(customDatum.Key, "(unregistered)", options))
					{
						Add(customDatum.Key, GetDisplayValue(item, customDatum.Key), "(unregistered)", null);
					}
				}
			}
			entries.Sort(delegate(DumpEntry a, DumpEntry b)
			{
				int num = string.Compare(a.ModId, b.ModId, StringComparison.Ordinal);
				return (num != 0) ? num : string.Compare(a.Key, b.Key, StringComparison.Ordinal);
			});
			return entries;
			void Add(string key, string? display, string modId, string? label)
			{
				if (PassesFilter(key, modId, options) && (display != null || options.IncludeEmpty) && seen.Add(key))
				{
					entries.Add(new DumpEntry(key, display ?? "", modId, label));
				}
			}
		}

		private static bool PassesFilter(string key, string modId, ItemCustomDataDumpOptions options)
		{
			if (!string.IsNullOrEmpty(options.Key) && !string.Equals(key, options.Key, StringComparison.Ordinal))
			{
				return false;
			}
			if (!string.IsNullOrEmpty(options.ModId) && !string.Equals(modId, options.ModId, StringComparison.Ordinal))
			{
				return false;
			}
			if (options.DrakeKeysOnly && !DrakeCustomDataCatalog.IsDrakeKey(key))
			{
				return false;
			}
			return true;
		}

		private static string DumpNeat(ItemData? item, List<DumpEntry> entries, ItemCustomDataDumpOptions options)
		{
			StringBuilder stringBuilder = new StringBuilder();
			if (options.IncludeItemSummary)
			{
				stringBuilder.AppendLine(SummarizeItem(item));
			}
			string a = null;
			foreach (DumpEntry entry in entries)
			{
				if (!string.Equals(a, entry.ModId, StringComparison.Ordinal))
				{
					if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '\n')
					{
						stringBuilder.AppendLine();
					}
					stringBuilder.Append('[').Append(entry.ModId).Append(']')
						.AppendLine();
					a = entry.ModId;
				}
				stringBuilder.Append("  ").Append(entry.Key);
				if (!string.IsNullOrEmpty(entry.Label))
				{
					stringBuilder.Append(" (").Append(entry.Label).Append(')');
				}
				stringBuilder.Append(" = ");
				stringBuilder.AppendLine(FormatNeatValue(entry.Display));
			}
			if (entries.Count == 0)
			{
				stringBuilder.AppendLine("(no matching custom data)");
			}
			return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
		}

		private static string DumpJson(ItemData? item, List<DumpEntry> entries, ItemCustomDataDumpOptions options)
		{
			StringBuilder stringBuilder = new StringBuilder("{");
			if (options.IncludeItemSummary)
			{
				AppendJsonProp(stringBuilder, "item", SummarizeItem(item), first: true);
				stringBuilder.Append(',');
			}
			bool flag = !options.IncludeItemSummary;
			foreach (DumpEntry entry in entries)
			{
				if (!flag)
				{
					stringBuilder.Append(',');
				}
				AppendJsonProp(stringBuilder, entry.Key, entry.Display, flag);
				flag = false;
			}
			stringBuilder.Append('}');
			return stringBuilder.ToString();
		}

		private static void AppendJsonProp(StringBuilder sb, string key, string value, bool first)
		{
			if (!first)
			{
				sb.Append(',');
			}
			sb.Append('"').Append(JsonEscape(key)).Append("\":\"")
				.Append(JsonEscape(value))
				.Append('"');
		}

		private static string FormatNeatValue(string display)
		{
			bool flag = ((display == "true" || display == "false") ? true : false);
			return flag ? display : ("\"" + display + "\"");
		}

		private static string SummarizeItem(ItemData? item)
		{
			if (item?.m_shared == null)
			{
				return "item: (null)";
			}
			string arg = item.m_shared.m_name ?? "?";
			return $"item: {arg} x{item.m_stack}";
		}

		private static string JsonEscape(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			foreach (char c in s)
			{
				switch (c)
				{
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					stringBuilder.AppendFormat("\\u{0:X4}", (int)c);
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}
	}
}
namespace DrakeModsLibs.API
{
	public static class CustomizationEvents
	{
		public static event Action<Player, ItemData, string, string>? OnItemNameChanged;

		public static event Action<Player, ItemData, string, string>? OnItemDescriptionChanged;

		public static event Action<Player, ItemData, string, string, string>? OnCraftedByDisplayChanged;

		public static void RaiseNameChanged(Player player, ItemData item, string oldName, string newName)
		{
			CustomizationEvents.OnItemNameChanged?.Invoke(player, item, oldName, newName);
		}

		public static void RaiseDescriptionChanged(Player player, ItemData item, string oldDesc, string newDesc)
		{
			CustomizationEvents.OnItemDescriptionChanged?.Invoke(player, item, oldDesc, newDesc);
		}

		public static void RaiseCraftedByDisplayChanged(Player player, ItemData item, string itemPrefabName, string oldDisplay, string newDisplay)
		{
			CustomizationEvents.OnCraftedByDisplayChanged?.Invoke(player, item, itemPrefabName, oldDisplay, newDisplay);
		}
	}
	public static class CustomizeLibsAPI
	{
		public static DrakeConfigSync CreateConfigSync(string modId, string displayName, string currentVersion, string minimumRequiredVersion = null)
		{
			return DrakeConfigSync.Create(modId, displayName, currentVersion, minimumRequiredVersion);
		}

		public static string GetDisplayNameForUi(ItemData? item, bool localize = false)
		{
			return ItemDisplayService.GetDisplayNameForUi(item, localize);
		}

		public static string GetProperName(ItemData? item)
		{
			return ItemDisplayService.GetProperName(item);
		}

		public static string GetProperDescription(ItemData? item)
		{
			return ItemDisplayService.GetProperDescription(item);
		}

		public static bool HasCustomName(ItemData? item)
		{
			return ItemDisplayService.HasCustomName(item);
		}

		public static bool HasCustomDescription(ItemData? item)
		{
			return ItemDisplayService.HasCustomDescription(item);
		}

		public static string GetProperName(ItemData? item, string defaultName)
		{
			return ItemDisplayService.GetProperName(item, defaultName);
		}

		public static string GetProperDescription(ItemData? item, string defaultDesc)
		{
			return ItemDisplayService.GetProperDescription(item, defaultDesc);
		}

		public static bool HasCraftedByDisplayOverride(ItemData? item)
		{
			return ItemDisplayService.HasCraftedByDisplayOverride(item);
		}

		public static bool HasCraftedByLineLabelOverride(ItemData? item)
		{
			return ItemDisplayService.HasCraftedByLineLabelOverride(item);
		}

		public static bool HasAnyCustomization(ItemData? item)
		{
			return ItemDisplayService.HasAnyCustomization(item);
		}

		public static string GetCraftedByDisplay(ItemData? item)
		{
			return ItemDisplayService.GetCraftedByDisplay(item);
		}

		public static void SetCustomName(ItemData item, string? name)
		{
			ItemDisplayService.SetCustomName(item, name);
		}

		public static void SetCustomDescription(ItemData item, string? desc)
		{
			ItemDisplayService.SetCustomDescription(item, desc);
		}

		public static void SetCraftedByDisplay(ItemData item, string? display)
		{
			ItemDisplayService.SetCraftedByDisplay(item, display);
		}

		public static void SetCraftedByLineLabel(ItemData item, string? lineLabel)
		{
			ItemDisplayService.SetCraftedByLineLabel(item, lineLabel);
		}

		public static void ClearCraftedByOverrides(ItemData item)
		{
			ItemDisplayService.ClearCraftedByOverrides(item);
		}

		public static bool IsBlockedByTag(CustomizeOperation operation, ItemData? item)
		{
			return CustomizationGatekeeper.IsBlockedByTag(operation, item);
		}

		public static void RegisterDisplayNameModifier(IDisplayNameModifier modifier)
		{
			if (modifier != null && !CustomizeLibsRuntime.DisplayNameModifiers.Contains(modifier))
			{
				CustomizeLibsRuntime.DisplayNameModifiers.Add(modifier);
			}
		}

		public static void RegisterStackMergePolicy(IStackMergePolicy policy)
		{
			CustomizeLibsRuntime.StackMergePolicy = policy;
		}

		public static void SetShowItemStandItemNameWhenNoAccess(bool value)
		{
			CustomizeLibsRuntime.ShowItemStandItemNameWhenNoAccess = value;
		}

		public static void RefreshItemStandDisplayNames()
		{
			ItemStandPatch.RefreshAllItemStandDisplayNames();
		}

		public static bool HasTag(ItemData? item, string tagKey)
		{
			return DrakeTagManager.HasTag(item, tagKey);
		}

		public static void SetTag(ItemData item, string tagKey)
		{
			DrakeTagManager.SetTag(item, tagKey);
		}

		public static void ClearTag(ItemData item, string tagKey)
		{
			DrakeTagManager.ClearTag(item, tagKey);
		}

		public static void RegisterEditValidator(CustomizeOperation operation, CustomizeEditValidator validator)
		{
			CustomizationGatekeeper.RegisterValidator(operation, validator);
		}

		public static bool CanPerform(CustomizeOperation operation, ItemData? item, Player? player)
		{
			return CustomizationGatekeeper.CanPerform(operation, item, player);
		}

		public static void RegisterTagBlockRule(string tagKey, CustomizeOperation blockedOperations)
		{
			CustomizationGatekeeper.RegisterTagBlockRule(tagKey, blockedOperations);
		}

		public static void RegisterDisplayNameLayer(IItemDisplayNameLayer layer)
		{
			DisplayNameResolutionPipeline.RegisterLayer(layer);
		}

		public static bool CanRenameItem(ItemData? item, Player? player)
		{
			return CanPerform(CustomizeOperation.RenameName, item, player);
		}

		public static bool CanEditDescription(ItemData? item, Player? player)
		{
			return CanPerform(CustomizeOperation.RenameDescription, item, player);
		}

		public static bool CanEditCraftedBy(ItemData? item, Player? player)
		{
			return CanPerform(CustomizeOperation.EditCraftedBy, item, player);
		}

		public static bool TryGetCustomDataRaw(ItemData? item, string key, out string? value)
		{
			return ItemCustomDataReader.TryGetRawValue(item, key, out value);
		}

		public static string? GetCustomDataRaw(ItemData? item, string key)
		{
			string value;
			return TryGetCustomDataRaw(item, key, out value) ? value : null;
		}

		public static string? GetCustomDataValue(ItemData? item, string key)
		{
			return ItemCustomDataReader.GetDisplayValue(item, key);
		}

		public static string DumpItemCustomData(ItemData? item, ItemCustomDataDumpOptions? options = null)
		{
			return ItemCustomDataReader.Dump(item, options);
		}

		public static string DumpItemCustomData(ItemData? item, string key)
		{
			return DumpItemCustomData(item, new ItemCustomDataDumpOptions
			{
				Key = key
			});
		}

		public static string DumpItemCustomDataForMod(ItemData? item, string modId, ItemCustomDataDumpFormat format = ItemCustomDataDumpFormat.Neat)
		{
			return DumpItemCustomData(item, new ItemCustomDataDumpOptions
			{
				ModId = modId,
				Format = format
			});
		}

		public static string DumpAllDrakeCustomData(ItemData? item, ItemCustomDataDumpFormat format = ItemCustomDataDumpFormat.Neat)
		{
			return DumpItemCustomData(item, new ItemCustomDataDumpOptions
			{
				DrakeKeysOnly = true,
				Format = format
			});
		}

		public static void RegisterModCustomDataFields(string modId, IEnumerable<DrakeCustomDataField> fields)
		{
			DrakeCustomDataCatalog.RegisterModFields(modId, fields);
		}

		public static IReadOnlyList<DrakeCustomDataField> GetRegisteredCustomDataFields(string? modId = null)
		{
			return string.IsNullOrEmpty(modId) ? DrakeCustomDataCatalog.GetAllFields() : DrakeCustomDataCatalog.GetFieldsForMod(modId);
		}
	}
	[Flags]
	public enum CustomizeOperation
	{
		None = 0,
		RenameName = 1,
		RenameDescription = 2,
		EditCraftedBy = 4,
		SetTag = 8,
		AllEdits = 7
	}
	public sealed class DrakeCustomDataField
	{
		public string Key { get; }

		public string ModId { get; }

		public DrakeCustomDataKind Kind { get; }

		public string? Label { get; }

		public DrakeCustomDataField(string key, string modId, DrakeCustomDataKind kind, string? label = null)
		{
			Key = key ?? throw new ArgumentNullException("key");
			ModId = modId ?? throw new ArgumentNullException("modId");
			Kind = kind;
			Label = label;
		}
	}
	public enum DrakeCustomDataKind
	{
		Tag,
		Text
	}
	public interface IDisplayNameModifier
	{
		bool AffectsDisplay(ItemData? item);

		string GetPrefixRaw(ItemData? item);
	}
	public interface IItemDisplayNameLayer
	{
		int Priority { get; }

		bool TryApply(ItemData? item, ref string displayName);
	}
	public static class DisplayNameLayerPriority
	{
		public const int RenameItBase = 100;

		public const int CosmeticPrefix = 150;

		public const int LootOverlay = 200;

		public const int ShopOverlay = 300;
	}
	public interface IStackMergePolicy
	{
		bool SeparateStacksEnabled { get; }

		bool SeparateStacksHardLock { get; }
	}
	public enum ItemCustomDataDumpFormat
	{
		Neat,
		Json
	}
	public sealed class ItemCustomDataDumpOptions
	{
		public ItemCustomDataDumpFormat Format { get; set; } = ItemCustomDataDumpFormat.Neat;

		public string? ModId { get; set; }

		public string? Key { get; set; }

		public bool DrakeKeysOnly { get; set; }

		public bool IncludeEmpty { get; set; }

		public bool IncludeUnknownKeys { get; set; } = true;

		public bool IncludeItemSummary { get; set; } = true;
	}
	public readonly struct TagBlockRule
	{
		public string TagKey { get; }

		public CustomizeOperation BlockedOperations { get; }

		public TagBlockRule(string tagKey, CustomizeOperation blockedOperations)
		{
			TagKey = tagKey;
			BlockedOperations = blockedOperations;
		}
	}
}
namespace ServerSync
{
	[PublicAPI]
	internal abstract class OwnConfigEntryBase
	{
		public object? LocalBaseValue;

		public bool SynchronizedConfig = true;

		public abstract ConfigEntryBase BaseConfig { get; }
	}
	[PublicAPI]
	internal class SyncedConfigEntry<T>(ConfigEntry<T> sourceConfig) : OwnConfigEntryBase()
	{
		public readonly ConfigEntry<T> SourceConfig = sourceConfig;

		public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig;

		public T Value
		{
			get
			{
				return SourceConfig.Value;
			}
			set
			{
				SourceConfig.Value = value;
			}
		}

		public void AssignLocalValue(T value)
		{
			if (LocalBaseValue == null)
			{
				Value = value;
			}
			else
			{
				LocalBaseValue = value;
			}
		}
	}
	internal abstract class CustomSyncedValueBase
	{
		public object? LocalBaseValue;

		public readonly string Identifier;

		public readonly Type Type;

		private object? boxedValue;

		protected bool localIsOwner;

		public readonly int Priority;

		public object? BoxedValue
		{
			get
			{
				return boxedValue;
			}
			set
			{
				boxedValue = value;
				this.ValueChanged?.Invoke();
			}
		}

		public event Action? ValueChanged;

		protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority)
		{
			Priority = priority;
			Identifier = identifier;
			Type = type;
			configSync.AddCustomValue(this);
			localIsOwner = configSync.IsSourceOfTruth;
			configSync.SourceOfTruthChanged += delegate(bool truth)
			{
				localIsOwner = truth;
			};
		}
	}
	[PublicAPI]
	internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase
	{
		public T Value
		{
			get
			{
				return (T)base.BoxedValue;
			}
			set
			{
				base.BoxedValue = value;
			}
		}

		public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0)
			: base(configSync, identifier, typeof(T), priority)
		{
			Value = value;
		}

		public void AssignLocalValue(T value)
		{
			if (localIsOwner)
			{
				Value = value;
			}
			else
			{
				LocalBaseValue = value;
			}
		}
	}
	internal class ConfigurationManagerAttributes
	{
		[UsedImplicitly]
		public bool? ReadOnly = false;
	}
	[PublicAPI]
	internal class ConfigSync
	{
		[HarmonyPatch(typeof(ZRpc), "HandlePackage")]
		private static class SnatchCurrentlyHandlingRPC
		{
			public static ZRpc? currentRpc;

			[HarmonyPrefix]
			private static void Prefix(ZRpc __instance)
			{
				currentRpc = __instance;
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		internal static class RegisterRPCPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ZNet __instance)
			{
				isServer = __instance.IsServer();
				foreach (ConfigSync configSync2 in configSyncs)
				{
					ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync);
					if (isServer)
					{
						configSync2.InitialSyncDone = true;
						Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections"));
					}
				}
				if (isServer)
				{
					((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges());
				}
				static void SendAdmin(List<ZNetPeer> peers, bool isAdmin)
				{
					ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1]
					{
						new PackageEntry
						{
							section = "Internal",
							key = "lockexempt",
							type = typeof(bool),
							value = isAdmin
						}
					});
					ConfigSync configSync = configSyncs.First();
					if (config