Decompiled source of Automatics v1.6.0

plugins/Automatics.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Automatics.AutomaticDoor;
using Automatics.AutomaticMapping;
using Automatics.AutomaticProcessing;
using Automatics.ConsoleCommands;
using Automatics.Valheim;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using LitJson;
using ModUtils;
using NDesk.Options;
using Splatform;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Automatics")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Automatics")]
[assembly: AssemblyCopyright("Copyright (c) 2022-2026 EideeHi")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("1821CB9A-56A9-4013-B268-86F0704773CF")]
[assembly: AssemblyFileVersion("1.6.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
[assembly: AssemblyVersion("1.6.0.0")]
namespace ModUtils
{
	public class Configuration
	{
		private sealed class LocalizedConfigEntry
		{
			public ConfigEntryBase Entry { get; set; }

			public L10N Localization { get; set; }

			public string Section { get; set; }

			public string Key { get; set; }

			public ConfigurationManagerAttributes Attributes { get; set; }

			public bool CategoryManaged { get; set; }

			public bool DispNameManaged { get; set; }

			public bool DescriptionManaged { get; set; }
		}

		private const int DefaultOrder = 4096;

		private static readonly object LocalizedEntriesLock;

		private static readonly Dictionary<ConfigEntryBase, LocalizedConfigEntry> LocalizedEntries;

		private static readonly string[] DescriptionFieldNames;

		private readonly ConfigFile _config;

		private readonly L10N _localization;

		private Logger _logger;

		private string Section { get; set; } = "general";


		private int Order { get; set; } = 4096;


		static Configuration()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			LocalizedEntriesLock = new object();
			DescriptionFieldNames = new string[4] { "<Description>k__BackingField", "Description", "description", "_description" };
			LocalizedEntries = new Dictionary<ConfigEntryBase, LocalizedConfigEntry>();
			if (!TomlTypeConverter.CanConvert(typeof(StringList)))
			{
				TomlTypeConverter.AddConverter(typeof(StringList), new TypeConverter
				{
					ConvertToObject = (string str, Type type) => (!string.IsNullOrEmpty(str)) ? new StringList(Csv.ParseLine(str, trimUnquotedFields: true)) : new StringList(),
					ConvertToString = delegate(object obj, Type type)
					{
						StringList source = (StringList)obj;
						return string.Join(", ", source.Select(Csv.Escape));
					}
				});
			}
			if (!TomlTypeConverter.CanConvert(typeof(KeyboardShortcut)))
			{
				TomlTypeConverter.AddConverter(typeof(KeyboardShortcut), new TypeConverter
				{
					ConvertToObject = (string str, Type type) => KeyboardShortcut.Deserialize(str),
					ConvertToString = delegate(object obj, Type type)
					{
						//IL_0001: Unknown result type (might be due to invalid IL or missing references)
						//IL_0006: Unknown result type (might be due to invalid IL or missing references)
						KeyboardShortcut val = (KeyboardShortcut)obj;
						return ((KeyboardShortcut)(ref val)).Serialize();
					}
				});
			}
		}

		public Configuration(ConfigFile config, L10N localization)
		{
			_config = config;
			_localization = localization;
		}

		internal static void RefreshAllLocalizedMetadata()
		{
			LocalizedConfigEntry[] array;
			lock (LocalizedEntriesLock)
			{
				array = LocalizedEntries.Values.ToArray();
			}
			LocalizedConfigEntry[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				RefreshLocalizedMetadata(array2[i]);
			}
		}

		private static void RegisterLocalizedEntry(ConfigEntryBase entry, L10N localization, string section, string key, ConfigurationManagerAttributes attributes, bool categoryManaged, bool dispNameManaged, bool descriptionManaged)
		{
			LocalizedConfigEntry localizedConfigEntry = new LocalizedConfigEntry
			{
				Entry = entry,
				Localization = localization,
				Section = section,
				Key = key,
				Attributes = attributes,
				CategoryManaged = categoryManaged,
				DispNameManaged = dispNameManaged,
				DescriptionManaged = descriptionManaged
			};
			lock (LocalizedEntriesLock)
			{
				LocalizedEntries[entry] = localizedConfigEntry;
			}
			RefreshLocalizedMetadata(localizedConfigEntry);
		}

		private static void RefreshLocalizedMetadata(LocalizedConfigEntry entry)
		{
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Expected O, but got Unknown
			if (entry?.Entry == null || entry.Localization == null)
			{
				return;
			}
			try
			{
				ConfigurationManagerAttributes attributes = entry.Attributes;
				if (attributes != null)
				{
					if (entry.CategoryManaged)
					{
						attributes.Category = entry.Localization.Translate("@config_" + entry.Section + "_section");
					}
					if (entry.DispNameManaged)
					{
						attributes.DispName = entry.Localization.Translate("@config_" + entry.Section + "_" + entry.Key + "_name");
					}
					string text = (attributes.Description = (entry.DescriptionManaged ? entry.Localization.Translate("@config_" + entry.Section + "_" + entry.Key + "_description") : attributes.Description));
					ConfigDescription description = entry.Entry.Description;
					AcceptableValueBase val = ((description != null) ? description.AcceptableValues : null);
					object[] array = ReplaceConfigurationManagerAttributes((description != null) ? description.Tags : null, attributes);
					ConfigDescription description2 = new ConfigDescription(text, val, array);
					if (!TrySetEntryDescription(entry.Entry, description2))
					{
						Debug.LogError((object)("[ModUtils] Failed to replace ConfigDescription for [" + entry.Section + ":" + entry.Key + "]."));
					}
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[ModUtils] Failed to refresh localized config metadata for [{entry.Section}:{entry.Key}]: {arg}");
			}
		}

		private static object[] ReplaceConfigurationManagerAttributes(object[] tags, ConfigurationManagerAttributes attributes)
		{
			if (tags == null || tags.Length == 0)
			{
				return new object[1] { attributes };
			}
			bool flag = false;
			List<object> list = new List<object>(tags.Length);
			foreach (object obj in tags)
			{
				if (obj is ConfigurationManagerAttributes)
				{
					if (!flag)
					{
						list.Add(attributes);
						flag = true;
					}
				}
				else
				{
					list.Add(obj);
				}
			}
			if (!flag)
			{
				list.Add(attributes);
			}
			return list.ToArray();
		}

		private static bool TrySetEntryDescription(ConfigEntryBase entry, ConfigDescription description)
		{
			MethodInfo methodInfo = AccessTools.PropertySetter(((object)entry).GetType(), "Description") ?? AccessTools.PropertySetter(typeof(ConfigEntryBase), "Description");
			if (methodInfo != null)
			{
				methodInfo.Invoke(entry, new object[1] { description });
				return true;
			}
			string[] descriptionFieldNames = DescriptionFieldNames;
			foreach (string text in descriptionFieldNames)
			{
				FieldInfo fieldInfo = AccessTools.Field(((object)entry).GetType(), text) ?? AccessTools.Field(typeof(ConfigEntryBase), text);
				if (!(fieldInfo == null) && typeof(ConfigDescription).IsAssignableFrom(fieldInfo.FieldType))
				{
					fieldInfo.SetValue(entry, description);
					return true;
				}
			}
			return false;
		}

		private void LogSection(string section)
		{
			_logger?.Debug("[CONFIG] === " + GetSection(section) + " / [" + section + "]");
		}

		private void LogConfigEntry<T>(ConfigEntry<T> entry, ConfigurationManagerAttributes attributes)
		{
			_logger?.Debug("[CONFIG] ==== " + attributes.DispName + " / [" + ((ConfigEntryBase)entry).Definition.Key + "]");
			_logger?.Debug("[CONFIG] " + ((ConfigEntryBase)entry).Description.Description);
			_logger?.Debug("[CONFIG] ");
			Type type = typeof(T);
			object defaultValue = ((ConfigEntryBase)entry).DefaultValue;
			if (attributes.ObjToStr != null)
			{
				_logger?.Debug("[CONFIG] - Default value: " + attributes.ObjToStr(defaultValue));
			}
			else if (TomlTypeConverter.CanConvert(type))
			{
				_logger?.Debug("[CONFIG] - Default value: " + TomlTypeConverter.ConvertToString(defaultValue, type));
			}
			else
			{
				_logger?.Debug($"[CONFIG] - Default value: {defaultValue}");
			}
			AcceptableValueBase acceptableValues = ((ConfigEntryBase)entry).Description.AcceptableValues;
			if (acceptableValues != null)
			{
				string[] array = acceptableValues.ToDescriptionString().Split('\n');
				foreach (string text in array)
				{
					_logger?.Debug("[CONFIG] - " + text);
				}
			}
			else if (type.IsEnum)
			{
				List<string> list = (from x in Enum.GetValues(type).OfType<T>()
					select Enum.GetName(type, x)).ToList();
				_logger?.Debug("[CONFIG] - Acceptable values: " + string.Join(", ", list));
				if (type.GetCustomAttributes(typeof(FlagsAttribute), inherit: false).Any())
				{
					IEnumerable<string> values = list.Where((string x) => !string.Equals(x, "none", StringComparison.OrdinalIgnoreCase) && !string.Equals(x, "all", StringComparison.OrdinalIgnoreCase)).Take(2);
					_logger?.Debug("[CONFIG] - Multiple values can be set at the same time by separating them with , (e.g. " + string.Join(", ", values) + ")");
				}
			}
			_logger?.Debug("[CONFIG] ");
		}

		public void SetDebugLogger(Logger logger)
		{
			_logger = logger;
		}

		public void ChangeSection(string section, int initialOrder = 4096)
		{
			Section = section;
			Order = initialOrder;
			if (_logger != null)
			{
				LogSection(Section);
			}
		}

		private ConfigEntry<T> Bind<T>(string section, int order, string key, T defaultValue, AcceptableValueBase acceptableValue = null, Action<ConfigurationManagerAttributes> initializer = null)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			L10N.EnsurePatched();
			string section2 = GetSection(section);
			string name = GetName(section, key);
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
			{
				Category = section2,
				Order = order,
				DispName = name,
				CustomDrawer = ConfigurationCustomDrawer.Get(typeof(T), acceptableValue)
			};
			initializer?.Invoke(configurationManagerAttributes);
			bool categoryManaged = string.Equals(configurationManagerAttributes.Category, section2, StringComparison.Ordinal);
			bool dispNameManaged = string.Equals(configurationManagerAttributes.DispName, name, StringComparison.Ordinal);
			bool flag = string.IsNullOrEmpty(configurationManagerAttributes.Description);
			string text = (configurationManagerAttributes.Description = (flag ? GetDescription(section, key) : configurationManagerAttributes.Description));
			ConfigEntry<T> val = _config.Bind<T>(section, key, defaultValue, new ConfigDescription(text, acceptableValue, new object[1] { configurationManagerAttributes }));
			RegisterLocalizedEntry((ConfigEntryBase)(object)val, _localization, section, key, configurationManagerAttributes, categoryManaged, dispNameManaged, flag);
			if (_logger != null)
			{
				LogConfigEntry<T>(val, configurationManagerAttributes);
			}
			return val;
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, AcceptableValueBase acceptableValue = null, Action<ConfigurationManagerAttributes> initializer = null)
		{
			return Bind(section, Order--, key, defaultValue, acceptableValue, initializer);
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, (T, T) acceptableValue, Action<ConfigurationManagerAttributes> initializer = null) where T : IComparable
		{
			var (val, val2) = acceptableValue;
			return Bind(section, key, defaultValue, (AcceptableValueBase)(object)new AcceptableValueRange<T>(val, val2), initializer);
		}

		public ConfigEntry<T> Bind<T>(string key, T defaultValue, AcceptableValueBase acceptableValue = null, Action<ConfigurationManagerAttributes> initializer = null)
		{
			return Bind(Section, key, defaultValue, acceptableValue, initializer);
		}

		public ConfigEntry<T> Bind<T>(string key, T defaultValue, (T, T) acceptableValue, Action<ConfigurationManagerAttributes> initializer = null) where T : IComparable
		{
			return Bind(Section, key, defaultValue, acceptableValue, initializer);
		}

		private string GetSection(string section)
		{
			return _localization.Translate("@config_" + section + "_section");
		}

		private string GetName(string section, string key)
		{
			return _localization.Translate("@config_" + section + "_" + key + "_name");
		}

		private string GetDescription(string section, string key)
		{
			return _localization.Translate("@config_" + section + "_" + key + "_description");
		}
	}
	public class StringList : ICollection<string>, IEnumerable<string>, IEnumerable
	{
		private readonly HashSet<string> _values;

		public int Count => _values.Count;

		public bool IsReadOnly => false;

		public StringList()
		{
			_values = new HashSet<string>();
		}

		public StringList(IEnumerable<string> collection)
		{
			_values = new HashSet<string>(collection);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public IEnumerator<string> GetEnumerator()
		{
			return _values.GetEnumerator();
		}

		public void Add(string item)
		{
			_values.Add(item);
		}

		public void Clear()
		{
			_values.Clear();
		}

		public bool Contains(string item)
		{
			return _values.Contains(item);
		}

		public void CopyTo(string[] array, int arrayIndex)
		{
			_values.CopyTo(array, arrayIndex);
		}

		public bool Remove(string item)
		{
			return _values.Remove(item);
		}

		public bool TryAdd(string item)
		{
			return _values.Add(item);
		}
	}
	public class LocalizedDescriptionAttribute : DescriptionAttribute
	{
		private readonly string _prefix;

		private readonly string _key;

		public override string Description => L10N.Translate(_prefix, _key);

		public LocalizedDescriptionAttribute(string prefix, string key)
			: base(key)
		{
			_prefix = prefix;
			_key = key;
		}

		public LocalizedDescriptionAttribute(string key)
			: this("", key)
		{
		}
	}
	public class AcceptableValueEnum<T> : AcceptableValueBase where T : Enum
	{
		private readonly bool _isFlags;

		private readonly IList<T> _values;

		public AcceptableValueEnum(params T[] values)
			: base(typeof(T))
		{
			_isFlags = ((AcceptableValueBase)this).ValueType.GetCustomAttributes(typeof(FlagsAttribute), inherit: false).Any();
			_values = MakeValues(((AcceptableValueBase)this).ValueType, (IReadOnlyCollection<T>)(object)values, _isFlags);
		}

		private static IList<T> MakeValues(Type type, IReadOnlyCollection<T> values, bool isFlags)
		{
			object collection;
			if (values.Count != 0)
			{
				collection = values;
			}
			else
			{
				collection = Enum.GetValues(type).OfType<T>();
			}
			List<T> list = new List<T>((IEnumerable<T>)collection);
			if (!isFlags)
			{
				return list;
			}
			HashSet<long> hashSet = new HashSet<long>();
			foreach (long item in list.Select((T @enum) => Convert.ToInt64(@enum)))
			{
				long[] array = hashSet.ToArray();
				foreach (long num in array)
				{
					hashSet.Add(num | item);
				}
				hashSet.Add(item);
			}
			return hashSet.Select((long x) => Enum.ToObject(type, x)).Cast<T>().ToList();
		}

		public override object Clamp(object value)
		{
			if (!((AcceptableValueBase)this).IsValid(value))
			{
				return _values[0];
			}
			return value;
		}

		public override bool IsValid(object value)
		{
			if (value is T item)
			{
				return _values.Contains(item);
			}
			if (!(value is IConvertible))
			{
				return false;
			}
			long @long = Convert.ToInt64(value);
			return _values.Any((T x) => Convert.ToInt64(x) == @long);
		}

		public override string ToDescriptionString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			Type type = typeof(T);
			List<string> list = (from x in _values
				where Enum.IsDefined(type, x)
				select Enum.GetName(type, x)).ToList();
			stringBuilder.Append("# Acceptable values: ").Append(string.Join(", ", list));
			if (!_isFlags)
			{
				return stringBuilder.ToString();
			}
			List<string> list2 = list.Where((string x) => !string.Equals(x, "none", StringComparison.OrdinalIgnoreCase) && !string.Equals(x, "all", StringComparison.OrdinalIgnoreCase)).Take(2).ToList();
			if (list2.Count == 2)
			{
				stringBuilder.Append('\n').Append("# Multiple values can be set at the same time by separating them with , (e.g. ").Append(string.Join(", ", list2))
					.Append(")");
			}
			return stringBuilder.ToString();
		}
	}
	public static class ConfigurationCustomDrawer
	{
		public delegate bool IsMatchConfig(Type type, AcceptableValueBase acceptableValue);

		public delegate Action<ConfigEntryBase> CustomDrawerSupplier();

		private const string L10NPrefix = "mod_utils";

		private const string EnabledKey = "@config_button_enabled";

		private const string DisabledKey = "@config_button_disabled";

		private const string AddKey = "@config_button_add";

		private const string RemoveKey = "@config_button_remove";

		private static readonly Dictionary<IsMatchConfig, CustomDrawerSupplier> CustomDrawers;

		private static bool _defaultTranslationsInitialized;

		private static readonly Dictionary<string, Dictionary<string, string>> BuiltinDrawerTranslations;

		private static readonly Dictionary<string, Dictionary<string, string>> CustomDrawerTranslations;

		private static readonly Dictionary<string, string> OwnedDefaults;

		static ConfigurationCustomDrawer()
		{
			BuiltinDrawerTranslations = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase)
			{
				{
					"English",
					new Dictionary<string, string>
					{
						{ "@config_button_enabled", "Enabled" },
						{ "@config_button_disabled", "Disabled" },
						{ "@config_button_add", "Add" },
						{ "@config_button_remove", "Remove" }
					}
				},
				{
					"Japanese",
					new Dictionary<string, string>
					{
						{ "@config_button_enabled", "有効" },
						{ "@config_button_disabled", "無効" },
						{ "@config_button_add", "追加" },
						{ "@config_button_remove", "削除" }
					}
				}
			};
			CustomDrawerTranslations = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
			OwnedDefaults = new Dictionary<string, string>();
			CustomDrawers = new Dictionary<IsMatchConfig, CustomDrawerSupplier>
			{
				{
					IsBool,
					() => Bool
				},
				{
					IsFloatWithRange,
					() => FloatSlider
				},
				{ IsStringList, StringList },
				{
					IsFlagsEnum,
					() => Flags
				}
			};
		}

		public static void RegisterDefaultDrawerTranslation(string language, string key, string value)
		{
			if (!CustomDrawerTranslations.TryGetValue(language, out var value2))
			{
				value2 = new Dictionary<string, string>();
				CustomDrawerTranslations[language] = value2;
			}
			value2[key] = value;
			if (_defaultTranslationsInitialized)
			{
				RefreshDefaultTranslations();
			}
		}

		private static void EnsureDefaultTranslations()
		{
			if (!_defaultTranslationsInitialized)
			{
				_defaultTranslationsInitialized = TryAddDefaultTranslations();
			}
		}

		internal static void RefreshDefaultTranslations()
		{
			_defaultTranslationsInitialized = false;
			_defaultTranslationsInitialized = TryAddDefaultTranslations();
			if (!_defaultTranslationsInitialized)
			{
				Debug.LogWarning((object)"[ModUtils] Failed to refresh custom drawer fallback translations for the current language.");
			}
		}

		private static Dictionary<string, string> ResolveDrawerTranslations(string language)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(BuiltinDrawerTranslations["English"]);
			if (CustomDrawerTranslations.TryGetValue("English", out var value))
			{
				foreach (KeyValuePair<string, string> item in value)
				{
					dictionary[item.Key] = item.Value;
				}
			}
			if (!string.Equals(language, "English", StringComparison.OrdinalIgnoreCase))
			{
				if (BuiltinDrawerTranslations.TryGetValue(language, out var value2))
				{
					foreach (KeyValuePair<string, string> item2 in value2)
					{
						dictionary[item2.Key] = item2.Value;
					}
				}
				if (CustomDrawerTranslations.TryGetValue(language, out var value3))
				{
					foreach (KeyValuePair<string, string> item3 in value3)
					{
						dictionary[item3.Key] = item3.Value;
					}
				}
			}
			return dictionary;
		}

		private static bool TryAddDefaultTranslations()
		{
			Localization instance;
			try
			{
				instance = Localization.instance;
			}
			catch (Exception)
			{
				return false;
			}
			if (instance == null)
			{
				return false;
			}
			Dictionary<string, string> field = Reflections.GetField<Dictionary<string, string>>(instance, "m_translations");
			if (field == null)
			{
				return false;
			}
			string text;
			try
			{
				text = instance.GetSelectedLanguage();
			}
			catch (Exception)
			{
				return false;
			}
			if (string.IsNullOrEmpty(text))
			{
				text = "English";
			}
			foreach (KeyValuePair<string, string> item in ResolveDrawerTranslations(text))
			{
				string translationKey = L10N.GetTranslationKey("mod_utils", item.Key);
				if (field.TryGetValue(translationKey, out var value))
				{
					if (OwnedDefaults.TryGetValue(translationKey, out var value2) && value == value2)
					{
						field[translationKey] = item.Value;
					}
				}
				else
				{
					field[translationKey] = item.Value;
				}
				OwnedDefaults[translationKey] = item.Value;
			}
			return true;
		}

		private static bool IsBool(Type type, AcceptableValueBase acceptableValue)
		{
			return type == typeof(bool);
		}

		private static bool IsFloatWithRange(Type type, AcceptableValueBase acceptableValue)
		{
			if (type == typeof(float))
			{
				return acceptableValue is AcceptableValueRange<float>;
			}
			return false;
		}

		private static bool IsStringList(Type type, AcceptableValueBase acceptableValue)
		{
			return type == typeof(StringList);
		}

		private static bool HasFlagsAttribute(Type type)
		{
			if (type.IsEnum)
			{
				return type.GetCustomAttributes(typeof(FlagsAttribute), inherit: false).Any();
			}
			return false;
		}

		private static bool IsFlagsEnum(Type type, AcceptableValueBase acceptableValue)
		{
			return HasFlagsAttribute(type);
		}

		public static void Bool(ConfigEntryBase entry)
		{
			EnsureDefaultTranslations();
			bool flag = (bool)entry.BoxedValue;
			string text = L10N.Translate("mod_utils", flag ? "@config_button_enabled" : "@config_button_disabled");
			bool flag2 = GUILayout.Toggle(flag, text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			if (flag2 != flag)
			{
				entry.BoxedValue = flag2;
			}
		}

		public static void FloatSlider(ConfigEntryBase entry)
		{
			AcceptableValueRange<float> val = (AcceptableValueRange<float>)(object)entry.Description.AcceptableValues;
			float num = (float)entry.BoxedValue;
			float minValue = val.MinValue;
			float maxValue = val.MaxValue;
			float num2 = GUILayout.HorizontalSlider(num, minValue, maxValue, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			num2 = Mathf.Floor(num2 * 100f) / 100f;
			if (Math.Abs(num2 - num) > Mathf.Abs(maxValue - minValue) / 1000f)
			{
				entry.BoxedValue = num2;
			}
			string text = num.ToString("0.00", CultureInfo.InvariantCulture);
			string text2 = GUILayout.TextField(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) });
			if (text2 == text)
			{
				return;
			}
			try
			{
				num2 = (float)Convert.ToDouble(text2, CultureInfo.InvariantCulture);
				object boxedValue = Convert.ChangeType(((AcceptableValueBase)val).Clamp((object)num2), entry.SettingType, CultureInfo.InvariantCulture);
				entry.BoxedValue = boxedValue;
			}
			catch (FormatException)
			{
			}
		}

		private static Action<ConfigEntryBase> StringList()
		{
			string inputText = "";
			return delegate(ConfigEntryBase entry)
			{
				//IL_011b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0125: Expected O, but got Unknown
				//IL_0120: Unknown result type (might be due to invalid IL or missing references)
				//IL_013a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0144: Expected O, but got Unknown
				//IL_013f: Unknown result type (might be due to invalid IL or missing references)
				EnsureDefaultTranslations();
				int num = Mathf.Min(Screen.width, 650);
				int num2 = num - Mathf.RoundToInt((float)num / 2.5f) - 115;
				string text = L10N.Translate("mod_utils", "@config_button_add");
				string text2 = L10N.Translate("mod_utils", "@config_button_remove");
				StringList stringList = new StringList((StringList)entry.BoxedValue);
				GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth((float)num2) });
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				inputText = GUILayout.TextField(inputText, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
				if (GUILayout.Button(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }) && !string.IsNullOrEmpty(inputText))
				{
					if (stringList.TryAdd(inputText))
					{
						entry.BoxedValue = stringList;
					}
					inputText = "";
				}
				GUILayout.EndHorizontal();
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				double num3 = 0.0;
				foreach (string item in stringList.ToList())
				{
					int num4 = Mathf.FloorToInt(GUI.skin.label.CalcSize(new GUIContent(item)).x) + Mathf.FloorToInt(GUI.skin.button.CalcSize(new GUIContent(text2)).x);
					num3 += (double)num4;
					if (num3 > (double)num2)
					{
						GUILayout.EndHorizontal();
						num3 = num4;
						GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					}
					GUILayout.Label(item, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
					if (GUILayout.Button(text2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }) && stringList.Remove(item))
					{
						entry.BoxedValue = stringList;
					}
				}
				GUILayout.EndHorizontal();
				GUILayout.EndVertical();
				GUILayout.FlexibleSpace();
			};
		}

		public static void Flags(ConfigEntryBase entry)
		{
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.Min(Screen.width, 650);
			int num2 = num - Mathf.RoundToInt((float)num / 2.5f) - 115;
			Type settingType = entry.SettingType;
			long num3 = Convert.ToInt64(entry.BoxedValue);
			AcceptableValueBase acceptableValues = entry.Description.AcceptableValues;
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth((float)num2) });
			int num4 = 0;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			foreach (object value2 in Enum.GetValues(settingType))
			{
				if (acceptableValues != null && !acceptableValues.IsValid(value2))
				{
					continue;
				}
				long num5 = Convert.ToInt64(value2);
				if (num5 != 0L)
				{
					string enumLabel = GetEnumLabel(settingType, value2);
					int num6 = Mathf.FloorToInt(GUI.skin.toggle.CalcSize(new GUIContent(enumLabel + "_")).x);
					num4 += num6;
					if (num4 > num2)
					{
						GUILayout.EndHorizontal();
						num4 = num6;
						GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					}
					GUI.changed = false;
					bool flag = GUILayout.Toggle((num3 & num5) == num5, enumLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
					if (GUI.changed)
					{
						long value = (flag ? (num3 | num5) : (num3 & ~num5));
						entry.BoxedValue = Enum.ToObject(settingType, value);
					}
				}
			}
			GUILayout.EndHorizontal();
			GUI.changed = false;
			GUILayout.EndVertical();
			GUILayout.FlexibleSpace();
		}

		public static Action<ConfigEntryBase> MultiSelect<T>(Func<IEnumerable<T>> allElementSupplier, Func<T, string> labelGenerator) where T : IComparable<T>
		{
			return delegate(ConfigEntryBase entry)
			{
				//IL_013b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0145: Expected O, but got Unknown
				//IL_0140: Unknown result type (might be due to invalid IL or missing references)
				int num = Mathf.Min(Screen.width, 650);
				int num2 = num - Mathf.RoundToInt((float)num / 2.5f) - 115;
				GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth((float)num2) });
				Type settingType = entry.SettingType;
				ConstructorInfo constructor = settingType.GetConstructor(new Type[1] { typeof(IEnumerable<T>) });
				if ((object)constructor == null)
				{
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label($"{settingType} does not define a constructor that takes IEnumerable<{typeof(T)}> as an argument.", Array.Empty<GUILayoutOption>());
					GUILayout.EndHorizontal();
					GUI.changed = false;
					GUILayout.EndVertical();
					GUILayout.FlexibleSpace();
				}
				else
				{
					ICollection<T> collection = (ICollection<T>)constructor.Invoke(new object[1] { entry.BoxedValue });
					AcceptableValueBase acceptableValues = entry.Description.AcceptableValues;
					int num3 = 0;
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					foreach (T item in allElementSupplier())
					{
						if (acceptableValues == null || acceptableValues.IsValid((object)item))
						{
							string text = labelGenerator(item) ?? item.ToString();
							int num4 = Mathf.FloorToInt(GUI.skin.toggle.CalcSize(new GUIContent(text + "_")).x);
							num3 += num4;
							if (num3 > num2)
							{
								GUILayout.EndHorizontal();
								num3 = num4;
								GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
							}
							GUI.changed = false;
							bool flag = GUILayout.Toggle(collection.Contains(item), text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
							if (GUI.changed)
							{
								if (flag)
								{
									collection.Add(item);
								}
								else
								{
									collection.Remove(item);
								}
								entry.BoxedValue = collection;
							}
						}
					}
					GUILayout.EndHorizontal();
					GUI.changed = false;
					GUILayout.EndVertical();
					GUILayout.FlexibleSpace();
				}
			};
		}

		public static void Register(IsMatchConfig matcher, CustomDrawerSupplier supplier)
		{
			CustomDrawers[matcher] = supplier;
		}

		public static Action<ConfigEntryBase> Get(Type type, AcceptableValueBase acceptableValue)
		{
			return (from x in CustomDrawers
				where x.Key(type, acceptableValue)
				select x.Value()).FirstOrDefault();
		}

		private static string GetEnumLabel(Type type, object @object)
		{
			return (type.GetMember(Enum.GetName(type, @object) ?? "").FirstOrDefault()?.GetCustomAttributes(typeof(DescriptionAttribute), inherit: false).OfType<DescriptionAttribute>().FirstOrDefault())?.Description ?? @object.ToString();
		}
	}
	public sealed class ConfigurationManagerAttributes
	{
		public bool? ShowRangeAsPercent;

		public Action<ConfigEntryBase> CustomDrawer;

		public bool? Browsable;

		public string Category;

		public object DefaultValue;

		public bool? HideDefaultButton;

		public bool? HideSettingName;

		public string Description;

		public string DispName;

		public int? Order;

		public bool? ReadOnly;

		public bool? IsAdvanced;

		public Func<object, string> ObjToStr;

		public Func<string, object> StrToObj;
	}
	public static class Csv
	{
		public sealed class Parser
		{
			private readonly StringBuilder _fieldBuffer;

			private readonly List<string> _recordBuffer;

			private readonly string _source;

			private readonly bool _trimUnquotedFields;

			private bool _fieldQuoted;

			private bool _fieldStarted;

			private bool _inQuotes;

			private bool _lastTokenWasDelimiter;

			private bool _quotedFieldClosed;

			private int _offset;

			public Parser(string source, int offset = 0, bool trimUnquotedFields = false)
			{
				_source = source ?? "";
				_offset = offset;
				_trimUnquotedFields = trimUnquotedFields;
				_recordBuffer = new List<string>();
				_fieldBuffer = new StringBuilder();
			}

			public List<List<string>> Parse()
			{
				List<List<string>> list = new List<List<string>>();
				int num = -1;
				while (HasNext())
				{
					List<string> list2 = ParseLine();
					int count = list2.Count;
					if (count != 0)
					{
						if (num == -1)
						{
							num = count;
						}
						else if (num != count)
						{
							throw new Exception("Number of fields in a record is not uniform.");
						}
						list.Add(list2);
					}
				}
				return list;
			}

			public bool HasNext()
			{
				return _offset < _source.Length;
			}

			public List<string> ParseLine()
			{
				_recordBuffer.Clear();
				_fieldBuffer.Clear();
				_fieldQuoted = false;
				_fieldStarted = false;
				_inQuotes = false;
				_lastTokenWasDelimiter = false;
				_quotedFieldClosed = false;
				List<string> list = new List<string>();
				bool recordHasContent = false;
				while (_offset < _source.Length)
				{
					char c = _source[_offset++];
					if (!ParseChar(c, ref recordHasContent))
					{
						break;
					}
				}
				if (recordHasContent || _fieldStarted || _lastTokenWasDelimiter)
				{
					FlushField();
				}
				if (_recordBuffer.Count > 0)
				{
					list.AddRange(_recordBuffer);
				}
				return list;
			}

			private bool ParseChar(char c, ref bool recordHasContent)
			{
				switch (c)
				{
				case '"':
					recordHasContent = true;
					if (_inQuotes)
					{
						if (_offset < _source.Length && _source[_offset] == '"')
						{
							_fieldBuffer.Append('"');
							_offset++;
						}
						else
						{
							_inQuotes = false;
							_quotedFieldClosed = true;
						}
					}
					else if (CanStartQuotedField())
					{
						_fieldQuoted = true;
						_fieldStarted = true;
						_inQuotes = true;
						_quotedFieldClosed = false;
						_fieldBuffer.Clear();
					}
					else
					{
						_fieldBuffer.Append(c);
						_fieldStarted = true;
						_quotedFieldClosed = false;
					}
					_lastTokenWasDelimiter = false;
					return true;
				case ',':
					if (!_inQuotes)
					{
						recordHasContent = true;
						FlushField();
						_lastTokenWasDelimiter = true;
						return true;
					}
					break;
				}
				if ((c == '\r' || c == '\n') && !_inQuotes)
				{
					if (c == '\r' && _offset < _source.Length && _source[_offset] == '\n')
					{
						_offset++;
					}
					return false;
				}
				if (_trimUnquotedFields && _fieldQuoted && _quotedFieldClosed && char.IsWhiteSpace(c))
				{
					_lastTokenWasDelimiter = false;
					return true;
				}
				recordHasContent = true;
				_fieldBuffer.Append(c);
				_fieldStarted = true;
				_quotedFieldClosed = false;
				_lastTokenWasDelimiter = false;
				return true;
			}

			private bool CanStartQuotedField()
			{
				if (_fieldStarted)
				{
					if (_trimUnquotedFields && !_fieldQuoted)
					{
						return FieldBufferIsWhitespace();
					}
					return false;
				}
				return true;
			}

			private bool FieldBufferIsWhitespace()
			{
				for (int i = 0; i < _fieldBuffer.Length; i++)
				{
					if (!char.IsWhiteSpace(_fieldBuffer[i]))
					{
						return false;
					}
				}
				return true;
			}

			private void FlushField()
			{
				string text = _fieldBuffer.ToString();
				_recordBuffer.Add((_trimUnquotedFields && !_fieldQuoted) ? text.Trim() : text);
				_fieldBuffer.Clear();
				_fieldQuoted = false;
				_fieldStarted = false;
				_quotedFieldClosed = false;
			}
		}

		private static readonly char[] MustQuoteChars = new char[4] { '"', ',', '\r', '\n' };

		public static string Escape(string field)
		{
			if (field == null)
			{
				return "";
			}
			if (field.Length == 0 || field.IndexOfAny(MustQuoteChars) != -1 || (field.Length > 0 && (char.IsWhiteSpace(field[0]) || char.IsWhiteSpace(field[field.Length - 1]))))
			{
				return "\"" + field.Replace("\"", "\"\"") + "\"";
			}
			return field;
		}

		public static List<List<string>> Parse(string csv)
		{
			return Parse(csv, trimUnquotedFields: false);
		}

		public static List<List<string>> Parse(string csv, bool trimUnquotedFields)
		{
			return new Parser(csv, 0, trimUnquotedFields).Parse();
		}

		public static List<string> ParseLine(string line)
		{
			return ParseLine(line, trimUnquotedFields: false);
		}

		public static List<string> ParseLine(string line, bool trimUnquotedFields)
		{
			return new Parser(line, 0, trimUnquotedFields).ParseLine();
		}
	}
	public class InstanceCache<T> : MonoBehaviour
	{
		private static readonly HashSet<T> Cache;

		private T _instance;

		public static Action<T> OnCacheAdded { get; set; }

		public static Action<T> OnCacheRemoved { get; set; }

		static InstanceCache()
		{
			Cache = new HashSet<T>();
		}

		private void Awake()
		{
			_instance = GetInstance();
			if (_instance != null)
			{
				object obj = _instance;
				Object val = (Object)((obj is Object) ? obj : null);
				if (val == null || Object.op_Implicit(val))
				{
					Cache.Add(_instance);
					OnCacheAdded?.Invoke(_instance);
					return;
				}
			}
			throw new NullReferenceException("Failed to acquire instance.");
		}

		private void OnDestroy()
		{
			Cache.Remove(_instance);
			OnCacheRemoved?.Invoke(_instance);
			_instance = default(T);
		}

		protected virtual T GetInstance()
		{
			return ((Component)this).GetComponent<T>();
		}

		public static IEnumerable<T> GetAllInstance()
		{
			return Cache.ToList();
		}

		public static void Fill(List<T> buffer)
		{
			if (buffer == null)
			{
				throw new ArgumentNullException("buffer");
			}
			buffer.Clear();
			buffer.AddRange(Cache);
		}
	}
	public enum WorldLevelMatchMode
	{
		Exact,
		CurrentOrHigher,
		Ignore
	}
	public static class Inventories
	{
		public static float CurrentWorldLevel
		{
			get
			{
				if (!((Object)(object)Game.instance != (Object)null))
				{
					return 0f;
				}
				return Game.m_worldLevel;
			}
		}

		private static bool IsWorldLevelMatch(ItemData data, float worldLevel, WorldLevelMatchMode matchMode)
		{
			return matchMode switch
			{
				WorldLevelMatchMode.Exact => (float)data.m_worldLevel == worldLevel, 
				WorldLevelMatchMode.CurrentOrHigher => (float)data.m_worldLevel >= CurrentWorldLevel, 
				WorldLevelMatchMode.Ignore => true, 
				_ => (float)data.m_worldLevel == worldLevel, 
			};
		}

		private static bool IsMatchedItem(ItemData data, string name, float worldLevel, int quality, bool isPrefabName, WorldLevelMatchMode matchMode)
		{
			if (data == null)
			{
				return false;
			}
			bool num;
			if (!isPrefabName)
			{
				num = data.m_shared.m_name == name;
			}
			else
			{
				if (!((Object)(object)data.m_dropPrefab != (Object)null))
				{
					goto IL_0057;
				}
				num = ((Object)data.m_dropPrefab).name == name;
			}
			if (num && (quality < 0 || data.m_quality == quality))
			{
				return IsWorldLevelMatch(data, worldLevel, matchMode);
			}
			goto IL_0057;
			IL_0057:
			return false;
		}

		private static void NotifyChanged(Inventory inventory)
		{
			Reflections.InvokeMethod(inventory, "Changed");
		}

		private static int CountItems(Inventory inventory, string name, float worldLevel, int quality, bool isPrefabName = false, WorldLevelMatchMode matchMode = WorldLevelMatchMode.Exact)
		{
			return GetItems(inventory, name, worldLevel, matchMode, quality, isPrefabName).Sum((ItemData x) => x.m_stack);
		}

		private static int FindFreeStackSpace(Inventory inventory, string name, float worldLevel, int quality, bool isPrefabName)
		{
			return (from item in GetItems(inventory, name, worldLevel, WorldLevelMatchMode.Exact, quality, isPrefabName)
				where item.m_stack < item.m_shared.m_maxStackSize
				select item).Sum((ItemData item) => item.m_shared.m_maxStackSize - item.m_stack);
		}

		public static IEnumerable<ItemData> GetItems(Inventory inventory, string name, float worldLevel, WorldLevelMatchMode matchMode, int quality = -1, bool isPrefabName = false)
		{
			return (from data in inventory.GetAllItems()
				where IsMatchedItem(data, name, worldLevel, quality, isPrefabName, matchMode)
				select data).ToList();
		}

		[Obsolete("Use overload with WorldLevelMatchMode parameter")]
		public static IEnumerable<ItemData> GetItems(Inventory inventory, string name, float worldLevel, int quality = -1, bool isPrefabName = false)
		{
			return GetItems(inventory, name, worldLevel, WorldLevelMatchMode.Exact, quality, isPrefabName);
		}

		public static int AddItem(Inventory inventory, GameObject prefab, int amount, int quality = -1)
		{
			if (amount <= 0)
			{
				return 0;
			}
			ItemData val = prefab.GetComponent<ItemDrop>().m_itemData.Clone();
			val.m_dropPrefab = prefab;
			val.m_stack = Mathf.Min(amount, val.m_shared.m_maxStackSize);
			val.m_quality = Mathf.Clamp(quality, 1, val.m_shared.m_maxQuality);
			if ((Object)(object)Game.instance != (Object)null)
			{
				val.m_worldLevel = Game.m_worldLevel;
			}
			int num = CountItems(inventory, val.m_shared.m_name, val.m_worldLevel, val.m_quality);
			inventory.AddItem(val);
			return CountItems(inventory, val.m_shared.m_name, val.m_worldLevel, val.m_quality) - num;
		}

		public static int FillFreeStackSpace(Inventory from, Inventory to, string name, float worldLevel, int amount, int quality = -1, bool isPrefabName = false)
		{
			if (amount <= 0)
			{
				return 0;
			}
			int num = Mathf.Min(amount, FindFreeStackSpace(to, name, worldLevel, quality, isPrefabName));
			if (num == 0)
			{
				return 0;
			}
			int num2 = 0;
			IEnumerable<ItemData> items = GetItems(to, name, worldLevel, WorldLevelMatchMode.Exact, quality, isPrefabName);
			foreach (ItemData item in GetItems(from, name, worldLevel, WorldLevelMatchMode.Exact, quality, isPrefabName))
			{
				foreach (ItemData item2 in items)
				{
					if (item2.m_stack >= item2.m_shared.m_maxStackSize)
					{
						continue;
					}
					int stack = item.m_stack;
					int num3 = Mathf.Min(Mathf.Min(num, stack), item2.m_shared.m_maxStackSize - item2.m_stack);
					if (num3 != 0)
					{
						item2.m_stack += num3;
						num2 += num3;
						num -= num3;
						from.RemoveItem(item, num3);
						NotifyChanged(to);
						if (num == 0)
						{
							goto end_IL_00ff;
						}
						if (stack - num3 <= 0)
						{
							break;
						}
					}
				}
				continue;
				end_IL_00ff:
				break;
			}
			return num2;
		}

		public static int FillFreeStackSpace(Inventory inventory, string name, float worldLevel, int amount, int quality = -1, bool isPrefabName = false)
		{
			if (amount <= 0)
			{
				return 0;
			}
			int num = amount;
			int num2 = 0;
			foreach (ItemData item in GetItems(inventory, name, worldLevel, WorldLevelMatchMode.Exact, quality, isPrefabName))
			{
				if (item.m_stack >= item.m_shared.m_maxStackSize)
				{
					continue;
				}
				int num3 = Mathf.Min(num, item.m_shared.m_maxStackSize - item.m_stack);
				if (num3 > 0)
				{
					item.m_stack += num3;
					num2 += num3;
					num -= num3;
					NotifyChanged(inventory);
					if (num == 0)
					{
						break;
					}
				}
			}
			return num2;
		}

		public static bool HaveItem(Inventory inventory, string name, float worldLevel, WorldLevelMatchMode matchMode, int amount, int quality = -1, bool isPrefabName = false)
		{
			if (amount <= 0)
			{
				return true;
			}
			int num = 0;
			foreach (ItemData item in GetItems(inventory, name, worldLevel, matchMode, quality, isPrefabName))
			{
				num += item.m_stack;
				if (num >= amount)
				{
					return true;
				}
			}
			return false;
		}

		[Obsolete("Use overload with WorldLevelMatchMode parameter")]
		public static bool HaveItem(Inventory inventory, string name, float worldLevel, int amount, int quality = -1, bool isPrefabName = false)
		{
			return HaveItem(inventory, name, worldLevel, WorldLevelMatchMode.Exact, amount, quality, isPrefabName);
		}

		public static int RemoveItem(Inventory inventory, string name, float worldLevel, WorldLevelMatchMode matchMode, int amount, int quality = -1, bool isPrefabName = false)
		{
			if (amount <= 0)
			{
				return 0;
			}
			int num = 0;
			foreach (ItemData item in GetItems(inventory, name, worldLevel, matchMode, quality, isPrefabName))
			{
				int num2 = Mathf.Min(amount, item.m_stack);
				inventory.RemoveItem(item, num2);
				num += num2;
				amount -= num2;
				if (amount == 0)
				{
					break;
				}
			}
			return num;
		}

		[Obsolete("Use overload with WorldLevelMatchMode parameter")]
		public static int RemoveItem(Inventory inventory, string name, float worldLevel, int amount, int quality = -1, bool isPrefabName = false)
		{
			return RemoveItem(inventory, name, worldLevel, WorldLevelMatchMode.Exact, amount, quality, isPrefabName);
		}
	}
	public static class Json
	{
		public static void AddImporter<TJson, TValue>(ImporterFunc<TJson, TValue> importer)
		{
			JsonMapper.RegisterImporter(importer);
		}

		public static void AddExporter<T>(ExporterFunc<T> exporter)
		{
			JsonMapper.RegisterExporter(exporter);
		}

		public static T Parse<T>(string jsonText, ReaderOption option)
		{
			return JsonMapper.ToObject<T>(new JsonReader(jsonText)
			{
				AllowComments = option.AllowComments,
				AllowSingleQuotedStrings = option.AllowSingleQuotedStrings,
				SkipNonMembers = option.SkipNonMembers
			});
		}

		public static T Parse<T>(string jsonText)
		{
			return Parse<T>(jsonText, new ReaderOption
			{
				AllowComments = true,
				AllowSingleQuotedStrings = false,
				SkipNonMembers = true
			});
		}

		public static string ToString(object obj, WriterOption option)
		{
			StringBuilder stringBuilder = new StringBuilder();
			JsonMapper.ToJson(obj, new JsonWriter(stringBuilder)
			{
				IndentValue = option.IndentSize,
				PrettyPrint = option.Pretty
			});
			return stringBuilder.ToString();
		}

		public static string ToString(object obj)
		{
			return ToString(obj, new WriterOption
			{
				Pretty = false,
				IndentSize = 0
			});
		}
	}
	public struct ReaderOption
	{
		public bool AllowComments;

		public bool AllowSingleQuotedStrings;

		public bool SkipNonMembers;
	}
	public struct WriterOption
	{
		public bool Pretty;

		public int IndentSize;
	}
	public class L10N
	{
		private struct TranslationSource
		{
			public L10N Localization;

			public string Directory;
		}

		private static readonly Regex InternalNamePattern;

		private static readonly Dictionary<string, string> TranslationCache;

		private static readonly Dictionary<string, string> RuntimeWords;

		private static readonly Regex WordPattern;

		private const string HarmonyId = "net.eideehi.modutils.localization";

		private static bool _patchesApplied;

		private static bool _initializePatchApplied;

		private static bool _setLanguagePatchApplied;

		private static bool _initializeMethodWarningLogged;

		private static bool _setLanguageMethodWarningLogged;

		private static bool _localizationCacheWarningLogged;

		private static string _currentLanguage;

		private static readonly List<TranslationSource> _translationSources;

		private readonly string _prefix;

		public static event Action<string> LanguageChanged;

		static L10N()
		{
			_translationSources = new List<TranslationSource>();
			InternalNamePattern = new Regex("^(\\$|@)(\\w|\\d|[^\\s(){}[\\]+\\-!?/\\\\&%,.:=<>])+$", RegexOptions.Compiled);
			TranslationCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			RuntimeWords = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			WordPattern = new Regex("(\\$|@)((?:\\w|\\d|[^\\s(){}[\\]+\\-!?/\\\\&%,.:=<>])+)", RegexOptions.Compiled);
		}

		public L10N(string prefix)
		{
			_prefix = prefix;
		}

		internal static void EnsurePatched()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			if (_patchesApplied)
			{
				return;
			}
			try
			{
				Harmony val = new Harmony("net.eideehi.modutils.localization");
				if (!_initializePatchApplied)
				{
					MethodInfo methodInfo = AccessTools.Method(typeof(Localization), "Initialize", (Type[])null, (Type[])null);
					if (methodInfo == null)
					{
						if (!_initializeMethodWarningLogged)
						{
							Debug.LogWarning((object)"[ModUtils] Localization.Initialize method not found. Auto-reload for initialization will not work until it becomes available.");
							_initializeMethodWarningLogged = true;
						}
					}
					else
					{
						val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(L10N), "OnLocalizationInitialized", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						_initializePatchApplied = true;
					}
				}
				if (!_setLanguagePatchApplied)
				{
					MethodInfo methodInfo2 = AccessTools.Method(typeof(Localization), "SetLanguage", (Type[])null, (Type[])null);
					if (methodInfo2 == null)
					{
						if (!_setLanguageMethodWarningLogged)
						{
							Debug.LogWarning((object)"[ModUtils] Localization.SetLanguage method not found. Auto-reload for language change will not work until it becomes available.");
							_setLanguageMethodWarningLogged = true;
						}
					}
					else
					{
						val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(L10N), "OnLanguageSet", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						_setLanguagePatchApplied = true;
					}
				}
				_patchesApplied = _initializePatchApplied && _setLanguagePatchApplied;
			}
			catch (Exception arg)
			{
				Debug.LogWarning((object)$"[ModUtils] Failed to patch Localization lifecycle methods. Auto-reload will stay disabled. {arg}");
			}
		}

		private static void OnLocalizationInitialized()
		{
			try
			{
				Localization instance = Localization.instance;
				string text = ((instance != null) ? instance.GetSelectedLanguage() : null);
				if (!string.IsNullOrEmpty(text))
				{
					HandleLanguageChange(text);
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[ModUtils] OnLocalizationInitialized error: {arg}");
			}
		}

		private static void OnLanguageSet(string language)
		{
			try
			{
				if (!string.IsNullOrEmpty(language))
				{
					HandleLanguageChange(language);
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[ModUtils] OnLanguageSet error: {arg}");
			}
		}

		private static void HandleLanguageChange(string language)
		{
			ApplyLanguageChange(language, fireLanguageChanged: true);
		}

		public static string SyncCurrentLanguage()
		{
			EnsurePatched();
			try
			{
				Localization instance = Localization.instance;
				if (instance == null)
				{
					return null;
				}
				string selectedLanguage = instance.GetSelectedLanguage();
				if (string.IsNullOrEmpty(selectedLanguage))
				{
					return null;
				}
				ApplyLanguageChange(selectedLanguage, fireLanguageChanged: false);
				return selectedLanguage;
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[ModUtils] SyncCurrentLanguage failed: {arg}");
				return null;
			}
		}

		private static void ApplyLanguageChange(string language, bool fireLanguageChanged)
		{
			if (!string.IsNullOrEmpty(language) && !string.Equals(language, _currentLanguage, StringComparison.OrdinalIgnoreCase))
			{
				ReloadTranslations(language);
				_currentLanguage = language;
				RefreshConfigurationMetadata();
				RefreshDefaultDrawerTranslations();
				if (fireLanguageChanged)
				{
					FireLanguageChanged(language);
				}
			}
		}

		private static void RefreshConfigurationMetadata()
		{
			try
			{
				Configuration.RefreshAllLocalizedMetadata();
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[ModUtils] Failed to refresh localized config metadata: {arg}");
			}
		}

		private static void RefreshDefaultDrawerTranslations()
		{
			try
			{
				ConfigurationCustomDrawer.RefreshDefaultTranslations();
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[ModUtils] Failed to refresh custom drawer translations: {arg}");
			}
		}

		private static void ReloadTranslations(string language)
		{
			TranslationCache.Clear();
			foreach (TranslationSource translationSource in _translationSources)
			{
				try
				{
					new TranslationsLoader(translationSource.Localization).LoadTranslations(translationSource.Directory, language);
				}
				catch (Exception arg)
				{
					Debug.LogError((object)$"[ModUtils] Failed to reload translations from {translationSource.Directory}: {arg}");
				}
			}
			Localization localization = TryGetLocalization();
			foreach (KeyValuePair<string, string> runtimeWord in RuntimeWords)
			{
				TranslationCache[runtimeWord.Key] = runtimeWord.Value;
				AddWordToLocalization(localization, runtimeWord.Key, runtimeWord.Value);
			}
		}

		private static void FireLanguageChanged(string language)
		{
			Action<string> languageChanged = L10N.LanguageChanged;
			if (languageChanged == null)
			{
				return;
			}
			Delegate[] invocationList = languageChanged.GetInvocationList();
			foreach (Delegate @delegate in invocationList)
			{
				try
				{
					((Action<string>)@delegate)(language);
				}
				catch (Exception arg)
				{
					Debug.LogError((object)$"[ModUtils] LanguageChanged handler error: {arg}");
				}
			}
		}

		public void AddTranslationDirectory(string directory)
		{
			EnsurePatched();
			_translationSources.Add(new TranslationSource
			{
				Localization = this,
				Directory = directory
			});
			new TranslationsLoader(this).LoadTranslations(directory, DetectCurrentLanguage());
			Localization localization = TryGetLocalization();
			foreach (KeyValuePair<string, string> runtimeWord in RuntimeWords)
			{
				TranslationCache[runtimeWord.Key] = runtimeWord.Value;
				AddWordToLocalization(localization, runtimeWord.Key, runtimeWord.Value);
			}
		}

		private static string DetectCurrentLanguage()
		{
			if (_currentLanguage != null)
			{
				return _currentLanguage;
			}
			try
			{
				Localization instance = Localization.instance;
				string text = ((instance != null) ? instance.GetSelectedLanguage() : null);
				if (!string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
			catch (Exception)
			{
			}
			return "English";
		}

		internal static Localization TryGetLocalization()
		{
			try
			{
				Localization instance = Localization.instance;
				if (instance == null)
				{
					return null;
				}
				return string.IsNullOrEmpty(instance.GetSelectedLanguage()) ? null : instance;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static string InvokeTranslate(string word)
		{
			Localization val = TryGetLocalization();
			TranslationCache.TryGetValue(word, out var value);
			if (val == null)
			{
				return value ?? word;
			}
			string text = Reflections.InvokeMethod<string>(val, "Translate", new object[1] { word });
			if (text == null || IsMissingTranslation(word, text))
			{
				return value ?? word;
			}
			return text;
		}

		private static bool IsMissingTranslation(string word, string localized)
		{
			if (!string.Equals(localized, word, StringComparison.Ordinal))
			{
				return string.Equals(localized, "[" + word + "]", StringComparison.Ordinal);
			}
			return true;
		}

		private static string InvokeInsertWordsFallback(string text, IReadOnlyList<string> words)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			string text2 = text;
			for (int i = 0; i < words.Count; i++)
			{
				text2 = text2.Replace($"${i + 1}", words[i] ?? "");
			}
			return text2;
		}

		private static string InvokeInsertWords(string text, string[] words)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			Localization val = TryGetLocalization();
			if (val == null)
			{
				return InvokeInsertWordsFallback(text, words);
			}
			return Reflections.InvokeMethod<string>(val, "InsertWords", new object[2] { text, words });
		}

		private static void InvokeAddWord(string key, string word)
		{
			TranslationCache[key] = word;
			AddWordToLocalization(TryGetLocalization(), key, word);
		}

		private static void InvokeAddRuntimeWord(string key, string word)
		{
			RuntimeWords[key] = word;
			TranslationCache[key] = word;
			AddWordToLocalization(TryGetLocalization(), key, word);
		}

		private static void AddWordToLocalization(Localization localization, string key, string word)
		{
			if (localization != null)
			{
				Reflections.InvokeMethod(localization, "AddWord", key, word);
				EvictLocalizationCache(localization);
			}
		}

		private static void EvictLocalizationCache(Localization localization)
		{
			try
			{
				object field = Reflections.GetField<object>(localization, "m_cache");
				if (field != null)
				{
					Reflections.InvokeMethod(field, "EvictAll");
				}
			}
			catch (Exception arg)
			{
				if (!_localizationCacheWarningLogged)
				{
					Debug.LogWarning((object)$"[ModUtils] Failed to evict Localization cache after adding a word. Game-localized text may stay stale until the next language reload. {arg}");
					_localizationCacheWarningLogged = true;
				}
			}
		}

		internal static string GetTranslationKey(string prefix, string internalName)
		{
			if (string.IsNullOrEmpty(internalName))
			{
				return "";
			}
			return internalName[0] switch
			{
				'$' => internalName.Substring(1), 
				'@' => prefix + "_" + internalName.Substring(1), 
				_ => internalName, 
			};
		}

		internal static string Translate(string prefix, string word)
		{
			return InvokeTranslate(GetTranslationKey(prefix, word));
		}

		public static bool IsInternalName(string text)
		{
			if (!string.IsNullOrEmpty(text))
			{
				return InternalNamePattern.IsMatch(text);
			}
			return false;
		}

		private string GetTranslationKey(string internalName)
		{
			return GetTranslationKey(_prefix, internalName);
		}

		public void AddWord(string key, string word)
		{
			InvokeAddRuntimeWord(GetTranslationKey(key), word);
		}

		internal void AddFileWord(string key, string word)
		{
			InvokeAddWord(GetTranslationKey(key), word);
		}

		public string Translate(string word)
		{
			return InvokeTranslate(GetTranslationKey(word));
		}

		public string TranslateInternalName(string internalName)
		{
			if (IsInternalName(internalName))
			{
				return InvokeTranslate(GetTranslationKey(internalName));
			}
			return internalName;
		}

		public string Localize(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder();
			int num = 0;
			foreach (Match item in WordPattern.Matches(text))
			{
				GroupCollection groups = item.Groups;
				string word = ((groups[1].Value == "@") ? (_prefix + "_" + groups[2].Value) : groups[2].Value);
				stringBuilder.Append(text.Substring(num, groups[0].Index - num));
				stringBuilder.Append(InvokeTranslate(word));
				num = groups[0].Index + groups[0].Value.Length;
			}
			stringBuilder.Append(text.Substring(num));
			return stringBuilder.ToString();
		}

		public string Localize(string text, params object[] args)
		{
			object[] array = args ?? new object[0];
			return InvokeInsertWords(Localize(text), Array.ConvertAll(array, (object arg) => (arg != null) ? ((!(arg is string internalName)) ? arg.ToString() : TranslateInternalName(internalName)) : ""));
		}

		public string LocalizeTextOnly(string text, params object[] args)
		{
			object[] array = args ?? new object[0];
			return InvokeInsertWords(Localize(text), Array.ConvertAll(array, delegate(object arg)
			{
				object obj;
				if (arg != null)
				{
					obj = arg as string;
					if (obj == null)
					{
						return arg.ToString();
					}
				}
				else
				{
					obj = "";
				}
				return (string)obj;
			}));
		}
	}
	public class TranslationsLoader
	{
		private static readonly string DefaultLanguage;

		private static readonly string JsonFilePattern;

		private readonly L10N _localization;

		private Dictionary<string, TranslationsFile> _cache;

		private Logger _logger;

		static TranslationsLoader()
		{
			DefaultLanguage = "English";
			JsonFilePattern = "*.json";
		}

		public TranslationsLoader(L10N localization)
		{
			_localization = localization;
		}

		public void SetDebugLogger(Logger logger)
		{
			_logger = logger;
		}

		private bool LoadAllFile(string directory, string filePattern, string language, Func<string, string, bool> loading)
		{
			_logger?.Debug("Load translation files for " + language + " from directory: [directory: " + directory + ", file pattern: " + filePattern + "]");
			return Directory.EnumerateFiles(directory, filePattern, SearchOption.AllDirectories).Count((string path) => loading(path, language)) > 0;
		}

		public void LoadTranslations(string languagesDir, string language)
		{
			_cache = new Dictionary<string, TranslationsFile>();
			if (!Directory.Exists(languagesDir))
			{
				_logger?.Error("Directory does not exist: " + languagesDir);
				return;
			}
			if (language != DefaultLanguage && !LoadAllFile(languagesDir, JsonFilePattern, DefaultLanguage, ReadJsonFile))
			{
				_logger?.Warning("Directory does not contain a translation file for the default language: " + languagesDir);
			}
			if (!LoadAllFile(languagesDir, JsonFilePattern, language, ReadJsonFile))
			{
				_logger?.Warning("Directory does not contain a translation file for the " + language + ": " + languagesDir);
			}
			_cache = null;
		}

		public void LoadTranslations(string languagesDir)
		{
			string text;
			try
			{
				Localization instance = Localization.instance;
				text = ((instance != null) ? instance.GetSelectedLanguage() : null);
			}
			catch (Exception)
			{
				text = null;
			}
			LoadTranslations(languagesDir, string.IsNullOrEmpty(text) ? DefaultLanguage : text);
		}

		private bool ReadJsonFile(string path, string language)
		{
			if (!_cache.TryGetValue(path, out var value))
			{
				try
				{
					value = Json.Parse<TranslationsFile>(File.ReadAllText(path));
					_cache.Add(path, value);
				}
				catch (Exception arg)
				{
					_logger?.Error($"Failed to read Json file\n{arg}");
					_cache.Add(path, default(TranslationsFile));
					return false;
				}
			}
			if (!string.Equals(value.language, language, StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			_logger?.Debug("Load translations: " + path);
			if (value.translations == null)
			{
				_logger?.Warning("Translation file does not contain translations: " + path);
				return true;
			}
			foreach (KeyValuePair<string, string> translation in value.translations)
			{
				_localization.AddFileWord(translation.Key, translation.Value);
			}
			return true;
		}
	}
	[Serializable]
	public struct TranslationsFile
	{
		public string language;

		public Dictionary<string, string> translations;
	}
	public class Logger
	{
		private readonly Func<LogLevel, bool> _isEnabled;

		private readonly ManualLogSource _logger;

		public Logger(ManualLogSource logger, Func<LogLevel, bool> isEnabled)
		{
			_logger = logger;
			_isEnabled = isEnabled;
		}

		private void Log(LogLevel level, string message)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (_isEnabled(level))
			{
				_logger.Log(level, (object)message);
			}
		}

		private void Log(LogLevel level, Func<string> message)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (_isEnabled(level))
			{
				_logger.Log(level, (object)message());
			}
		}

		public void Fatal(Func<string> message)
		{
			Log((LogLevel)1, message);
		}

		public void Fatal(string message)
		{
			Log((LogLevel)1, message);
		}

		public void Error(Func<string> message)
		{
			Log((LogLevel)2, message);
		}

		public void Error(string message)
		{
			Log((LogLevel)2, message);
		}

		public void Warning(Func<string> message)
		{
			Log((LogLevel)4, message);
		}

		public void Warning(string message)
		{
			Log((LogLevel)4, message);
		}

		public void Info(Func<string> message)
		{
			Log((LogLevel)16, message);
		}

		public void Info(string message)
		{
			Log((LogLevel)16, message);
		}

		public void Message(Func<string> message)
		{
			Log((LogLevel)8, message);
		}

		public void Message(string message)
		{
			Log((LogLevel)8, message);
		}

		public void Debug(Func<string> message)
		{
			Log((LogLevel)32, message);
		}

		public void Debug(string message)
		{
			Log((LogLevel)32, message);
		}
	}
	public abstract class ObjectNode<TNode, TNetwork> : MonoBehaviour where TNode : ObjectNode<TNode, TNetwork> where TNetwork : ObjectNetwork<TNode, TNetwork>
	{
		protected static readonly HashSet<TNode> ObjectNodes;

		private TNode This => (TNode)this;

		public TNetwork Network { get; set; }

		static ObjectNode()
		{
			ObjectNodes = new HashSet<TNode>();
		}

		protected virtual void Awake()
		{
			ObjectNodes.Add(This);
			((MonoBehaviour)this).Invoke("NetworkConstruction", Random.Range(1f, 2f));
		}

		protected virtual void OnDestroy()
		{
			ObjectNodes.Remove(This);
			Network?.RemoveNode(This);
			Network = null;
		}

		protected abstract TNetwork CreateNetwork();

		protected abstract bool IsConnectable(TNode other);

		private void NetworkConstruction()
		{
			foreach (TNode item in ObjectNodes.Where(IsConnectable))
			{
				if (Network == item.Network)
				{
					continue;
				}
				if (Network == null)
				{
					Network = item.Network;
					Network.AddNode(This);
					continue;
				}
				if (item.Network == null)
				{
					item.Network = Network;
					item.Network.AddNode(item);
					continue;
				}
				TNetwork obj = ((Network.NodeCount >= item.Network.NodeCount) ? item.Network : Network);
				TNetwork network = ((Network.NodeCount >= item.Network.NodeCount) ? Network : item.Network);
				foreach (TNode allNode in obj.GetAllNodes())
				{
					allNode.Network.RemoveNode(allNode);
					allNode.Network = network;
					allNode.Network.AddNode(allNode);
				}
			}
			if (Network == null)
			{
				Network = CreateNetwork();
				Network.AddNode(This);
			}
		}
	}
	public class ObjectNetwork<TNode, TNetwork> where TNode : ObjectNode<TNode, TNetwork> where TNetwork : ObjectNetwork<TNode, TNetwork>
	{
		private readonly HashSet<TNode> _nodes;

		protected Action<IEnumerable<TNode>> OnNodeChanged { get; set; }

		public bool IsDirty { get; private set; }

		public int NodeCount => _nodes.Count;

		protected ObjectNetwork()
		{
			_nodes = new HashSet<TNode>();
		}

		public IEnumerable<TNode> EnumerateNodes()
		{
			return _nodes;
		}

		public IEnumerable<TNode> GetAllNodes()
		{
			return _nodes.ToList();
		}

		public void AddNode(TNode node)
		{
			if (_nodes.Add(node))
			{
				IsDirty = true;
			}
		}

		public void RemoveNode(TNode node)
		{
			if (_nodes.Remove(node))
			{
				IsDirty = true;
			}
		}

		public void Update()
		{
			if (IsDirty)
			{
				OnNodeChanged?.Invoke(GetAllNodes());
				IsDirty = false;
			}
		}
	}
	public static class Objects
	{
		private static readonly ConditionalWeakTable<Component, ZNetView> ZNetViewCache;

		private static readonly ConditionalWeakTable<Component, string> NameCache;

		private const string HoverableMarker = "\u0001HOVERABLE";

		static Objects()
		{
			ZNetViewCache = new ConditionalWeakTable<Component, ZNetView>();
			NameCache = new ConditionalWeakTable<Component, string>();
		}

		public static string GetPrefabName(GameObject gameObject)
		{
			return Utils.GetPrefabName(gameObject);
		}

		public static string GetName(Component component)
		{
			if (!Object.op_Implicit((Object)(object)component))
			{
				return "";
			}
			if (NameCache.TryGetValue(component, out var value))
			{
				if ((object)value == "\u0001HOVERABLE")
				{
					Hoverable component2 = component.GetComponent<Hoverable>();
					string text = ((component2 != null) ? component2.GetHoverName() : null);
					if (string.IsNullOrEmpty(text))
					{
						return Utils.GetPrefabName(component.gameObject);
					}
					return text;
				}
				return value;
			}
			string text2 = (from x in ((object)component).GetType().GetFields(AccessTools.all)
				where x.Name == "m_name" && x.FieldType == typeof(string)
				select x.GetValue(component) as string).FirstOrDefault();
			if (!string.IsNullOrEmpty(text2))
			{
				NameCache.Add(component, text2);
				return text2;
			}
			Hoverable component3 = component.GetComponent<Hoverable>();
			if (component3 != null)
			{
				HoverText val = (HoverText)(object)((component3 is HoverText) ? component3 : null);
				if (val != null)
				{
					text2 = val.m_text;
					if (!string.IsNullOrEmpty(text2))
					{
						NameCache.Add(component, text2);
						return text2;
					}
				}
				else
				{
					text2 = component3.GetHoverName();
					if (!string.IsNullOrEmpty(text2))
					{
						NameCache.Add(component, "\u0001HOVERABLE");
						return text2;
					}
				}
			}
			text2 = Utils.GetPrefabName(component.gameObject);
			NameCache.Add(component, text2);
			return text2;
		}

		public static bool GetZNetView(Component component, out ZNetView zNetView)
		{
			if (!Object.op_Implicit((Object)(object)component))
			{
				zNetView = null;
				return false;
			}
			if (ZNetViewCache.TryGetValue(component, out zNetView))
			{
				return Object.op_Implicit((Object)(object)zNetView);
			}
			zNetView = (from x in ((object)component).GetType().GetFields(AccessTools.all)
				where x.Name == "m_nview" && x.FieldType == typeof(ZNetView)
				select x).Select(delegate(FieldInfo x)
			{
				object? value = x.GetValue(component);
				return (ZNetView)((value is ZNetView) ? value : null);
			}).FirstOrDefault() ?? component.GetComponent<ZNetView>();
			if ((Object)(object)zNetView == (Object)null)
			{
				return false;
			}
			ZNetViewCache.Add(component, zNetView);
			return true;
		}

		public static bool GetZdoid(Component component, out ZDOID id)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			id = (GetZNetView(component, out var zNetView) ? zNetView.GetZDO() : null)?.m_uid ?? ZDOID.None;
			return id != ZDOID.None;
		}

		public static bool HasValidOwnership(ZNetView zNetView)
		{
			if ((Object)(object)zNetView != (Object)null && zNetView.GetZDO() != null && zNetView.IsValid())
			{
				return zNetView.IsOwner();
			}
			return false;
		}

		public static bool HasValidOwnership(Component component)
		{
			ZNetView zNetView;
			return HasValidOwnership(component, out zNetView);
		}

		public static bool HasValidOwnership(Component component, out ZNetView zNetView)
		{
			if (GetZNetView(component, out zNetView))
			{
				return HasValidOwnership(zNetView);
			}
			return false;
		}

		public static float Distance(Component obj1, Component obj2)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			return Vector3.Distance(obj1.transform.position, obj2.transform.position);
		}

		public static List<(Collider collider, T obj, float distance)> GetInsideSphere<T>(Vector3 origin, float radius, Func<Collider, T> convertor, Collider[] buffer, int layerMask = -1)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			List<(Collider, T, float)> list = new List<(Collider, T, float)>();
			int num = Physics.OverlapSphereNonAlloc(origin, radius, buffer, layerMask);
			for (int i = 0; i < num; i++)
			{
				Collider val = buffer[i];
				T val2 = convertor(val);
				if (val2 != null)
				{
					list.Add((val, val2, Vector3.Distance(origin, ((Component)val).transform.position)));
				}
			}
			return list;
		}

		public static List<(Collider collider, T obj, float distance)> GetInsideSphere<T>(Vector3 origin, float radius, Func<Collider, T> convertor, int bufferSize = 128, int layerMask = -1)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return GetInsideSphere(origin, radius, convertor, (Collider[])(object)new Collider[bufferSize], layerMask);
		}
	}
	public static class Reflections
	{
		public static TR InvokeStaticMethod<T, TR>(string methodName, params object[] args)
		{
			return Traverse.Create<T>().Method(methodName, args).GetValue<TR>(args);
		}

		public static void InvokeStaticMethod<T>(string methodName, params object[] args)
		{
			Traverse.Create<T>().Method(methodName, args).GetValue(args);
		}

		public static TR InvokeStaticMethod<T, TR>(string methodName)
		{
			return Traverse.Create<T>().Method(methodName, Array.Empty<object>()).GetValue<TR>();
		}

		public static void InvokeStaticMethod<T>(string methodName)
		{
			Traverse.Create<T>().Method(methodName, Array.Empty<object>()).GetValue();
		}

		public static T InvokeMethod<T>(object instance, string methodName, params object[] args)
		{
			return Traverse.Create(instance).Method(methodName, args).GetValue<T>(args);
		}

		public static void InvokeMethod(object instance, string methodName, params object[] args)
		{
			Traverse.Create(instance).Method(methodName, args).GetValue(args);
		}

		public static T InvokeMethod<T>(object instance, string methodName)
		{
			return Traverse.Create(instance).Method(methodName, Array.Empty<object>()).GetValue<T>();
		}

		public static void InvokeMethod(object instance, string methodName)
		{
			Traverse.Create(instance).Method(methodName, Array.Empty<object>()).GetValue();
		}

		public static TType GetStaticField<TClass, TType>(string fieldName)
		{
			return Traverse.Create<TClass>().Field<TType>(fieldName).Value;
		}

		public static TType SetStaticField<TClass, TType>(string fieldName, TType value)
		{
			return Traverse.Create<TClass>().Field<TType>(fieldName).Value = value;
		}

		public static T GetField<T>(object instance, string fieldName)
		{
			return Traverse.Create(instance).Field<T>(fieldName).Value;
		}

		public static void SetField<T>(object instance, string fieldName, T value)
		{
			Traverse.Create(instance).Field<T>(fieldName).Value = value;
		}
	}
	public class SpriteLoader
	{
		private static readonly Dictionary<string, Texture2D> TextureCache;

		private Logger _logger;

		static SpriteLoader()
		{
			TextureCache = new Dictionary<string, Texture2D>();
		}

		private static Sprite CreateSprite(Texture2D texture, int width, int height)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			return Sprite.Create(texture, new Rect(0f, 0f, (float)width, (float)height), Vector2.zero);
		}

		public static string GetTextureFileName(Sprite sprite)
		{
			using (IEnumerator<KeyValuePair<string, Texture2D>> enumerator = TextureCache.Where((KeyValuePair<string, Texture2D> pair) => (Object)(object)pair.Value == (Object)(object)sprite.texture).GetEnumerator())
			{
				if (enumerator.MoveNext())
				{
					return Path.GetFileName(enumerator.Current.Key);
				}
			}
			return "";
		}

		public void SetDebugLogger(Logger logger)
		{
			_logger = logger;
		}

		public Sprite Load(string texturePath, int width, int height)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			if (!File.Exists(texturePath))
			{
				return null;
			}
			if (TextureCache.TryGetValue(texturePath, out var value))
			{
				if (!((Object)(object)value != (Object)null))
				{
					return null;
				}
				return CreateSprite(value, width, height);
			}
			try
			{
				_logger?.Info("Try to create sprite: " + texturePath);
				Texture2D val = new Texture2D(0, 0);
				ImageConversion.LoadImage(val, File.ReadAllBytes(texturePath));
				TextureCache.Add(texturePath, val);
				return CreateSprite(val, width, height);
			}
			catch (Exception arg)
			{
				_logger?.Error($"Failed to create sprite: {texturePath}\n{arg}");
				TextureCache.Add(texturePath, null);
				return null;
			}
		}
	}
}
namespace Automatics
{
	[BepInPlugin("net.eidee.valheim.automatics", "Automatics", "1.6.0")]
	public class UnityPlugin : BaseUnityPlugin
	{
		private const string ModId = "net.eidee.valheim.automatics";

