Decompiled source of ChestPeek v1.1.0

BepInEx/plugins/ChestPeek/ChestPeek.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using 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.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("Chest Peek")]
[assembly: AssemblyTitle("Chest Peek")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ChestPeek
{
	internal static class ChestColors
	{
		private enum Fill
		{
			Partial,
			Empty,
			Full
		}

		private static readonly HashSet<string> Chests = new HashSet<string> { "piece_chest_wood", "piece_chest", "piece_chest_private", "piece_chest_barrel", "piece_chest_blackmetal", "piece_chest_grausten", "piece_chest_warderobe" };

		private static readonly Dictionary<string, Vector2[][]> Regions = new Dictionary<string, Vector2[][]> { 
		{
			"woodchest",
			new Vector2[2][]
			{
				(Vector2[])(object)new Vector2[4]
				{
					new Vector2(0.46f, 0.77f),
					new Vector2(0.76f, 0.77f),
					new Vector2(0.76f, 1f),
					new Vector2(0.46f, 1f)
				},
				(Vector2[])(object)new Vector2[9]
				{
					new Vector2(0.0095f, 0.6895f),
					new Vector2(0.0116f, 0.6054f),
					new Vector2(0.012f, 0.5919f),
					new Vector2(0.0138f, 0.5888f),
					new Vector2(0.0491f, 0.599f),
					new Vector2(0.0692f, 0.6237f),
					new Vector2(0.0689f, 0.6613f),
					new Vector2(0.045f, 0.6858f),
					new Vector2(0.0113f, 0.6931f)
				}
			}
		} };

		private const int RegionScale = 4;

		private const float MaskStart = 0.25f;

		private const float MaskFull = 0.5f;

		private const float TintBrightness = 0.75f;

		private static readonly int MainTexId = Shader.PropertyToID("_MainTex");

		private static readonly int MetallicTexId = Shader.PropertyToID("_MetallicTex");

		private static readonly int MetalColorId = Shader.PropertyToID("_MetalColor");

		private static readonly Dictionary<Material, Material> emptyMaterials = new Dictionary<Material, Material>();

		private static readonly Dictionary<Material, Material> fullMaterials = new Dictionary<Material, Material>();

		private static readonly Dictionary<Material, Material> originals = new Dictionary<Material, Material>();

		private static Color? emptyColor;

		private static Color? fullColor;

		private static readonly HashSet<Container> changed = new HashSet<Container>();

		public static void Track(Container container)
		{
			if (container.m_inventory != null && IsChest(container))
			{
				Inventory inventory = container.m_inventory;
				inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, (Action)delegate
				{
					changed.Add(container);
				});
			}
		}

		public static void Loaded(Container container)
		{
			if (container.m_inventory != null && IsChest(container))
			{
				changed.Add(container);
			}
		}

		public static void Update()
		{
			if (changed.Count == 0)
			{
				return;
			}
			foreach (Container item in changed)
			{
				Apply(item);
			}
			changed.Clear();
		}

		public static void Refresh()
		{
			emptyColor = ParseColor(Plugin.EmptyColor.Value, ((ConfigEntryBase)Plugin.EmptyColor).Definition.Key);
			fullColor = ParseColor(Plugin.FullColor.Value, ((ConfigEntryBase)Plugin.FullColor).Definition.Key);
			List<Material> list = new List<Material>(originals.Keys);
			emptyMaterials.Clear();
			fullMaterials.Clear();
			Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
			foreach (Container val in array)
			{
				if (val.m_inventory != null && IsChest(val))
				{
					Apply(val);
				}
			}
			foreach (Material item in list)
			{
				originals.Remove(item);
				Object.Destroy((Object)(object)item.mainTexture);
				Object.Destroy((Object)(object)item);
			}
		}

		private static bool IsChest(Container container)
		{
			if ((Object)(object)container.m_rootObjectOverride == (Object)null)
			{
				return Chests.Contains(Utils.GetPrefabName(((Component)container).gameObject));
			}
			return false;
		}

		private static void Apply(Container container)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)container == (Object)null)
			{
				return;
			}
			Fill fill = GetFill(container.m_inventory);
			Color? val = fill switch
			{
				Fill.Full => fullColor, 
				Fill.Empty => emptyColor, 
				_ => null, 
			};
			Renderer[] componentsInChildren = ((Component)container).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val2 in componentsInChildren)
			{
				Material[] sharedMaterials = val2.sharedMaterials;
				bool flag = false;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					if (!((Object)(object)sharedMaterials[j] == (Object)null))
					{
						Material value;
						Material val3 = (originals.TryGetValue(sharedMaterials[j], out value) ? value : sharedMaterials[j]);
						Material val4 = val3;
						if (val.HasValue)
						{
							val4 = GetTinted(val3, (fill == Fill.Empty) ? emptyMaterials : fullMaterials, val.Value) ?? val3;
						}
						if ((Object)(object)val4 != (Object)(object)sharedMaterials[j])
						{
							sharedMaterials[j] = val4;
							flag = true;
						}
					}
				}
				if (flag)
				{
					val2.sharedMaterials = sharedMaterials;
				}
			}
		}

		private static Fill GetFill(Inventory inventory)
		{
			if (inventory.NrOfItems() == 0)
			{
				return Fill.Empty;
			}
			if (inventory.GetEmptySlots() > 0)
			{
				return Fill.Partial;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_stack < allItem.m_shared.m_maxStackSize)
				{
					return Fill.Partial;
				}
			}
			return Fill.Full;
		}

		private static Color? ParseColor(string text, string setting)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			text = (text ?? "").Trim();
			if (text.Length == 0)
			{
				return null;
			}
			Color value = default(Color);
			if (!text.StartsWith("#") && text.Length == 6 && ColorUtility.TryParseHtmlString("#" + text, ref value))
			{
				return value;
			}
			Color value2 = default(Color);
			if (ColorUtility.TryParseHtmlString(text, ref value2))
			{
				return value2;
			}
			Plugin.Log.LogWarning((object)(setting + " \"" + text + "\" isn't a color - use a hex color like #FF4040 or a name like red, or leave it blank"));
			return null;
		}

		private static Material GetTinted(Material original, Dictionary<Material, Material> cache, Color color)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (cache.TryGetValue(original, out var value))
			{
				return value;
			}
			value = (cache[original] = MakeTinted(original, color));
			if ((Object)(object)value != (Object)null)
			{
				originals[value] = original;
			}
			return value;
		}

		private static Material MakeTinted(Material original, Color color)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Expected O, but got Unknown
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			if (original.HasProperty(MainTexId))
			{
				Texture texture = original.GetTexture(MainTexId);
				Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
				if (val != null)
				{
					int num = ((!Regions.ContainsKey(((Object)original).name)) ? 1 : 4);
					int num2 = ((Texture)val).width * num;
					int num3 = ((Texture)val).height * num;
					float[] weights = GetWeights(original, num2, num3);
					if (weights == null)
					{
						return null;
					}
					Color32[] array = ReadPixels((Texture)(object)val, num2, num3, linear: false);
					float num4 = 0f;
					float num5 = 0f;
					for (int i = 0; i < array.Length; i++)
					{
						num4 += weights[i];
						num5 += Luminance(array[i]) * weights[i];
					}
					if (num4 < 1f)
					{
						return null;
					}
					float num6 = Mathf.Max(num5 / num4, 0.01f);
					for (int j = 0; j < array.Length; j++)
					{
						if (!(weights[j] <= 0f))
						{
							float num7 = Mathf.Min(1f, Luminance(array[j]) / num6 * 0.75f);
							Color val2 = color * num7;
							val2.a = (float)(int)array[j].a / 255f;
							array[j] = Color32.op_Implicit(Color.Lerp(Color32.op_Implicit(array[j]), val2, weights[j]));
						}
					}
					Texture2D val3 = new Texture2D(num2, num3, (TextureFormat)4, true, false);
					((Object)val3).name = ((Object)val).name + " (Chest Peek)";
					((Texture)val3).filterMode = ((Texture)val).filterMode;
					((Texture)val3).wrapMode = ((Texture)val).wrapMode;
					((Texture)val3).anisoLevel = ((Texture)val).anisoLevel;
					val3.SetPixels32(array);
					val3.Apply(true, true);
					Material val4 = new Material(original);
					((Object)val4).name = ((Object)original).name + " (Chest Peek)";
					val4.SetTexture(MainTexId, (Texture)(object)val3);
					if (val4.HasProperty(MetalColorId))
					{
						val4.SetColor(MetalColorId, Color.white);
					}
					return val4;
				}
			}
			return null;
		}

		private static float[] GetWeights(Material material, int width, int height)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			float[] array = new float[width * height];
			if (Regions.TryGetValue(((Object)material).name, out var value))
			{
				Vector2 point = default(Vector2);
				for (int i = 0; i < height; i++)
				{
					for (int j = 0; j < width; j++)
					{
						((Vector2)(ref point))..ctor(((float)j + 0.5f) / (float)width, ((float)i + 0.5f) / (float)height);
						Vector2[][] array2 = value;
						for (int k = 0; k < array2.Length; k++)
						{
							if (Contains(array2[k], point))
							{
								array[i * width + j] = 1f;
								break;
							}
						}
					}
				}
				return array;
			}
			if (!material.HasProperty(MetallicTexId))
			{
				return null;
			}
			Texture texture = material.GetTexture(MetallicTexId);
			if ((Object)(object)texture == (Object)null)
			{
				return null;
			}
			Color32[] array3 = ReadPixels(texture, width, height, linear: true);
			for (int l = 0; l < array3.Length; l++)
			{
				array[l] = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0.25f, 0.5f, (float)(int)array3[l].r / 255f));
			}
			return array;
		}

		private static bool Contains(Vector2[] polygon, Vector2 point)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			int num = 0;
			int num2 = polygon.Length - 1;
			while (num < polygon.Length)
			{
				Vector2 val = polygon[num];
				Vector2 val2 = polygon[num2];
				if (val.y > point.y != val2.y > point.y && point.x < (val2.x - val.x) * (point.y - val.y) / (val2.y - val.y) + val.x)
				{
					flag = !flag;
				}
				num2 = num++;
			}
			return flag;
		}

		private static Color32[] ReadPixels(Texture source, int width, int height, bool linear)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)(linear ? 1 : 2));
			RenderTexture active = RenderTexture.active;
			Graphics.Blit(source, temporary);
			RenderTexture.active = temporary;
			Texture2D val = new Texture2D(width, height, (TextureFormat)4, false, linear);
			val.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0);
			RenderTexture.active = active;
			RenderTexture.ReleaseTemporary(temporary);
			Color32[] pixels = val.GetPixels32();
			Object.Destroy((Object)val);
			return pixels;
		}

		private static float Luminance(Color32 pixel)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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)
			return (0.299f * (float)(int)pixel.r + 0.587f * (float)(int)pixel.g + 0.114f * (float)(int)pixel.b) / 255f;
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	public static class ContainerAwakePatch
	{
		private static void Postfix(Container __instance)
		{
			ChestColors.Track(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "Load")]
	public static class ContainerLoadPatch
	{
		private static void Postfix(Container __instance, bool __result)
		{
			if (__result)
			{
				ChestColors.Loaded(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Container), "GetHoverText")]
	public static class ContainerContentsPatch
	{
		private const int MaxLines = 12;

		private static void Postfix(Container __instance, ref string __result)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (__instance.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, false, false))
			{
				return;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (ItemData allItem in __instance.m_inventory.GetAllItems())
			{
				string key = Localization.instance.Localize(allItem.m_shared.m_name);
				dictionary[key] = (dictionary.TryGetValue(key, out var value) ? value : 0) + allItem.m_stack;
			}
			if (dictionary.Count != 0)
			{
				List<KeyValuePair<string, int>> list = (from pair in dictionary
					orderby pair.Value descending, pair.Key
					select pair).ToList();
				int num = ((list.Count > 12) ? 11 : list.Count);
				StringBuilder stringBuilder = new StringBuilder("\n");
				for (int num2 = 0; num2 < num; num2++)
				{
					stringBuilder.Append("\n" + list[num2].Key + " <color=orange>x" + list[num2].Value + "</color>");
				}
				if (num < list.Count)
				{
					stringBuilder.Append("\n<color=#a0a0a0>+" + (list.Count - num) + " more</color>");
				}
				__result += stringBuilder.ToString();
			}
		}
	}
	[BepInPlugin("kriona.ChestPeek", "Chest Peek", "1.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "kriona.ChestPeek";

		public const string PluginName = "Chest Peek";

		public const string PluginVersion = "1.1.0";

		public static ConfigEntry<string> EmptyColor;

		public static ConfigEntry<string> FullColor;

		internal static ManualLogSource Log;

		private Harmony harmony;

		private ConfigWatcher watcher;

		private void Awake()
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			EmptyColor = ((BaseUnityPlugin)this).Config.Bind<string>("Chest Colors", "EmptyColor", "", "Color of a chest's metal fittings, or the wood chest's rope rings and front circle, when it holds nothing - a hex color like #00FF00 or a name like green, or blank to leave them unchanged");
			FullColor = ((BaseUnityPlugin)this).Config.Bind<string>("Chest Colors", "FullColor", "#FFFF00", "Color of a chest's metal fittings, or the wood chest's rope rings and front circle, when every slot holds a full stack - a hex color like #FFFF00 or a name like yellow, or blank to leave them unchanged");
			ChestColors.Refresh();
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			watcher = new ConfigWatcher(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, "Chest Peek");
			harmony = new Harmony("kriona.ChestPeek");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Chest Peek 1.1.0 loaded");
		}

		private void Update()
		{
			watcher.Update();
			ChestColors.Update();
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			ChestColors.Refresh();
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged;
			watcher?.Dispose();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
}
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 + ")";
		}
	}
}