Decompiled source of SwapGear v1.2.1

BepInEx/plugins/SwapGear/SwapGear.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Kriona.Shared;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Kriona")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Kriona Hagen - MIT License")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1")]
[assembly: AssemblyProduct("Swap Gear")]
[assembly: AssemblyTitle("Swap Gear")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SwapGear
{
	[HarmonyPatch(typeof(Switch), "GetHoverText")]
	public static class SwitchHoverPatch
	{
		private static void Postfix(Switch __instance, ref string __result)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (!Swap.Running)
			{
				ArmorStand componentInParent = ((Component)__instance).GetComponentInParent<ArmorStand>();
				if (!((Object)(object)componentInParent == (Object)null) && Swap.CanSwap(componentInParent))
				{
					__result = __result + "\n[<color=yellow><b>" + ((object)Plugin.SwapKey.Value/*cast due to .constrained prefix*/).ToString() + "</b></color>] Swap gear (Esc to cancel)";
				}
			}
		}
	}
	[BepInPlugin("kriona.SwapGear", "Swap Gear", "1.2.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "kriona.SwapGear";

		public const string PluginName = "Swap Gear";

		public const string PluginVersion = "1.2.1";

		public static ConfigEntry<KeyboardShortcut> SwapKey;

		public static ConfigEntry<float> PickupTimeout;

		public static ConfigEntry<bool> InstantEquip;

		private Harmony harmony;

		private Coroutine swap;

		private ConfigWatcher watcher;

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			SwapKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "SwapKey", new KeyboardShortcut((KeyCode)121, Array.Empty<KeyCode>()), "Key to press while looking at an armor stand to swap your gear with what's on it - a Unity KeyCode name, optionally with modifiers, e.g. Y or LeftControl + Y");
			PickupTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PickupTimeout", 10f, "Seconds to wait for the armor stand's items to be picked up before giving up on equipping them");
			InstantEquip = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "InstantEquip", false, "Unequip and equip gear instantly instead of taking as long as it does from the inventory");
			watcher = new ConfigWatcher(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, "Swap Gear");
			harmony = new Harmony("kriona.SwapGear");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Swap Gear 1.2.1 loaded");
		}

		private void Update()
		{
			watcher.Update();
			if (Swap.Running || !IsSwapKeyDown())
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).TakeInput())
			{
				ArmorStand hoveredStand = Swap.GetHoveredStand(localPlayer);
				if ((Object)(object)hoveredStand != (Object)null)
				{
					swap = ((MonoBehaviour)this).StartCoroutine(Swap.Run(localPlayer, hoveredStand));
				}
			}
		}

		private static bool IsSwapKeyDown()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = SwapKey.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey, false))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, false))
				{
					return false;
				}
			}
			return true;
		}

		private void OnDestroy()
		{
			if (swap != null)
			{
				((MonoBehaviour)this).StopCoroutine(swap);
				Swap.Running = false;
			}
			watcher?.Dispose();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
	internal static class Swap
	{
		private class StandItem
		{
			public int Slot;

			public string PrefabName;

			public string Name;

			public ItemType Type;

			public ItemData Worn;

			public ItemData PickedUp;

			public bool Queued;

			public bool Done;
		}

		private const float OwnershipTimeout = 5f;

		private const float AttachTimeout = 2f;

		public static bool Running;

		public static ArmorStand GetHoveredStand(Player player)
		{
			GameObject hoverObject = ((Humanoid)player).GetHoverObject();
			if ((Object)(object)hoverObject == (Object)null)
			{
				return null;
			}
			ArmorStand componentInParent = hoverObject.GetComponentInParent<ArmorStand>();
			if (!((Object)(object)componentInParent != (Object)null) || !CanSwap(componentInParent))
			{
				return null;
			}
			return componentInParent;
		}

		public static bool CanSwap(ArmorStand stand)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (!stand.m_nview.IsValid() || !PrivateArea.CheckAccess(((Component)stand).transform.position, 0f, false, false))
			{
				return false;
			}
			for (int i = 0; i < stand.m_slots.Count; i++)
			{
				if (stand.HaveAttachment(i))
				{
					return true;
				}
			}
			return false;
		}

		public static IEnumerator Run(Player player, ArmorStand stand)
		{
			Running = true;
			try
			{
				List<StandItem> items = GetStandItems(stand);
				if (items.Count == 0)
				{
					yield break;
				}
				foreach (StandItem item2 in items)
				{
					ItemData worn = GetWornItem(player, item2.Type);
					if (worn != null && !items.Exists((StandItem other) => other.Worn == worn))
					{
						item2.Worn = worn;
					}
				}
				if (!stand.m_nview.IsOwner())
				{
					stand.m_nview.InvokeRPC("RPC_RequestOwn", Array.Empty<object>());
				}
				foreach (StandItem item3 in items)
				{
					if (item3.Worn != null)
					{
						StartUnequip(player, item3.Worn);
					}
				}
				while (items.Exists((StandItem standItem) => standItem.Worn != null && ((Humanoid)player).IsItemEquiped(standItem.Worn)))
				{
					if (IsCanceled(player) || !IsStandValid(stand))
					{
						CancelQueued(player, items);
						yield break;
					}
					if (items.Exists((StandItem standItem) => standItem.Worn != null && ((Humanoid)player).IsItemEquiped(standItem.Worn) && !player.IsEquipActionQueued(standItem.Worn)))
					{
						CancelQueued(player, items);
						((Character)player).Message((MessageType)2, "Swap gear interrupted", 0, (Sprite)null, false);
						yield break;
					}
					yield return null;
				}
				float deadline = Time.time + 5f;
				while (!stand.m_nview.IsOwner())
				{
					if (IsCanceled(player) || !IsStandValid(stand))
					{
						yield break;
					}
					if (Time.time > deadline)
					{
						((Character)player).Message((MessageType)2, "Swap gear failed - the armor stand is busy", 0, (Sprite)null, false);
						yield break;
					}
					yield return null;
				}
				HashSet<ItemData> before = new HashSet<ItemData>(((Humanoid)player).GetInventory().GetAllItems());
				foreach (StandItem item4 in items)
				{
					stand.DropItem(item4.Slot);
				}
				foreach (StandItem item in items)
				{
					if (item.Worn == null)
					{
						continue;
					}
					int slot = FindSlot(stand, item.Slot, item.Worn);
					if (slot < 0 || !stand.UseItem(stand.m_slots[slot].m_switch, (Humanoid)(object)player, item.Worn))
					{
						continue;
					}
					deadline = Time.time + 2f;
					while (!stand.HaveAttachment(slot) && ((Humanoid)player).GetInventory().ContainsItem(item.Worn) && Time.time < deadline)
					{
						if (!IsCanceled(player) && IsStandValid(stand))
						{
							yield return null;
							continue;
						}
						yield break;
					}
				}
				deadline = Time.time + Plugin.PickupTimeout.Value;
				while (!EquipPickedUp(player, items, before, Time.time > deadline))
				{
					if (IsCanceled(player))
					{
						CancelQueued(player, items);
						yield break;
					}
					yield return null;
				}
				List<string> list = new List<string>();
				foreach (StandItem item5 in items)
				{
					if (item5.PickedUp == null)
					{
						list.Add(item5.Name);
					}
				}
				string text = ((list.Count == 0) ? "Gear swapped" : ("Swap gear - not picked up: " + string.Join(", ", list)));
				((Character)player).Message((MessageType)2, Localization.instance.Localize(text), 0, (Sprite)null, false);
			}
			finally
			{
				Running = false;
			}
		}

		private static List<StandItem> GetStandItems(ArmorStand stand)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			List<StandItem> list = new List<StandItem>();
			for (int i = 0; i < stand.m_slots.Count; i++)
			{
				if (stand.HaveAttachment(i))
				{
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(stand.GetAttachedItem(i));
					ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
					if (!((Object)(object)val == (Object)null))
					{
						list.Add(new StandItem
						{
							Slot = i,
							PrefabName = ((Object)itemPrefab).name,
							Name = val.m_itemData.m_shared.m_name,
							Type = val.m_itemData.m_shared.m_itemType
						});
					}
				}
			}
			return list;
		}

		private static ItemData GetWornItem(Player player, ItemType type)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected I4, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Invalid comparison between Unknown and I4
			ItemData result = ((Humanoid)player).m_rightItem ?? ((Humanoid)player).m_hiddenRightItem;
			ItemData val = ((Humanoid)player).m_leftItem ?? ((Humanoid)player).m_hiddenLeftItem;
			switch (type - 3)
			{
			case 3:
				return ((Humanoid)player).m_helmetItem;
			case 4:
				return ((Humanoid)player).m_chestItem;
			case 8:
				return ((Humanoid)player).m_legItem;
			case 14:
				return ((Humanoid)player).m_shoulderItem;
			case 15:
				return ((Humanoid)player).m_utilityItem;
			case 21:
				return ((Humanoid)player).m_trinketItem;
			case 1:
			case 2:
			case 19:
				return val;
			case 12:
				if (val != null && (int)val.m_shared.m_itemType == 15)
				{
					return val;
				}
				return result;
			case 0:
			case 11:
			case 16:
				return result;
			default:
				return null;
			}
		}

		private static int FindSlot(ArmorStand stand, int preferred, ItemData item)
		{
			if (Fits(stand, preferred, item))
			{
				return preferred;
			}
			for (int i = 0; i < stand.m_slots.Count; i++)
			{
				if (Fits(stand, i, item))
				{
					return i;
				}
			}
			return -1;
		}

		private static bool Fits(ArmorStand stand, int slot, ItemData item)
		{
			if (!stand.HaveAttachment(slot))
			{
				return stand.CanAttach(stand.m_slots[slot], item);
			}
			return false;
		}

		private static bool IsInstant(ItemData item)
		{
			if (!Plugin.InstantEquip.Value)
			{
				return item.m_shared.m_equipDuration <= 0f;
			}
			return true;
		}

		private static void StartUnequip(Player player, ItemData item)
		{
			if (IsInstant(item) || !((Humanoid)player).IsItemEquiped(item))
			{
				((Humanoid)player).UnequipItem(item, true);
			}
			else if (!player.IsEquipActionQueued(item))
			{
				player.QueueUnequipAction(item);
			}
		}

		private static void CancelQueued(Player player, List<StandItem> items)
		{
			foreach (StandItem item in items)
			{
				((Humanoid)player).RemoveEquipAction(item.Worn);
				((Humanoid)player).RemoveEquipAction(item.PickedUp);
			}
		}

		private static bool EquipPickedUp(Player player, List<StandItem> items, HashSet<ItemData> before, bool timedOut)
		{
			bool result = true;
			foreach (StandItem item in items)
			{
				if (item.Done)
				{
					continue;
				}
				if (item.PickedUp == null)
				{
					item.PickedUp = FindPickedUp(player, items, before, item.PrefabName);
				}
				if (item.PickedUp == null)
				{
					if (timedOut)
					{
						item.Done = true;
					}
					else
					{
						result = false;
					}
				}
				else if (item.Queued)
				{
					if (((Humanoid)player).IsItemEquiped(item.PickedUp) || !player.IsEquipActionQueued(item.PickedUp))
					{
						item.Done = true;
					}
					else
					{
						result = false;
					}
				}
				else if (!IsInstant(item.PickedUp) && !((Humanoid)player).IsItemEquiped(item.PickedUp))
				{
					player.QueueEquipAction(item.PickedUp);
					item.Queued = true;
					result = false;
				}
				else if (((Humanoid)player).IsItemEquiped(item.PickedUp) || ((Humanoid)player).EquipItem(item.PickedUp, true))
				{
					item.Done = true;
				}
				else if (((Character)player).InAttack() || ((Character)player).InDodge())
				{
					result = false;
				}
				else
				{
					item.Done = true;
				}
			}
			return result;
		}

		private static ItemData FindPickedUp(Player player, List<StandItem> items, HashSet<ItemData> before, string prefabName)
		{
			foreach (ItemData item in ((Humanoid)player).GetInventory().GetAllItems())
			{
				if (!before.Contains(item) && !((Object)(object)item.m_dropPrefab == (Object)null) && !(((Object)item.m_dropPrefab).name != prefabName) && !items.Exists((StandItem other) => other.PickedUp == item))
				{
					return item;
				}
			}
			return null;
		}

		private static bool IsCanceled(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)player || ((Character)player).IsDead())
			{
				return true;
			}
			if (ZInput.GetKeyDown((KeyCode)27, false))
			{
				((Character)player).Message((MessageType)2, "Swap gear canceled", 0, (Sprite)null, false);
				return true;
			}
			return false;
		}

		private static bool IsStandValid(ArmorStand stand)
		{
			if ((Object)(object)stand != (Object)null)
			{
				return stand.m_nview.IsValid();
			}
			return false;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace Kriona.Shared
{
	internal class ConfigMirror
	{
		private readonly ConfigFile config;

		private readonly Action changed;

		private bool syncing;

		public ConfigMirror(ConfigFile config, Action changed)
		{
			this.config = config;
			this.changed = changed;
			config.SaveOnConfigSet = false;
			config.SettingChanged += OnSettingChanged;
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, AcceptableValueBase acceptableValues, int? order, string displayName)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes();
			configurationManagerAttributes.Order = order;
			configurationManagerAttributes.DispName = displayName;
			return config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, acceptableValues, new object[1] { configurationManagerAttributes }));
		}

		public void Remove(ConfigEntryBase entry)
		{
			config.Remove(entry.Definition);
		}

		public void Sync(Action apply)
		{
			syncing = true;
			try
			{
				apply();
			}
			finally
			{
				syncing = false;
			}
		}

		public static void SetDisplayName(ConfigEntryBase entry, string displayName)
		{
			object[] tags = entry.Description.Tags;
			for (int i = 0; i < tags.Length; i++)
			{
				if (tags[i] is ConfigurationManagerAttributes configurationManagerAttributes)
				{
					configurationManagerAttributes.DispName = displayName;
				}
			}
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			if (!syncing)
			{
				changed();
			}
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public string DispName;
	}
	internal class ConfigWatcher : IDisposable
	{
		private const float ReloadDelay = 0.5f;

		private readonly string path;

		private readonly Action reload;

		private readonly ManualLogSource log;

		private readonly string pluginName;

		private readonly FileSystemWatcher watcher;

		private string knownContents;

		private volatile bool fileChanged;

		private float reloadTime = -1f;

		public ConfigWatcher(string path, Action reload, ManualLogSource log, string pluginName)
		{
			this.path = path;
			this.reload = reload;
			this.log = log;
			this.pluginName = pluginName;
			Remember();
			try
			{
				watcher = new FileSystemWatcher(Path.GetDirectoryName(path), Path.GetFileName(path));
				watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
				watcher.Changed += delegate
				{
					fileChanged = true;
				};
				watcher.Created += delegate
				{
					fileChanged = true;
				};
				watcher.Renamed += delegate
				{
					fileChanged = true;
				};
				watcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				watcher = null;
				log.LogWarning((object)("Couldn't watch " + path + " for changes, so changes made to it while Valheim is running aren't read: " + ex.Message));
			}
		}

		public ConfigWatcher(ConfigFile config, ManualLogSource log, string pluginName)
			: this(config.ConfigFilePath, delegate
			{
				ReloadConfig(config);
			}, log, pluginName)
		{
		}

		private static void ReloadConfig(ConfigFile config)
		{
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				config.Reload();
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
			if (saveOnConfigSet && !MatchesFile(config))
			{
				config.Save();
			}
		}

		private static bool MatchesFile(ConfigFile config)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			Dictionary<ConfigDefinition, string> dictionary = new Dictionary<ConfigDefinition, string>();
			string text = string.Empty;
			string[] array = File.ReadAllLines(config.ConfigFilePath);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.StartsWith("#"))
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]"))
				{
					text = text2.Substring(1, text2.Length - 2);
					continue;
				}
				string[] array2 = text2.Split(new char[1] { '=' }, 2);
				if (array2.Length == 2)
				{
					dictionary[new ConfigDefinition(text, array2[0].Trim())] = array2[1].Trim();
				}
			}
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				if (!dictionary.TryGetValue(item.Key, out var value))
				{
					return false;
				}
				try
				{
					if (!object.Equals(TomlTypeConverter.ConvertToValue(value, item.Value.SettingType), item.Value.BoxedValue))
					{
						return false;
					}
				}
				catch (Exception)
				{
					return false;
				}
			}
			return true;
		}

		public void Update()
		{
			if (fileChanged)
			{
				fileChanged = false;
				reloadTime = Time.realtimeSinceStartup + 0.5f;
			}
			if (reloadTime < 0f || Time.realtimeSinceStartup < reloadTime)
			{
				return;
			}
			reloadTime = -1f;
			string text;
			try
			{
				text = File.ReadAllText(path);
			}
			catch (FileNotFoundException)
			{
				return;
			}
			catch (IOException)
			{
				reloadTime = Time.realtimeSinceStartup + 0.5f;
				return;
			}
			if (text == knownContents)
			{
				return;
			}
			reload();
			Remember();
			if (pluginName != null)
			{
				log.LogInfo((object)("Reloaded " + Path.GetFileName(path)));
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, pluginName + ": config reloaded", 0, (Sprite)null, false, true);
				}
			}
		}

		public void Remember()
		{
			try
			{
				knownContents = File.ReadAllText(path);
			}
			catch (IOException)
			{
				knownContents = null;
			}
		}

		public void Dispose()
		{
			watcher?.Dispose();
		}
	}
	internal class StateEntries
	{
		public const string Inherit = "inherit";

		private static readonly AcceptableValueList<string> values = new AcceptableValueList<string>(new string[3] { "inherit", "true", "false" });

		private readonly ConfigMirror mirror;

		private readonly string help;

		private readonly Dictionary<string, ConfigEntry<string>> entries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase);

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

		public StateEntries(ConfigMirror mirror, string help)
		{
			this.mirror = mirror;
			this.help = help;
		}

		public void Set(string section, string key, string displayName, bool? state)
		{
			string entryName = GetEntryName(key, displayName);
			if (entries.TryGetValue(key, out var value) && ((ConfigEntryBase)value).Definition.Section != section)
			{
				mirror.Remove((ConfigEntryBase)(object)value);
				entries.Remove(key);
				value = null;
			}
			if (value == null)
			{
				try
				{
					value = mirror.Bind(section, key, "inherit", "Prefab " + key + " - " + help, (AcceptableValueBase)(object)values, null, entryName);
				}
				catch (ArgumentException)
				{
					return;
				}
				entries[key] = value;
			}
			else
			{
				ConfigMirror.SetDisplayName((ConfigEntryBase)(object)value, entryName);
			}
			value.Value = Format(state);
			set.Add(key);
		}

		public void RemoveUnset()
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, ConfigEntry<string>> entry in entries)
			{
				if (!set.Contains(entry.Key))
				{
					mirror.Remove((ConfigEntryBase)(object)entry.Value);
					list.Add(entry.Key);
				}
			}
			foreach (string item in list)
			{
				entries.Remove(item);
			}
			set.Clear();
		}

		public bool TryGetState(string key, out bool? state)
		{
			state = null;
			if (!entries.TryGetValue(key, out var value))
			{
				return false;
			}
			if (bool.TryParse(value.Value, out var result))
			{
				state = result;
			}
			return true;
		}

		public static string Format(bool? state)
		{
			if (!state.HasValue)
			{
				return "inherit";
			}
			if (!state.Value)
			{
				return "false";
			}
			return "true";
		}

		private static string GetEntryName(string key, string displayName)
		{
			string text = displayName.Trim();
			if (text.Length == 0 || text.Equals(key, StringComparison.OrdinalIgnoreCase))
			{
				return key;
			}
			return text + " (" + key + ")";
		}
	}
}