		private const string ModName = "Automatics";

		private const string ModVersion = "1.6.0";

		private void Awake()
		{
			Automatics.Initialize((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger);
		}

		private void FixedUpdate()
		{
			if (!((Object)(object)Player.m_localPlayer != (Object)null) && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer())
			{
				Hooks.OnDedicatedServerFixedUpdate?.Invoke(Time.fixedDeltaTime);
			}
		}
	}
	internal static class Automatics
	{
		public const string L10NPrefix = "automatics";

		private static string _modLocation;

		private static string _guid;

		private static List<string> _allResourcesDirectory;

		private static bool _runtimeInitialized;

		public static BaseUnityPlugin Plugin { get; private set; }

		public static Logger Logger { get; private set; }

		public static ManualLogSource LogSource { get; private set; }

		public static L10N L10N { get; private set; }

		private static IEnumerable<string> GetAllResourcesDirectory()
		{
			if (_allResourcesDirectory != null)
			{
				return _allResourcesDirectory;
			}
			_allResourcesDirectory = new List<string> { _modLocation };
			string pluginPath = Paths.PluginPath;
			if (!Directory.Exists(pluginPath))
			{
				return _allResourcesDirectory;
			}
			string[] directories = Directory.GetDirectories(pluginPath);
			foreach (string text in directories)
			{
				if (File.Exists(Path.Combine(text, "automatics-child-mod")))
				{
					_allResourcesDirectory.Add(text);
				}
				else if (File.Exists(Path.Combine(text, "automatics-resources-marker")))
				{
					_allResourcesDirectory.Add(text);
				}
			}
			return _allResourcesDirectory;
		}

		private static void InitializeModules(Assembly assembly)
		{
			foreach (var item2 in from x in ((IEnumerable<Type>)AccessTools.GetTypesFromAssembly(assembly)).SelectMany((Func<Type, IEnumerable<MethodInfo>>)AccessTools.GetDeclaredMethods)
				select (x, x.GetCustomAttributes(typeof(AutomaticsInitializerAttribute), inherit: false).OfType<AutomaticsInitializerAttribute>().FirstOrDefault()) into x
				where x.Item2 != null
				orderby x.Item2.Order
				select x)
			{
				MethodInfo item = item2.Item1;
				try
				{
					item.Invoke(null, new object[0]);
				}
				catch (Exception arg)
				{
					Logger.Error($"Error while initializing {item.Name}\n{arg}");
				}
			}
		}

		public static IEnumerable<string> GetAllResourcePath(string pathname)
		{
			return from x in GetAllResourcesDirectory()
				select Path.Combine(x, pathname);
		}

		public static string GetHarmonyId(string moduleName)
		{
			return _guid + "." + moduleName;
		}

		public static void Initialize(BaseUnityPlugin plugin, ManualLogSource logger)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			Plugin = plugin;
			LogSource = logger;
			Logger = new Logger(logger, Config.IsLogEnabled);
			_modLocation = Path.GetDirectoryName(Plugin.Info.Location) ?? "";
			_guid = Plugin.Info.Metadata.GUID;
			Logger.Debug("Mod location: " + _modLocation);
			ConfigMigration.Migration(Plugin.Config);
			ValheimObject.Initialize(GetAllResourcePath("Data"));
			Harmony val = new Harmony(_guid);
			L10N = new L10N("automatics");
			foreach (string item in GetAllResourcePath("Languages"))
			{
				L10N.AddTranslationDirectory(item);
			}
			L10N.LanguageChanged += InitializeRuntime;
			InitializeRuntime(L10N.SyncCurrentLanguage() ?? "English");
			Hooks.OnInitTerminal = (Action)Delegate.Combine(Hooks.OnInitTerminal, new Action(Commands.Register));
			val.PatchAll(typeof(Patches));
		}

		public static void InitializeRuntime(string language)
		{
			if (_runtimeInitialized)
			{
				ConfigurationManagerBridge.Refresh();
				return;
			}
			Config.Initialize(Plugin.Config);
			Plugin.Config.Save();
			InitializeModules(Assembly.GetExecutingAssembly());
			Plugin.Config.Save();
			ValheimObject.PostInitialize();
			_runtimeInitialized = true;
			ConfigurationManagerBridge.Refresh();
		}
	}
	[AttributeUsage(AttributeTargets.Method)]
	[MeansImplicitUse]
	public class AutomaticsInitializerAttribute : Attribute
	{
		public int Order { get; }

		public AutomaticsInitializerAttribute(int order = 0)
		{
			Order = order;
		}
	}
	internal static class Commands
	{
		public static void Register()
		{
			new ShowCommands().Register();
			new PrintNames().Register();
			new PrintObjects().Register();
			new RemoveMapPins().Register();
		}
	}
	internal static class Config
	{
		private const int NexusID = 1700;

		private static ConfigEntry<bool> _logEnabled;

		private static ConfigEntry<LogLevel> _allowedLogLevel;

		public static bool LogEnabled => _logEnabled.Value;

		public static LogLevel AllowedLogLevel => _allowedLogLevel.Value;

		public static bool Initialized { get; private set; }

		public static Configuration Instance { get; private set; }

		public static bool IsLogEnabled(LogLevel level)
		{
			//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_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			if (Initialized)
			{
				if (LogEnabled)
				{
					return (AllowedLogLevel & level) > 0;
				}
				return false;
			}
			return true;
		}

		public static ConfigEntry<List<ObjectElement>> BindCustomValheimObject(this Configuration config, string key, ValheimObject obj)
		{
			ConfigEntry<List<ObjectElement>> entry = config.Bind(key, new List<ObjectElement>());
			if (entry.Value.Any())
			{
				obj.RegisterCustom(entry.Value);
			}
			entry.SettingChanged += delegate
			{
				obj.RegisterCustom(entry.Value);
			};
			return entry;
		}

		public static ConfigEntry<StringList> BindValheimObjectList(this Configuration config, string key, ValheimObject obj, IEnumerable<string> defaults = null, IEnumerable<string> includes = null, IEnumerable<string> excludes = null)
		{
			List<string> list = new List<string>();
			list.AddRange(defaults ?? (from x in obj.GetAllElements()
				select x.identifier));
			if (includes != null)
			{
				list.RemoveAll((string x) => !includes.Contains(x));
			}
			if (excludes != null)
			{
				list.RemoveAll(excludes.Contains<string>);
			}
			return config.Bind(key, new StringList(list), null, delegate(ConfigurationManagerAttributes x)
			{
				x.CustomDrawer = ConfigurationCustomDrawer.MultiSelect(() => from y in obj.GetAllElements()
					select y.identifier, (string identifier) => (!obj.GetName(identifier, out var name)) ? identifier : Automatics.L10N.TranslateInternalName(name));
			});
		}

		public static void Initialize(ConfigFile config)
		{
			if (!Initialized)
			{
				Instance = new Configuration(config, Automatics.L10N);
				Instance.ChangeSection("hidden");
				Instance.Bind("NexusID", 1700, null, delegate(ConfigurationManagerAttributes x)
				{
					x.Browsable = false;
					x.ReadOnly = true;
				});
				Instance.ChangeSection("system");
				_logEnabled = Instance.Bind("enable_logging", defaultValue: false);
				_allowedLogLevel = Instance.Bind<LogLevel>("log_level_to_allow_logging", (LogLevel)15);
				Instance.ChangeSection("general");
				Instance.BindCustomValheimObject("custom_animal", ValheimObject.Animal);
				Instance.BindCustomValheimObject("custom_dungeon", ValheimObject.Dungeon);
				Instance.BindCustomValheimObject("custom_flora", ValheimObject.Flora);
				Instance.BindCustomValheimObject("custom_mineral", ValheimObject.Mineral);
				Instance.BindCustomValheimObject("custom_monster", ValheimObject.Monster);
				Instance.BindCustomValheimObject("custom_spawner", ValheimObject.Spawner);
				Instance.BindCustomValheimObject("custom_spot", ValheimObject.Spot);
				Initialized = true;
			}
		}
	}
	public enum AutomaticsModule
	{
		[LocalizedDescription("automatics", "@message_module_enabled")]
		Enabled,
		[LocalizedDescription("automatics", "@message_module_disabled")]
		Disabled
	}
	public enum Message
	{
		[LocalizedDescription("automatics", "@message_none")]
		None,
		[LocalizedDescription("automatics", "@message_center")]
		Center,
		[LocalizedDescription("automatics", "@message_top_left")]
		TopLeft
	}
	internal static class ConfigMigration
	{
		private class Config
		{
			public int Offset;

			public string Category;

			public string Key;

			public string Value;
		}

		private delegate bool Operation(string category, List<string> lines, int begin, int end);

		private readonly struct Version : IComparable<Version>
		{
			private readonly int _major;

			private readonly int _minor;

			private readonly int _patch;

			public Version(int major, int minor, int patch)
			{
				_major = major;
				_minor = minor;
				_patch = patch;
			}

			public override string ToString()
			{
				return $"v{_major}.{_minor}.{_patch}";
			}

			public int CompareTo(Version other)
			{
				if (_major != other._major)
				{
					return _major.CompareTo(other._major);
				}
				if (_minor == other._minor)
				{
					return _patch.CompareTo(other._patch);
				}
				return _minor.CompareTo(other._minor);
			}

			public static bool operator >(Version a, Version b)
			{
				return a.CompareTo(b) > 0;
			}

			public static bool operator <(Version a, Version b)
			{
				return a.CompareTo(b) < 0;
			}
		}

		private static readonly Regex VersionPattern;

		private static readonly char[] KeyValueSeparator;

		private static readonly Dictionary<string, List<Config>> ConfigCache;

		static ConfigMigration()
		{
			VersionPattern = new Regex("v(\\d+)\\.(\\d+)\\.(\\d+)$", RegexOptions.Compiled);
			KeyValueSeparator = new char[1] { '=' };
			ConfigCache = new Dictionary<string, List<Config>>();
		}

		private static Operation RenameCategory(string newCategory)
		{
			return delegate(string category, List<string> lines, int begin, int end)
			{
				string text = lines[begin];
				lines[begin] = newCategory;
				Automatics.Logger.Message("Rename category: " + text + " => " + newCategory);
				return true;
			};
		}

		private static Operation RemoveConfig(string key)
		{
			return delegate(string category, List<string> lines, int begin, int end)
			{
				foreach (Config item in FindConfig(category, key, lines, begin, end))
				{
					string key2 = item.Key;
					item.Key = "";
					UpdateConfig(item, lines);
					Automatics.Logger.Message("Remove config: " + category + " " + key2);
				}
				return true;
			};
		}

		private static Operation RenameConfig(string key, string newKey)
		{
			return delegate(string category, List<string> lines, int begin, int end)
			{
				foreach (Config item in FindConfig(category, key, lines, begin, end))
				{
					string key2 = item.Key;
					item.Key = (GetMatcher(key).IsRegex ? Regex.Replace(key, key, newKey) : newKey);
					UpdateConfig(item, lines);
					Automatics.Logger.Message("Rename config: " + category + " " + key2 + " => " + item.Key);
				}
				return true;
			};
		}

		private static Operation ReplaceValue(string key, string value, string newValue)
		{
			return delegate(string category, List<string> lines, int begin, int end)
			{
				foreach (Config item in from x in FindConfig(category, key, lines, begin, end)
					where string.Equals(x.Value, value, StringComparison.OrdinalIgnoreCase)
					select x)
				{
					string value2 = item.Value;
					item.Value = newValue;
					UpdateConfig(item, lines);
					Automatics.Logger.Message("Replace value: " + category + " " + key + " " + value2 + " => " + newValue);
				}
				return true;
			};
		}

		private static Operation AppendValues(string key, string[] valuesToAppend)
		{
			return delegate(string category, List<string> lines, int begin, int end)
			{
				List<Config> list = FindConfig(category, key, lines, begin, end).ToList();
				if (!list.Any())
				{
					return false;
				}
				foreach (Config item2 in list)
				{
					string text = item2.Value ?? "";
					List<string> list2 = (string.IsNullOrWhiteSpace(text) ? new List<string>() : (from x in Csv.ParseLine(text)
						where !string.IsNullOrEmpty(x)
						select x).ToList());
					HashSet<string> hashSet = new HashSet<string>(list2, StringComparer.Ordinal);
					List<string> list3 = new List<string>();
					string[] array = valuesToAppend;
					foreach (string text2 in array)
					{
						if (!string.IsNullOrWhiteSpace(text2))
						{
							string item = text2.Trim();
							if (hashSet.Add(item))
							{
								list2.Add(item);
								list3.Add(item);
							}
						}
					}
					if (list3.Count != 0)
					{
						item2.Value = string.Join(", ", list2.Select(Csv.Escape));
						UpdateConfig(item2, lines);
						Automatics.Logger.Message("Append values: " + category + " " + key + " += [" + string.Join(", ", list3) + "]");
					}
				}
				return true;
			};
		}

		private static IEnumerable<Config> FindConfig(string category, string key, List<string> lines, int begin = 0, int end = int.MaxValue)
		{
			(bool IsRegex, string Pattern) matcher = GetMatcher(key);
			if (ConfigCache.TryGetValue(category, out var value))
			{
				return value.Where((Config x) => x.Category == category && IsMatch(x.Key, matcher));
			}
			end = Mathf.Min(end, lines.Count);
			begin = Mathf.Clamp(begin, 0, end);
			value = new List<Config>(0);
			string text = "";
			int i;
			for (i = begin; i < end; i++)
			{
				string text2 = lines[i];
				if (text2.StartsWith("#"))
				{
					continue;
				}
				if (Regex.IsMatch(text2, "^\\[[\\w\\d_]+\\]$"))
				{
					text = text2;
					if (!ConfigCache.TryGetValue(text, out value))
					{
						value = new List<Config>();
						ConfigCache[text] = value;
					}
				}
				else if (!value.Any((Config x) => x.Offset == i))
				{
					string[] array = text2.Split(KeyValueSeparator, 2, StringSplitOptions.None);
					if (array.Length == 2)
					{
						Config item = new Config
						{
							Offset = i,
							Category = text,
							Key = array[0].Trim(),
							Value = array[1].Trim()
						};
						value.Add(item);
					}
				}
			}
			if (ConfigCache.TryGetValue(category, out value))
			{
				return value.Where((Config x) => x.Category == category && IsMatch(x.Key, matcher));
			}
			Automatics.Logger.Warning("No config matching condition exists: " + category + " / " + key);
			return Array.Empty<Config>();
		}

		private static void UpdateConfig(Config config, List<string> lines)
		{
			if (config.Offset >= 0 || config.Offset < lines.Count)
			{
				if (string.IsNullOrEmpty(config.Key))
				{
					lines[config.Offset] = "";
				}
				else
				{
					lines[config.Offset] = config.Key + " = " + config.Value;
				}
			}
		}

		private static (bool IsRegex, string Pattern) GetMatcher(string pattern)
		{
			if (string.IsNullOrEmpty(pattern))
			{
				return (false, "");
			}
			if (!pattern.StartsWith("r/"))
			{
				return (false, pattern);
			}
			return (true, pattern.Substring(2));
		}

		private static bool IsMatch(string value, (bool IsRegex, string Pattern) matcher)
		{
			if (!matcher.IsRegex)
			{
				if (!string.IsNullOrEmpty(matcher.Pattern))
				{
					return string.Equals(value, matcher.Pattern, StringComparison.OrdinalIgnoreCase);
				}
				return false;
			}
			return Regex.IsMatch(value, matcher.Pattern, RegexOptions.IgnoreCase);
		}

		public static void Migration(ConfigFile config)
		{
			string configFilePath = config.ConfigFilePath;
			if (!File.Exists(configFilePath))
			{
				return;
			}
			List<string> list = File.ReadAllLines(configFilePath).ToList();
			if (list.Any())
			{
				bool flag = false;
				Version version = ParseVersion(list[0]);
				Version version2 = new Version(1, 3, 0);
				if (version < version2)
				{
					Automatics.Logger.Message($"Migrating config from {version} to {version2}");
					flag = true;
					MigrationFor130(list);
				}
				version2 = new Version(1, 4, 0);
				if (version < version2)
				{
					Automatics.Logger.Message($"Migrating config from {version} to {version2}");
					flag = true;
					MigrationFor140(list);
				}
				version2 = new Version(1, 4, 5);
				if (version < version2)
				{
					Automatics.Logger.Message($"Migrating config from {version} to {version2}");
					flag = true;
					MigrationFor145(list);
				}
				version2 = new Version(1, 6, 0);
				if (version < version2)
				{
					Automatics.Logger.Message($"Migrating config from {version} to {version2}");
					flag = true;
					MigrationFor160(list);
				}
				if (flag)
				{
					File.WriteAllText(configFilePath, string.Join(Environment.NewLine, list), Encoding.UTF8);
					config.Reload();
				}
			}
		}

		private static void MigrationFor130(List<string> lines)
		{
			Migration(lines, new Dictionary<string, List<Operation>>
			{
				{
					"[logging]",
					new List<Operation>
					{
						RenameCategory("[system]"),
						RenameConfig("logging_enabled", "enable_logging"),
						RenameConfig("allowed_log_level", "log_level_to_allow_logging")
					}
				},
				{
					"[automatic_door]",
					new List<Operation>
					{
						RenameConfig("automatic_door_enabled", "enable_automatic_door"),
						RenameConfig("player_search_radius_to_open", "distance_for_automatic_opening"),
						RenameConfig("player_search_radius_to_close", "distance_for_automatic_closing"),
						RenameConfig("toggle_automatic_door_enabled_key", "automatic_door_enable_disable_toggle")
					}
				},
				{
					"[automatic_map_pinning]",
					new List<Operation>
					{
						RenameCategory("[automatic_mapping]"),
						RenameConfig("automatic_map_pinning_enabled", "enable_automatic_mapping"),
						RenameConfig("allow_pinning_vein", "allow_pinning_deposit"),
						RenameConfig("allow_pinning_vein_custom", "allow_pinning_deposit_custom"),
						RenameConfig("ignore_tamed_animals", "not_pinning_tamed_animals"),
						RenameConfig("in_ground_veins_need_wishbone", "need_to_equip_wishbone_for_underground_deposits")
					}
				},
				{
					"[automatic_processing]",
					new List<Operation>
					{
						RenameConfig("automatic_processing_enabled", "enable_automatic_processing"),
						RenameConfig("r/^([\\w_]+)_allow_automatic_processing", "allow_processing_by_$1"),
						RenameConfig("r/^([\\w_]+)_container_search_range", "container_search_range_by_$1"),
						RenameConfig("r/^([\\w_]+)_material_count_that_suppress_automatic_process", "$1_material_count_of_suppress_processing"),
						RenameConfig("r/^([\\w_]+)_fuel_count_that_suppress_automatic_process", "$1_fuel_count_of_suppress_processing"),
						RenameConfig("r/^([\\w_]+)_product_count_that_suppress_automatic_store", "$1_product_count_of_suppress_processing")
					}
				},
				{
					"[automatic_feeding]",
					new List<Operation>
					{
						RenameConfig("automatic_feeding_enabled", "enable_automatic_feeding"),
						RenameConfig("need_close_to_eat_the_feed", "need_get_close_to_eat_the_feed"),
						RenameConfig("automatic_repair_enabled", "enable_automatic_repair")
					}
				}
			});
		}

		private static void MigrationFor140(List<string> lines)
		{
			Migration(lines, new Dictionary<string, List<Operation>>
			{
				{
					"[automatic_door]",
					new List<Operation>
					{
						RemoveConfig("allow_automatic_door_custom"),
						ReplaceValue("allow_automatic_door", "All", "WoodDoor, WoodGate, IronGate, DarkwoodGate, WoodShutter")
					}
				},
				{
					"[automatic_mapping]",
					new List<Operation>
					{
						RenameConfig("dynamic_object_search_range", "dynamic_object_mapping_range"),
						RenameConfig("static_object_search_range", "static_object_mapping_range"),
						RenameConfig("location_search_range", "location_mapping_range"),
						RenameConfig("allow_pinning_deposit", "allow_pinning_mineral"),
						RemoveConfig("allow_pinning_ship"),
						RemoveConfig("r/^allow_pinning_[\\w]+_custom$"),
						RenameConfig("static_object_search_interval", "static_object_mapping_interval"),
						RenameConfig("need_to_equip_wishbone_for_underground_deposits", "need_to_equip_wishbone_for_underground_minerals"),
						RenameConfig("static_object_search_key", "static_object_mapping_key"),
						ReplaceValue("allow_pinning_animal", "All", "Boar, Piggy, Deer, Wolf, WolfCub, Lox, LoxCalf, Hen, Chicken, Hare, Bird, Fish"),
						ReplaceValue("allow_pinning_monster", "All", "Greyling, Neck, Ghost, Greydwarf, GreydwarfBrute, GreydwarfShaman, RancidRemains, Skeleton, Troll, Abomination, Blob, Draugr, DraugrElite, Leech, Oozer, Surtling, Wraith, Drake, Fenring, StoneGolem, Deathsquito, Fuling, FulingBerserker, FulingShaman, Growth, Serpent, Bat, FenringCultist, Ulv, DvergrRogue, DvergrMage, Tick, Seeker, SeekerBrood, Gjall, SeekerSoldier"),
						ReplaceValue("allow_pinning_flora", "All", "Dandelion, Mushroom, Raspberries, Blueberries, Carrot, CarrotSeeds, YellowMushroom, Thistle, Turnip, TurnipSeeds, Onion, OnionSeeds, Barley, Cloudberries, Flex, JotunPuffs, Magecap"),
						ReplaceValue("allow_pinning_mineral", "All", "CopperDeposit, TinDeposit, MudPile, ObsidianDeposit, SilverVein, PetrifiedBone, SoftTissue"),
						ReplaceValue("allow_pinning_spawner", "All", "GreydwarfNest, EvilBonePile, BodyPile"),
						ReplaceValue("allow_pinning_other", "All", "Vegvisir, Runestone, WildBeehive"),
						ReplaceValue("allow_pinning_dungeon", "All", "BurialChambers, TrollCave, SunkenCrypts, MountainCave, InfestedMine"),
						ReplaceValue("allow_pinning_spot", "All", "InfestedTree, FireHole, DrakeNest, GoblinCamp, TarPit, DvergrExcavation, DvergrGuardTower, DvergrHarbour, DvergrLighthouse, PetrifiedBone")
					}
				},
				{
					"[automatic_processing]",
					new List<Operation> { RemoveConfig("r/^([\\w_]+)_product_count_of_suppress_processing") }
				}
			});
		}

		private static void MigrationFor145(List<string> lines)
		{
			Migration(lines, new Dictionary<string, List<Operation>> { 
			{
				"[automatic_processing]",
				new List<Operation> { ReplaceValue("r/^allow_processing_by", "All", "Craft, Refuel, Store") }
			} });
		}

		private static void MigrationFor160(List<string> lines)
		{
			Migration(lines, new Dictionary<string, List<Operation>>
			{
				{
					"[automatic_mapping]",
					new List<Operation>
					{
						AppendValues("allow_pinning_monster", new string[15]
						{
							"CharredMelee", "CharredArcher", "CharredMage", "CharredTwitcher", "CharredGrunt", "Morgen", "Volture", "BonemawSerpent", "FallenValkyrie", "Fader",
							"SkeletonFire", "FrostBlob", "Bear", "Vile", "LavaBlob"
						}),
						AppendValues("allow_pinning_animal", new string[2] { "Asksvin", "AsksvinHatchling" }),
						AppendValues("allow_pinning_flora", new string[4] { "Fiddlehead", "SmokePuff", "Vineberry", "VineberrySeeds" }),
						AppendValues("allow_pinning_mineral", new string[1] { "FlametalDeposit" }),
						AppendValues("allow_pinning_vehicle", new string[1] { "Drakkar" }),
						AppendValues("allow_pinning_spot", new string[3] { "CharredFortress", "LeviathanLava", "BogWitchCamp" }),
						AppendValues("allow_pinning_dungeon", new string[1] { "MorgenHole" })
					}
				},
				{
					"[automatic_mining]",
					new List<Operation> { AppendValues("allow_mining_mineral", new string[1] { "FlametalDeposit" }) }
				},
				{
					"[automatic_door]",
					new List<Operation> { AppendValues("allow_automatic_door", new string[3] { "AshwoodDoor", "FlametalGate", "DvergrDoor" }) }
				},
				{
					"[automatic_processing]",
					new List<Operation> { AppendValues("allow_container", new string[1] { "PieceChestCharred" }) }
				}
			});
		}

		private static void Migration(List<string> lines, Dictionary<string, List<Operation>> operations)
		{
			string category = "";
			int begin = -1;
			int i;
			List<Operation> value;
			for (i = 0; i < lines.Count; i++)
			{
				string text = lines[i];
				if (text.StartsWith("#") || string.I