Decompiled source of LethalEditor v1.2.2

LethalEditor.dll

Decompiled 10 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using LethalEditor.Configuration;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LethalEditor")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a50a4b9309fff851727ceb7264988c601eba2a71")]
[assembly: AssemblyProduct("LethalEditor")]
[assembly: AssemblyTitle("LethalEditor")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Kyxino/LethalEditor")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LethalEditor
{
	internal class Utils
	{
		public static string MakeFileNameSafe(string FileName)
		{
			return Regex.Replace(FileName, "[\\/:*?\"<>|]", "");
		}

		public static void AddAllTypes()
		{
			TomlTypeConverter.AddConverter(typeof(AnimationCurve), (TypeConverter)(object)new AnimationCurve_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(string[]), (TypeConverter)(object)new StringArray_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(Battery), (TypeConverter)(object)new Battery_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(IntWithRarity[]), (TypeConverter)(object)new IntWithRarityArray_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(CompatibleNoun[]), (TypeConverter)(object)new CompatibleNounArray_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(TerminalKeyword), (TypeConverter)(object)new TerminalKeyword_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(TerminalNode), (TypeConverter)(object)new TerminalNode_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(SpawnableEnemyWithRarity[]), (TypeConverter)(object)new SpawnableEnemyWithRarityArray_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(SpawnableItemWithRarity[]), (TypeConverter)(object)new SpawnableItemWithRarityArray_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(List<SpawnableEnemyWithRarity>), (TypeConverter)(object)new SpawnableEnemyWithRarityList_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(List<SpawnableItemWithRarity>), (TypeConverter)(object)new SpawnableItemWithRarityList_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(EnemyType), (TypeConverter)(object)new EnemyType_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(List<ItemGroup>), (TypeConverter)(object)new ItemGroupList_TypeConverter());
			TomlTypeConverter.AddConverter(typeof(RandomWeatherWithVariables[]), (TypeConverter)(object)new RandomWeatherWithVariablesArray_TypeConverter());
		}
	}
	internal class AnimationCurve_TypeConverter : TypeConverter
	{
		public AnimationCurve_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Expected O, but got Unknown
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0051: Expected O, but got Unknown
				//IL_0321: Unknown result type (might be due to invalid IL or missing references)
				//IL_0328: Expected O, but got Unknown
				//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
				//IL_0302: Unknown result type (might be due to invalid IL or missing references)
				//IL_0304: Unknown result type (might be due to invalid IL or missing references)
				if (type != typeof(AnimationCurve))
				{
					return (object)new AnimationCurve();
				}
				string text = Regex.Replace(input, "[\\s%()%[\\]]+", "");
				string[] array2 = text.Split(',');
				if (string.IsNullOrEmpty(array2[0]))
				{
					return (object)new AnimationCurve();
				}
				Keyframe[] array3 = (Keyframe[])(object)new Keyframe[array2.Length];
				Keyframe val2 = default(Keyframe);
				for (int j = 0; j < array3.Length; j++)
				{
					string[] array4 = array2[j].Split(';');
					string[] array5 = ((array4 != null && array4.Length >= 1) ? ((array4 != null) ? array4[0]?.Split(':') : null) : null);
					string[] array6 = ((array4 != null && array4.Length >= 2) ? ((array4 != null) ? array4[1]?.Split('|') : null) : null);
					string[] array7 = ((array4 != null && array4.Length >= 3) ? ((array4 != null) ? array4[2]?.Split(':') : null) : null);
					string[] array8 = ((array7 != null && array7.Length >= 2) ? ((array7 != null) ? array7[1] : null) : ((array7 != null) ? array7[0] : null))?.Split('|');
					float result = (float.TryParse(((array5 != null) ? array5[0] : null) ?? "0", NumberStyles.Float, new CultureInfo("en-US"), out result) ? result : 0f);
					float result2 = (float.TryParse((array5 != null && array5.Length >= 2) ? ((array5 != null) ? array5[1] : null) : "0", NumberStyles.Float, new CultureInfo("en-US"), out result2) ? result2 : 0f);
					float result3 = (float.TryParse(((array6 != null) ? array6[0] : null) ?? "0", NumberStyles.Float, new CultureInfo("en-US"), out result3) ? result3 : 0f);
					float result4 = (float.TryParse((array6 != null && array6.Length >= 2) ? ((array6 != null) ? array6[1] : null) : (((array6 != null) ? array6[0] : null) ?? "0"), NumberStyles.Float, new CultureInfo("en-US"), out result4) ? result4 : 0f);
					float result5 = (float.TryParse(((array8 != null) ? array8[0] : null) ?? "0", NumberStyles.Float, new CultureInfo("en-US"), out result5) ? result5 : 0f);
					float result6 = (float.TryParse((array8 != null && array8.Length >= 2) ? ((array8 != null) ? array8[1] : null) : (((array8 != null) ? array8[0] : null) ?? "0"), NumberStyles.Float, new CultureInfo("en-US"), out result6) ? result6 : 0f);
					int result7 = (int.TryParse(((array7 != null) ? array7[0] : null) ?? "0", NumberStyles.Float, new CultureInfo("en-US"), out result7) ? result7 : 0);
					((Keyframe)(ref val2))..ctor(result, result2, result3, result4, result5, result6);
					((Keyframe)(ref val2)).weightedMode = (WeightedMode)result7;
					Keyframe val3 = val2;
					array3[j] = val3;
				}
				return (object)new AnimationCurve(array3);
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0089: Unknown result type (might be due to invalid IL or missing references)
				//IL_008e: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d0: Expected I4, but got Unknown
				if (type != typeof(AnimationCurve))
				{
					return "";
				}
				Keyframe[] keys = ((AnimationCurve)input).keys;
				if (keys.Length == 0)
				{
					return "";
				}
				if (keys.Length == 1)
				{
					return ((Keyframe)(ref keys[0])).value.ToString(new CultureInfo("en-US"));
				}
				StringBuilder stringBuilder = new StringBuilder();
				Keyframe[] array = keys;
				for (int i = 0; i < array.Length; i++)
				{
					Keyframe val = array[i];
					float time = ((Keyframe)(ref val)).time;
					float value = ((Keyframe)(ref val)).value;
					float inTangent = ((Keyframe)(ref val)).inTangent;
					float outTangent = ((Keyframe)(ref val)).outTangent;
					float inWeight = ((Keyframe)(ref val)).inWeight;
					float outWeight = ((Keyframe)(ref val)).outWeight;
					int num = (int)((Keyframe)(ref val)).weightedMode;
					stringBuilder.Append(time.ToString(new CultureInfo("en-US")) + ":" + value.ToString(new CultureInfo("en-US")));
					if (inTangent != 0f || outTangent != 0f)
					{
						stringBuilder.Append((inTangent == outTangent) ? $";({inTangent})" : $";({inTangent}|{outTangent})");
						if (inWeight != 0f || outWeight != 0f)
						{
							stringBuilder.Append((num != 0) ? (";" + num.ToString(new CultureInfo("en-US")) + ":") : ";");
							stringBuilder.Append((inWeight == outWeight) ? ("[" + inWeight.ToString(new CultureInfo("en-US")) + "]") : ("[" + inWeight.ToString(new CultureInfo("en-US")) + "|" + outWeight.ToString(new CultureInfo("en-US")) + "]"));
						}
						else if (num != 0)
						{
							stringBuilder.Append(";" + num.ToString(new CultureInfo("en-US")));
						}
					}
					stringBuilder.Append(", ");
				}
				return stringBuilder.ToString(0, Math.Max(0, stringBuilder.Length - 2));
			};
		}
	}
	internal class StringArray_TypeConverter : TypeConverter
	{
		public StringArray_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(string[])) ? new string[0] : JsonConvert.DeserializeObject<string[]>(input);
			((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(string[])) ? "[]" : JsonConvert.SerializeObject(input);
		}
	}
	internal class Battery_TypeConverter : TypeConverter
	{
		public Battery_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(Battery)) ? ((object)new Battery(false, 1f)) : ((object)JsonConvert.DeserializeObject<Battery>(input));
			((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(Battery)) ? JsonConvert.SerializeObject((object)new Battery(false, 1f)) : JsonConvert.SerializeObject(input);
		}
	}
	internal class IntWithRarityArray_TypeConverter : TypeConverter
	{
		public IntWithRarityArray_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				//IL_0040: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_0064: Expected O, but got Unknown
				if (type != typeof(IntWithRarity[]))
				{
					return new IntWithRarity[0];
				}
				Dictionary<int, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<int, int>>(input);
				List<IntWithRarity> list = new List<IntWithRarity>();
				foreach (KeyValuePair<int, int> item in dictionary2)
				{
					list.Add(new IntWithRarity
					{
						id = item.Key,
						rarity = item.Value
					});
				}
				return list.ToArray();
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				if (type != typeof(IntWithRarity[]))
				{
					return "{}";
				}
				Dictionary<int, int> dictionary = new Dictionary<int, int>();
				IntWithRarity[] array = (IntWithRarity[])input;
				foreach (IntWithRarity val in array)
				{
					dictionary.Add(val.id, val.rarity);
				}
				return JsonConvert.SerializeObject((object)dictionary);
			};
		}
	}
	internal class CompatibleNounArray_TypeConverter : TypeConverter
	{
		public CompatibleNounArray_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				//IL_004f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				//IL_009d: Expected O, but got Unknown
				if (type != typeof(CompatibleNoun[]))
				{
					return new CompatibleNoun[0];
				}
				Dictionary<string, string> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, string>>(input);
				List<CompatibleNoun> list = new List<CompatibleNoun>();
				foreach (KeyValuePair<string, string> PAIR in dictionary2)
				{
					list.Add(new CompatibleNoun
					{
						noun = (from X in Resources.FindObjectsOfTypeAll<TerminalKeyword>()
							where Utils.MakeFileNameSafe(((Object)X).name) == PAIR.Key
							select X).FirstOrDefault(),
						result = (from X in Resources.FindObjectsOfTypeAll<TerminalNode>()
							where Utils.MakeFileNameSafe(((Object)X).name) == PAIR.Value
							select X).FirstOrDefault()
					});
				}
				return list.ToArray();
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				if (type != typeof(CompatibleNoun[]))
				{
					return "{}";
				}
				Dictionary<string, string> NewOptions = new Dictionary<string, string>();
				foreach (CompatibleNoun item in ((CompatibleNoun[])input).Where((CompatibleNoun x) => (Object)(object)x.noun != (Object)null && !NewOptions.ContainsKey(((Object)x.noun).name)))
				{
					Dictionary<string, string> dictionary = NewOptions;
					string name = ((Object)item.noun).name;
					TerminalNode result = item.result;
					dictionary.Add(name, ((result != null) ? ((Object)result).name : null) ?? "");
				}
				return JsonConvert.SerializeObject((object)NewOptions);
			};
		}
	}
	internal class TerminalKeyword_TypeConverter : TypeConverter
	{
		public TerminalKeyword_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(TerminalKeyword)) ? ((object)ScriptableObject.CreateInstance<TerminalKeyword>()) : ((object)(from X in Resources.FindObjectsOfTypeAll<TerminalKeyword>()
				where Utils.MakeFileNameSafe(((Object)X).name) == input
				select X).FirstOrDefault());
			((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(TerminalKeyword)) ? "" : ((Object)(TerminalKeyword)input).name;
		}
	}
	internal class TerminalNode_TypeConverter : TypeConverter
	{
		public TerminalNode_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(TerminalNode)) ? ((object)ScriptableObject.CreateInstance<TerminalNode>()) : ((object)(from X in Resources.FindObjectsOfTypeAll<TerminalNode>()
				where Utils.MakeFileNameSafe(((Object)X).name) == input
				select X).FirstOrDefault());
			((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(TerminalNode)) ? "" : ((Object)(TerminalNode)input).name;
		}
	}
	internal class SpawnableEnemyWithRarityArray_TypeConverter : TypeConverter
	{
		public SpawnableEnemyWithRarityArray_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_0098: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Expected O, but got Unknown
				if (type != typeof(SpawnableEnemyWithRarity[]))
				{
					return new SpawnableEnemyWithRarity[0];
				}
				Dictionary<string, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, int>>(input);
				List<SpawnableEnemyWithRarity> list = new List<SpawnableEnemyWithRarity>();
				foreach (KeyValuePair<string, int> PAIR in dictionary2)
				{
					EnemyType val2 = (from X in Resources.FindObjectsOfTypeAll<EnemyType>()
						where Utils.MakeFileNameSafe(X.enemyName) == PAIR.Key
						select X).FirstOrDefault();
					if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)null))
					{
						list.Add(new SpawnableEnemyWithRarity
						{
							enemyType = val2,
							rarity = PAIR.Value
						});
					}
				}
				return list.ToArray();
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				if (type != typeof(SpawnableEnemyWithRarity[]))
				{
					return "{}";
				}
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				SpawnableEnemyWithRarity[] array = (SpawnableEnemyWithRarity[])input;
				foreach (SpawnableEnemyWithRarity val in array)
				{
					string text = val.enemyType?.enemyName;
					if (text != null && text != null)
					{
						if (dictionary.ContainsKey(text))
						{
							dictionary[text] += val.rarity;
						}
						else
						{
							dictionary.Add(text, val.rarity);
						}
					}
				}
				return JsonConvert.SerializeObject((object)dictionary);
			};
		}
	}
	internal class SpawnableItemWithRarityArray_TypeConverter : TypeConverter
	{
		public SpawnableItemWithRarityArray_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_0098: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Expected O, but got Unknown
				if (type != typeof(SpawnableItemWithRarity[]))
				{
					return new SpawnableItemWithRarity[0];
				}
				Dictionary<string, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, int>>(input);
				List<SpawnableItemWithRarity> list = new List<SpawnableItemWithRarity>();
				foreach (KeyValuePair<string, int> PAIR in dictionary2)
				{
					Item val2 = (from X in Resources.FindObjectsOfTypeAll<Item>()
						where Utils.MakeFileNameSafe(X.itemName) == PAIR.Key
						select X).FirstOrDefault();
					if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)null))
					{
						list.Add(new SpawnableItemWithRarity
						{
							spawnableItem = val2,
							rarity = PAIR.Value
						});
					}
				}
				return list.ToArray();
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				if (type != typeof(SpawnableItemWithRarity[]))
				{
					return "{}";
				}
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				SpawnableItemWithRarity[] array = (SpawnableItemWithRarity[])input;
				foreach (SpawnableItemWithRarity val in array)
				{
					string text = val.spawnableItem?.itemName;
					if (text != null && text != null)
					{
						if (dictionary.ContainsKey(text))
						{
							dictionary[text] += val.rarity;
						}
						else
						{
							dictionary.Add(text, val.rarity);
						}
					}
				}
				return JsonConvert.SerializeObject((object)dictionary);
			};
		}
	}
	internal class SpawnableEnemyWithRarityList_TypeConverter : TypeConverter
	{
		public SpawnableEnemyWithRarityList_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Expected O, but got Unknown
				if (type != typeof(List<SpawnableEnemyWithRarity>))
				{
					return new List<SpawnableEnemyWithRarity>();
				}
				Dictionary<string, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, int>>(input);
				List<SpawnableEnemyWithRarity> list = new List<SpawnableEnemyWithRarity>();
				foreach (KeyValuePair<string, int> PAIR in dictionary2)
				{
					EnemyType val = (from X in Resources.FindObjectsOfTypeAll<EnemyType>()
						where Utils.MakeFileNameSafe(X.enemyName) == PAIR.Key
						select X).FirstOrDefault();
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)null))
					{
						list.Add(new SpawnableEnemyWithRarity
						{
							enemyType = val,
							rarity = PAIR.Value
						});
					}
				}
				return list;
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				if (type != typeof(List<SpawnableEnemyWithRarity>))
				{
					return "{}";
				}
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				foreach (SpawnableEnemyWithRarity item in (List<SpawnableEnemyWithRarity>)input)
				{
					string text = item.enemyType?.enemyName;
					if (text != null && text != null)
					{
						if (dictionary.ContainsKey(text))
						{
							dictionary[text] += item.rarity;
						}
						else
						{
							dictionary.Add(text, item.rarity);
						}
					}
				}
				return JsonConvert.SerializeObject((object)dictionary);
			};
		}
	}
	internal class SpawnableItemWithRarityList_TypeConverter : TypeConverter
	{
		public SpawnableItemWithRarityList_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Expected O, but got Unknown
				if (type != typeof(List<SpawnableItemWithRarity>))
				{
					return new List<SpawnableItemWithRarity>();
				}
				Dictionary<string, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, int>>(input);
				List<SpawnableItemWithRarity> list = new List<SpawnableItemWithRarity>();
				foreach (KeyValuePair<string, int> PAIR in dictionary2)
				{
					Item val = (from X in Resources.FindObjectsOfTypeAll<Item>()
						where Utils.MakeFileNameSafe(X.itemName) == PAIR.Key
						select X).FirstOrDefault();
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)null))
					{
						list.Add(new SpawnableItemWithRarity
						{
							spawnableItem = val,
							rarity = PAIR.Value
						});
					}
				}
				return list;
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				if (type != typeof(List<SpawnableItemWithRarity>))
				{
					return "{}";
				}
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				foreach (SpawnableItemWithRarity item in (List<SpawnableItemWithRarity>)input)
				{
					string text = item.spawnableItem?.itemName;
					if (text != null && text != null)
					{
						if (dictionary.ContainsKey(text))
						{
							dictionary[text] += item.rarity;
						}
						else
						{
							dictionary.Add(text, item.rarity);
						}
					}
				}
				return JsonConvert.SerializeObject((object)dictionary);
			};
		}
	}
	internal class EnemyType_TypeConverter : TypeConverter
	{
		public EnemyType_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(EnemyType)) ? ((object)new EnemyType()) : ((object)(from X in Resources.FindObjectsOfTypeAll<EnemyType>()
				where Utils.MakeFileNameSafe(X.enemyName) == input
				select X).FirstOrDefault());
			((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(EnemyType)) ? "" : ((EnemyType)input).enemyName;
		}
	}
	internal class ItemGroupList_TypeConverter : TypeConverter
	{
		public ItemGroupList_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				if (type != typeof(List<ItemGroup>))
				{
					return new List<ItemGroup>();
				}
				List<ItemGroup> list2 = new List<ItemGroup>();
				foreach (string item in JsonConvert.DeserializeObject<List<string>>(input))
				{
					ItemGroup val = ScriptableObject.CreateInstance<ItemGroup>();
					((Object)val).name = item;
					list2.Add(val);
				}
				return list2;
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				if (type != typeof(List<ItemGroup>))
				{
					return "{}";
				}
				List<string> list = new List<string>();
				foreach (ItemGroup item2 in (List<ItemGroup>)input)
				{
					list.Add(((Object)item2).name);
				}
				return JsonConvert.SerializeObject((object)list);
			};
		}
	}
	internal class RandomWeatherWithVariablesArray_TypeConverter : TypeConverter
	{
		public RandomWeatherWithVariablesArray_TypeConverter()
		{
			((TypeConverter)this).ConvertToObject = delegate(string input, Type type)
			{
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				//IL_005c: Unknown result type (might be due to invalid IL or missing references)
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_0088: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Expected O, but got Unknown
				//IL_00b2: Expected O, but got I4
				//IL_005e->IL005e: Incompatible stack types: O vs I4
				//IL_005c->IL005e: Incompatible stack types: I4 vs O
				//IL_005c->IL005e: Incompatible stack types: O vs I4
				if (type != typeof(RandomWeatherWithVariables[]))
				{
					return new RandomWeatherWithVariables[0];
				}
				Dictionary<string, List<int>> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, List<int>>>(input);
				List<RandomWeatherWithVariables> list = new List<RandomWeatherWithVariables>();
				foreach (KeyValuePair<string, List<int>> item in dictionary2)
				{
					object obj = list;
					? val2 = new RandomWeatherWithVariables();
					val2 = val2;
					int num;
					if (!Enum.TryParse<LevelWeatherType>(item.Key, out LevelWeatherType result))
					{
						num = -1;
						obj = num;
						num = (int)obj;
					}
					else
					{
						obj = result;
						num = (int)obj;
					}
					((RandomWeatherWithVariables)val2).weatherType = (LevelWeatherType)obj;
					((RandomWeatherWithVariables)val2).weatherVariable = ((item.Value.Count > 0) ? item.Value[0] : 0);
					((RandomWeatherWithVariables)val2).weatherVariable2 = ((item.Value.Count > 1) ? item.Value[1] : 0);
					((List<RandomWeatherWithVariables>)num).Add((RandomWeatherWithVariables)val2);
				}
				return list.ToArray();
			};
			((TypeConverter)this).ConvertToString = delegate(object input, Type type)
			{
				if (type != typeof(RandomWeatherWithVariables[]))
				{
					return "{}";
				}
				Dictionary<string, List<int>> dictionary = new Dictionary<string, List<int>>();
				RandomWeatherWithVariables[] array = (RandomWeatherWithVariables[])input;
				foreach (RandomWeatherWithVariables val in array)
				{
					dictionary.Add(((object)(LevelWeatherType)(ref val.weatherType)).ToString(), new List<int>(2) { val.weatherVariable, val.weatherVariable2 });
				}
				return JsonConvert.SerializeObject((object)dictionary);
			};
		}
	}
	[Flags]
	public enum ConfigBehavior
	{
		DoNotRun = 0,
		ApplyExisting = 1,
		CreateNewFiles = 2
	}
	[BepInPlugin("Kyxino.LethalEditor", "LethalEditor", "1.2.2")]
	public class EditorMod : BaseUnityPlugin
	{
		public static ManualLogSource MLS = Logger.CreateLogSource("Kyxino.LethalEditor");

		public static DirectoryInfo MainDir = Directory.CreateDirectory(Paths.BepInExRootPath + "\\LethalEditor");

		public static ConfigFile LoadConfig = new ConfigFile(MainDir.FullName + "\\EditorSettings.cfg", saveOnInit: true, MetadataHelper.GetMetadata(typeof(EditorMod)));

		public static ConfigEntry<bool> IgnoreOverlap = LoadConfig.Bind("Settings", "IgnoreOverlap", defaultValue: false, "When set to true, this WILL cause DefaultValues to be overwritten by non-Vanilla Values, but make Value more certain to apply.", 0);

		public static ConfigBehavior BehaviorGrabbables = LoadConfig.Bind("Item Config Creation", "Grabbables", ConfigBehavior.ApplyExisting, "CreateNewFiles will allow files to be generated.\nApplyExisting allows existing files to run and potentially apply their settings.\nCreateNewFiles, ApplyExisting does both.\nBehavior for all GrabbableObjects configs.", 1).Value;

		public static ConfigBehavior BehaviorItems = LoadConfig.Bind("Item Config Creation", "Items", ConfigBehavior.ApplyExisting, "Behavior for all Item configs.", 1).Value;

		public static ConfigBehavior BehaviorSpecialItems = LoadConfig.Bind("Item Config Creation", "SpecialItems", ConfigBehavior.ApplyExisting, "Behavior for all special item classes configs.", 1).Value;

		public static ConfigBehavior BehaviorEnemyAIs = LoadConfig.Bind("Enemy Config Creation", "EnemyAIs", ConfigBehavior.ApplyExisting, "Behavior for all EnemyAI configs.", 2).Value;

		public static ConfigBehavior BehaviorEnemyTypes = LoadConfig.Bind("Enemy Config Creation", "EnemyTypes", ConfigBehavior.ApplyExisting, "Behavior for all EnemyType configs.", 2).Value;

		public static ConfigBehavior BehaviorSpecialEnemies = LoadConfig.Bind("Enemy Config Creation", "SpecialEnemies", ConfigBehavior.ApplyExisting, "Behavior for all special enemy classes configs.", 2).Value;

		public static ConfigBehavior BehaviorLevels = LoadConfig.Bind("Misc Config Creation", "Levels", ConfigBehavior.ApplyExisting, "Behavior for all SelectableLevel configs.").Value;

		public static ConfigBehavior BehaviorUnlockableItems = LoadConfig.Bind("Misc Config Creation", "UnlockableItems", ConfigBehavior.ApplyExisting, "Behavior for all UnlockableItem configs.").Value;

		public static ConfigBehavior BehaviorTerminalNodes = LoadConfig.Bind("Misc Config Creation", "TerminalNodes", ConfigBehavior.ApplyExisting, "Behavior for all TerminalNode configs.").Value;

		public static ConfigBehavior BehaviorTerminalKeywords = LoadConfig.Bind("Misc Config Creation", "TerminalKeywords", ConfigBehavior.ApplyExisting, "Behavior for all TerminalKeyword configs.").Value;

		public static Dictionary<Type, List<string>> FilterSettings = new Dictionary<Type, List<string>>
		{
			[typeof(GrabbableObject)] = new List<string>(17)
			{
				"isHeld", "isHeldByEnemy", "hasHitGround", "itemUsedUp", "isPocketed", "isBeingUsed", "isInElevator", "isInShipRoom", "isInFactory", "wasOwnerLastFrame",
				"heldByPlayerOnServer", "reachedFloorTarget", "targetFloorPosition", "startFallingPosition", "currentUseCooldown", "fallTime", "scrapPersistedThroughRounds"
			},
			[typeof(SelectableLevel)] = new List<string>(3) { "currentWeather", "minTotalScrapValue", "maxTotalScrapValue" },
			[typeof(EnemyType)] = new List<string>(1) { "numberSpawned" },
			[typeof(EnemyAI)] = new List<string>(18)
			{
				"currentBehaviourStateIndex", "previousBehaviourStateIndex", "isInsidePlayerShip", "inSpecialAnimation", "thisEnemyIndex", "isClientCalculatingAI", "movingTowardsTargetPlayer", "moveTowardsDestination", "destination", "addPlayerVelocityToDestination",
				"timeSinceSpawn", "ventAnimationFinished", "isEnemyDead", "daytimeEnemyLeaving", "stunnedIndefinitely", "stunNormalizedTimer", "postStunInvincibilityTimer", "isOutside"
			},
			[typeof(BlobAI)] = new List<string>(1) { "currentSlimeRange" },
			[typeof(UnlockableItem)] = new List<string>(5) { "hasBeenMoved", "placedPosition", "placedRotation", "inStorage", "hasBeenUnlockedByPlayer" }
		};

		public static bool AllowFileCreation = false;

		private static List<object> ObjectApplied = new List<object>();

		public static IEnumerable<Type> LethalCompanyClasses = from CheckType in Assembly.GetAssembly(typeof(GrabbableObject)).GetTypes()
			where CheckType.IsClass && !CheckType.IsAbstract
			select CheckType;

		private static readonly object LOCKERBOI = new object();

		private void Awake()
		{
			Utils.AddAllTypes();
			SceneManager.sceneLoaded += delegate(Scene scene, LoadSceneMode mode)
			{
				AllowFileCreation = (Object.op_Implicit((Object)(object)GameObject.Find("NetworkManager")) && NetworkManager.Singleton.IsHost) || ((Scene)(ref scene)).name == "InitSceneLaunchOptions" || ((Scene)(ref scene)).name == "InitScene" || ((Scene)(ref scene)).name == "MainMenu";
				MLS.LogWarning((object)$"Scene changed: {((Scene)(ref scene)).name} ({((Scene)(ref scene)).path}) - Allow file creation? {AllowFileCreation}");
				MLS.LogInfo((object)"Attempting to apply settings.");
				try
				{
					Stopwatch stopwatch = Stopwatch.StartNew();
					foreach (Type item in LethalCompanyClasses.Where((Type CheckType) => CheckType.IsSubclassOf(typeof(GrabbableObject))))
					{
						SaveCustom(item, "Items\\{GrabbableObject.itemName}\\{TypeName}Settings", BehaviorSpecialItems);
					}
					SaveCustom(typeof(GrabbableObject), "Items\\{GrabbableObject.itemName}\\GrabbableSettings", BehaviorGrabbables);
					SaveCustom(typeof(Item), "Items\\{itemName}\\ItemSettings", BehaviorItems);
					foreach (Type item2 in LethalCompanyClasses.Where((Type CheckType) => CheckType.IsSubclassOf(typeof(EnemyAI))))
					{
						SaveCustom(item2, "Enemies\\{EnemyAI.enemyName}\\{TypeName}Settings", BehaviorSpecialEnemies);
					}
					SaveCustom(typeof(EnemyAI), "Enemies\\{EnemyAI.enemyName}\\EnemyAISettings", BehaviorEnemyAIs);
					SaveCustom(typeof(EnemyType), "Enemies\\{enemyName}\\EnemyTypeSettings", BehaviorEnemyTypes);
					SaveCustom(typeof(SelectableLevel), "Maps\\{PlanetName}\\MapSettings", BehaviorLevels);
					SaveCustom(typeof(UnlockableItem), "UnlockableItems\\{unlockableName}\\UnlockableItemSettings", BehaviorUnlockableItems, Resources.FindObjectsOfTypeAll<UnlockablesList>().SelectMany((UnlockablesList x) => x.unlockables));
					SaveCustom(typeof(TerminalNode), "TerminalNode\\{name}", BehaviorTerminalNodes);
					SaveCustom(typeof(TerminalKeyword), "TerminalKeywords\\{name}", BehaviorTerminalKeywords);
					MLS.LogInfo((object)$"Done applying settings. ({(float)stopwatch.ElapsedTicks / 10000f:000.0000}ms)");
				}
				catch (Exception ex)
				{
					MLS.LogWarning((object)"Failed to apply settings.");
					MLS.LogWarning((object)ex);
				}
			};
		}

		public static string ReplaceIfPresent(string ModifyingPath, string StringToReplace, Func<object, string> GetReplacement, object CopyObject = null)
		{
			if (!ModifyingPath.Contains(StringToReplace))
			{
				return ModifyingPath;
			}
			string text = Utils.MakeFileNameSafe(GetReplacement(CopyObject));
			if (Utility.IsNullOrWhiteSpace(text))
			{
				return ModifyingPath;
			}
			return ModifyingPath.Replace(StringToReplace, text);
		}

		private static void SaveCustom(Type SaveType, string Path, ConfigBehavior Behavior, IEnumerable<object> CustomLookup = null)
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			if (Behavior == ConfigBehavior.DoNotRun)
			{
				return;
			}
			lock (LOCKERBOI)
			{
				FieldInfo[] fields = SaveType.GetFields(BindingFlags.Instance | BindingFlags.Public);
				Path = ReplaceIfPresent(Path, "{TypeName}", (object CopyObject) => SaveType.Name);
				foreach (object item in (CustomLookup ?? Resources.FindObjectsOfTypeAll(SaveType)).Where((object x) => !ObjectApplied.Contains(x)))
				{
					if (item == null)
					{
						MLS.LogWarning((object)$"Missing object class: {item} {SaveType}");
						continue;
					}
					if (!IgnoreOverlap.Value)
					{
						ObjectApplied.Add(item);
					}
					string text = Path;
					try
					{
						object obj;
						if (!SaveType.IsAssignableFrom(typeof(Object)))
						{
							obj = "";
						}
						else
						{
							Object val = (Object)item;
							obj = (((int)val != 0) ? val.name : null);
						}
						string Name = (string)obj;
						text = ReplaceIfPresent(text, "{name}", (object CopyObject) => Name, item);
						text = ReplaceIfPresent(text, "{PlanetName}", delegate(object CopyObject)
						{
							//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)
							SelectableLevel val6 = (SelectableLevel)CopyObject;
							return (((int)val6 != 0) ? val6.PlanetName : null) ?? Name;
						}, item);
						text = ReplaceIfPresent(text, "{GrabbableObject.itemName}", delegate(object CopyObject)
						{
							//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)
							GrabbableObject val5 = (GrabbableObject)CopyObject;
							return (((int)val5 == 0) ? null : val5.itemProperties?.itemName) ?? Name;
						}, item);
						text = ReplaceIfPresent(text, "{itemName}", delegate(object CopyObject)
						{
							//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)
							Item val4 = (Item)CopyObject;
							return (((int)val4 != 0) ? val4.itemName : null) ?? Name;
						}, item);
						text = ReplaceIfPresent(text, "{EnemyAI.enemyName}", delegate(object CopyObject)
						{
							//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)
							EnemyAI val3 = (EnemyAI)CopyObject;
							return (((int)val3 == 0) ? null : val3.enemyType?.enemyName) ?? Name;
						}, item);
						text = ReplaceIfPresent(text, "{enemyName}", delegate(object CopyObject)
						{
							//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)
							EnemyType val2 = (EnemyType)CopyObject;
							return (((int)val2 != 0) ? val2.enemyName : null) ?? Name;
						}, item);
						if ((!Behavior.HasFlag(ConfigBehavior.CreateNewFiles) || !AllowFileCreation) && !File.Exists(MainDir.FullName + "\\" + text + ".cfg"))
						{
							continue;
						}
						ConfigFile configFile = new ConfigFile(MainDir.FullName + "\\" + text + ".cfg", saveOnInit: false, MetadataHelper.GetMetadata(typeof(EditorMod)));
						ConfigEntry<bool> configEntry = configFile.Bind("ACTIVE SETTINGS", "ConfigEnabled", defaultValue: false, "Whether or not this config file will apply.", 0);
						configFile.Bind("ACTIVE SETTINGS", "SaveType", SaveType.Name, "The type of this config.", 0);
						ConfigEntry<string[]> configEntry2 = configFile.Bind("ACTIVE SETTINGS", "LoadSettings", new string[0], "Settings that will load and apply to the game.\nEx: [\"SettingName\",\"OtherSettingName\"]", 0);
						int num = 1;
						string text2 = "SETTINGS";
						FieldInfo[] array = fields;
						foreach (FieldInfo fieldInfo in array)
						{
							foreach (CustomAttributeTypedArgument item2 in fieldInfo.CustomAttributes.Where((CustomAttributeData x) => x.AttributeType == typeof(HeaderAttribute)).SelectMany((CustomAttributeData Data) => Data.ConstructorArguments.ToList()))
							{
								num++;
								text2 = item2.Value.ToString().ToUpper() ?? "";
							}
							if (fieldInfo.CustomAttributes.FirstOrDefault()?.AttributeType == typeof(HideInInspector) || fieldInfo.DeclaringType != SaveType || (FilterSettings.ContainsKey(SaveType) && FilterSettings[SaveType].Contains(fieldInfo.Name)) || !TomlTypeConverter.CanConvert(fieldInfo.FieldType))
							{
								continue;
							}
							object value = configFile.Bind(text2, fieldInfo.Name, fieldInfo.FieldType, fieldInfo.GetValue(item), fieldInfo.CustomAttributes.Where((CustomAttributeData x) => x.AttributeType == typeof(TooltipAttribute)).FirstOrDefault()?.ConstructorArguments.FirstOrDefault().Value?.ToString() ?? "", num).Value;
							if (Behavior.HasFlag(ConfigBehavior.ApplyExisting) && configEntry.Value && configEntry2.Value.Contains(fieldInfo.Name))
							{
								if (value.GetType() != fieldInfo.FieldType)
								{
									MLS.LogWarning((object)(text + ".cfg > " + text2 + " > " + fieldInfo.Name + "'s input type (" + value.GetType().Name + ") is not the same as the required type: " + fieldInfo.FieldType.Name + "."));
									return;
								}
								fieldInfo.SetValue(item, value);
							}
						}
						configFile.Save();
					}
					catch (Exception arg)
					{
						MLS.LogWarning((object)$"Failed to apply settings to a specific object. Path: {MainDir.FullName}\\{text}\n{arg}");
					}
				}
			}
		}
	}
}
namespace LethalEditor.Configuration
{
	public sealed class SettingChangedEventArgs : EventArgs
	{
		public ConfigEntryBase ChangedSetting { get; }

		public SettingChangedEventArgs(ConfigEntryBase changedSetting)
		{
			ChangedSetting = changedSetting;
			base..ctor();
		}
	}
	public class ConfigDescription
	{
		public string Description { get; } = description ?? throw new ArgumentNullException("description");


		public AcceptableValueBase AcceptableValues { get; }

		public object[] Tags { get; }

		public static ConfigDescription Empty { get; } = new ConfigDescription("", null);


		public ConfigDescription(string description, AcceptableValueBase acceptableValues = null, params object[] tags)
		{
			AcceptableValues = acceptableValues;
			Tags = tags;
			base..ctor();
		}
	}
	public class ConfigDefinition : IEquatable<ConfigDefinition>
	{
		private static readonly char[] _invalidConfigChars = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' };

		public int Order { get; }

		public string Section { get; }

		public string Key { get; }

		public ConfigDefinition(string section, string key, int order = -1)
		{
			CheckInvalidConfigChars(section, "section");
			CheckInvalidConfigChars(key, "key");
			Order = order;
			Key = key;
			Section = section;
		}

		private static void CheckInvalidConfigChars(string val, string name)
		{
			if (val == null)
			{
				throw new ArgumentNullException(name);
			}
			if (val != val.Trim())
			{
				throw new ArgumentException("Cannot use whitespace characters at start or end of section and key names", name);
			}
			if (val.Any((char c) => _invalidConfigChars.Contains(c)))
			{
				throw new ArgumentException("Cannot use any of the following characters in section and key names: = \\n \\t \\ \" ' [ ]", name);
			}
		}

		public bool Equals(ConfigDefinition other)
		{
			return other != null && string.Equals(Key, other.Key) && string.Equals(Section, other.Section);
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (this == (ConfigDefinition)obj)
			{
				return true;
			}
			return Equals(obj as ConfigDefinition);
		}

		public override int GetHashCode()
		{
			return (((Key != null) ? Key.GetHashCode() : 0) * 397) ^ ((Section != null) ? Section.GetHashCode() : 0);
		}

		public static bool operator ==(ConfigDefinition left, ConfigDefinition right)
		{
			return object.Equals(left, right);
		}

		public static bool operator !=(ConfigDefinition left, ConfigDefinition right)
		{
			return !object.Equals(left, right);
		}

		public override string ToString()
		{
			return Section + "." + Key;
		}
	}
	public abstract class ConfigEntryBase
	{
		public ConfigFile ConfigFile { get; }

		public ConfigDefinition Definition { get; }

		public ConfigDescription Description { get; }

		public Type SettingType { get; }

		public object DefaultValue { get; }

		public abstract object BoxedValue { get; set; }

		internal ConfigEntryBase(ConfigFile configFile, ConfigDefinition definition, Type settingType, object defaultValue, ConfigDescription configDescription)
		{
			ConfigFile = configFile ?? throw new ArgumentNullException("configFile");
			Definition = definition ?? throw new ArgumentNullException("definition");
			SettingType = settingType ?? throw new ArgumentNullException("settingType");
			Description = configDescription ?? ConfigDescription.Empty;
			if (Description.AcceptableValues != null && !SettingType.IsAssignableFrom(Description.AcceptableValues.ValueType))
			{
				throw new ArgumentException("configDescription.AcceptableValues is for a different type than the type of this setting");
			}
			DefaultValue = defaultValue;
			BoxedValue = defaultValue;
		}

		public string GetSerializedValue()
		{
			return TomlTypeConverter.ConvertToString(BoxedValue, SettingType);
		}

		public void SetSerializedValue(string value)
		{
			try
			{
				object boxedValue = TomlTypeConverter.ConvertToValue(value, SettingType);
				BoxedValue = boxedValue;
			}
			catch (Exception ex)
			{
				EditorMod.MLS.Log((LogLevel)4, (object)$"Config value of setting \"{Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}");
			}
		}

		protected T ClampValue<T>(T value)
		{
			if (Description.AcceptableValues != null)
			{
				return (T)Description.AcceptableValues.Clamp((object)value);
			}
			return value;
		}

		protected void OnSettingChanged(object sender)
		{
			ConfigFile.OnSettingChanged(sender, this);
		}

		public void WriteDescription(StreamWriter writer)
		{
			if (!string.IsNullOrEmpty(Description.Description))
			{
				writer.WriteLine("## " + Description.Description.Replace("\n", "\n## "));
			}
			string text = ((typeof(List<>).Name == SettingType.Name && SettingType.GenericTypeArguments.Length != 0) ? ("List<" + SettingType.GenericTypeArguments[0].Name + ">") : SettingType.Name);
			writer.WriteLine("# Setting type: " + text);
			writer.WriteLine("# Default value: " + TomlTypeConverter.ConvertToString(DefaultValue, SettingType));
			if (Description.AcceptableValues != null)
			{
				writer.WriteLine(Description.AcceptableValues.ToDescriptionString());
			}
			else if (SettingType.IsEnum)
			{
				writer.WriteLine("# Acceptable values: " + string.Join(", ", Enum.GetNames(SettingType)));
				if (SettingType.GetCustomAttributes(typeof(FlagsAttribute), inherit: true).Any())
				{
					writer.WriteLine("# Multiple values can be set at the same time by separating them with , (e.g. Debug, Warning)");
				}
			}
		}
	}
	public sealed class ConfigEntry<T> : ConfigEntryBase
	{
		private T _typedValue;

		public T Value
		{
			get
			{
				return _typedValue;
			}
			set
			{
				value = ClampValue(value);
				if (!object.Equals(_typedValue, value))
				{
					_typedValue = value;
					OnSettingChanged(this);
				}
			}
		}

		public override object BoxedValue
		{
			get
			{
				return Value;
			}
			set
			{
				Value = (T)value;
			}
		}

		public event EventHandler SettingChanged;

		internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition, T defaultValue, ConfigDescription configDescription)
			: base(configFile, definition, typeof(T), defaultValue, configDescription)
		{
			configFile.SettingChanged += delegate(object sender, SettingChangedEventArgs args)
			{
				if (args.ChangedSetting == this)
				{
					this.SettingChanged?.Invoke(sender, args);
				}
			};
		}

		internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition, Type settingType, object defaultValue, ConfigDescription configDescription)
			: base(configFile, definition, settingType, defaultValue, configDescription)
		{
			configFile.SettingChanged += delegate(object sender, SettingChangedEventArgs args)
			{
				if (args.ChangedSetting == this)
				{
					this.SettingChanged?.Invoke(sender, args);
				}
			};
		}
	}
	public class ConfigFile : IDictionary<ConfigDefinition, ConfigEntryBase>, ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>, IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>, IEnumerable
	{
		private readonly BepInPlugin _ownerMetadata;

		private readonly object _ioLock = new object();

		internal static ConfigFile CoreConfig { get; } = new ConfigFile(Paths.BepInExConfigPath, saveOnInit: true);


		protected Dictionary<ConfigDefinition, ConfigEntryBase> Entries { get; } = new Dictionary<ConfigDefinition, ConfigEntryBase>();


		private Dictionary<ConfigDefinition, string> OrphanedEntries { get; } = new Dictionary<ConfigDefinition, string>();


		public string ConfigFilePath { get; }

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


		public int Count
		{
			get
			{
				lock (_ioLock)
				{
					return Entries.Count;
				}
			}
		}

		public bool IsReadOnly => false;

		ConfigEntryBase IDictionary<ConfigDefinition, ConfigEntryBase>.this[ConfigDefinition key]
		{
			get
			{
				lock (_ioLock)
				{
					return Entries[key];
				}
			}
			set
			{
				throw new InvalidOperationException("Directly setting a config entry is not supported");
			}
		}

		public ConfigEntryBase this[ConfigDefinition key]
		{
			get
			{
				lock (_ioLock)
				{
					return Entries[key];
				}
			}
		}

		public ConfigEntryBase this[string section, string key] => this[new ConfigDefinition(section, key)];

		public ICollection<ConfigDefinition> Keys
		{
			get
			{
				lock (_ioLock)
				{
					return Entries.Keys.ToArray();
				}
			}
		}

		ICollection<ConfigEntryBase> IDictionary<ConfigDefinition, ConfigEntryBase>.Values
		{
			get
			{
				lock (_ioLock)
				{
					return Entries.Values.ToArray();
				}
			}
		}

		public event EventHandler ConfigReloaded;

		public event EventHandler<SettingChangedEventArgs> SettingChanged;

		public ConfigFile(string configPath, bool saveOnInit)
			: this(configPath, saveOnInit, null)
		{
		}

		public ConfigFile(string configPath, bool saveOnInit, BepInPlugin ownerMetadata)
		{
			_ownerMetadata = ownerMetadata;
			if (configPath == null)
			{
				throw new ArgumentNullException("configPath");
			}
			configPath = Path.GetFullPath(configPath);
			ConfigFilePath = configPath;
			if (File.Exists(ConfigFilePath))
			{
				Reload();
			}
			else if (saveOnInit)
			{
				Save();
			}
		}

		public void Reload()
		{
			lock (_ioLock)
			{
				OrphanedEntries.Clear();
				string section = string.Empty;
				string[] array = File.ReadAllLines(ConfigFilePath);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.StartsWith("#"))
					{
						continue;
					}
					if (text.StartsWith("[") && text.EndsWith("]"))
					{
						string text2 = text;
						section = text2.Substring(1, text2.Length - 1 - 1);
						continue;
					}
					string[] array2 = text.Split(new char[1] { '=' }, 2);
					if (array2.Length == 2)
					{
						string key = array2[0].Trim();
						string text3 = array2[1].Trim();
						ConfigDefinition key2 = new ConfigDefinition(section, key);
						Entries.TryGetValue(key2, out var value);
						if (value != null)
						{
							value.SetSerializedValue(text3);
						}
						else
						{
							OrphanedEntries[key2] = text3;
						}
					}
				}
			}
			OnConfigReloaded();
		}

		public void Save()
		{
			lock (_ioLock)
			{
				string directoryName = Path.GetDirectoryName(ConfigFilePath);
				if (directoryName != null)
				{
					Directory.CreateDirectory(directoryName);
				}
				using StreamWriter streamWriter = new StreamWriter(ConfigFilePath, append: false, Utility.UTF8NoBom);
				if (_ownerMetadata != null)
				{
					streamWriter.WriteLine($"## Settings file was created by plugin {_ownerMetadata.Name} v{_ownerMetadata.Version}");
					streamWriter.WriteLine("## Plugin GUID: " + _ownerMetadata.GUID);
					streamWriter.WriteLine();
				}
				foreach (var item in from x in Entries.Select((KeyValuePair<ConfigDefinition, ConfigEntryBase> x) => new
					{
						Key = x.Key,
						entry = x.Value,
						value = x.Value.GetSerializedValue()
					}).Concat(OrphanedEntries.Select((KeyValuePair<ConfigDefinition, string> x) => new
					{
						Key = x.Key,
						entry = (ConfigEntryBase)null,
						value = x.Value
					}))
					group x by (x.Key.Order, x.Key.Section) into x
					orderby x.Key.Order == -1, x.Key.Order, x.Key.Section
					select x)
				{
					streamWriter.WriteLine("[" + item.Key.Item2 + "]");
					foreach (var item2 in item)
					{
						streamWriter.WriteLine();
						item2.entry?.WriteDescription(streamWriter);
						streamWriter.WriteLine(item2.Key.Key + " = " + item2.value);
					}
					streamWriter.WriteLine();
				}
			}
		}

		public bool TryGetEntry<T>(ConfigDefinition configDefinition, out ConfigEntry<T> entry)
		{
			lock (_ioLock)
			{
				if (Entries.TryGetValue(configDefinition, out var value))
				{
					entry = (ConfigEntry<T>)value;
					return true;
				}
				entry = null;
				return false;
			}
		}

		public bool TryGetEntry<T>(string section, string key, out ConfigEntry<T> entry)
		{
			return TryGetEntry(new ConfigDefinition(section, key), out entry);
		}

		public ConfigEntry<T> Bind<T>(ConfigDefinition configDefinition, Type settingType, T defaultValue, ConfigDescription configDescription = null)
		{
			if (!TomlTypeConverter.CanConvert(settingType))
			{
				throw new ArgumentException(string.Format("Type {0} is not supported by the config system. Supported types: {1}", settingType, string.Join(", ", (from x in TomlTypeConverter.GetSupportedTypes()
					select x.Name).ToArray())));
			}
			lock (_ioLock)
			{
				if (Entries.TryGetValue(configDefinition, out var value))
				{
					return (ConfigEntry<T>)value;
				}
				ConfigEntry<T> configEntry = new ConfigEntry<T>(this, configDefinition, settingType, defaultValue, configDescription);
				Entries[configDefinition] = configEntry;
				if (OrphanedEntries.TryGetValue(configDefinition, out var value2))
				{
					configEntry.SetSerializedValue(value2);
					OrphanedEntries.Remove(configDefinition);
				}
				if (SaveOnConfigSet)
				{
					Save();
				}
				return configEntry;
			}
		}

		public ConfigEntry<T> Bind<T>(ConfigDefinition configDefinition, T defaultValue, ConfigDescription configDescription = null)
		{
			return Bind(configDefinition, typeof(T), defaultValue, configDescription);
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription configDescription = null, int order = -1)
		{
			return Bind(new ConfigDefinition(section, key, order), defaultValue, configDescription);
		}

		public ConfigEntry<T> Bind<T>(string section, string key, Type settingType, T defaultValue, ConfigDescription configDescription = null, int order = -1)
		{
			return Bind(new ConfigDefinition(section, key, order), settingType, defaultValue, configDescription);
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, int order = -1)
		{
			return Bind(new ConfigDefinition(section, key, order), defaultValue, new ConfigDescription(description, null));
		}

		public ConfigEntry<T> Bind<T>(string section, string key, Type settingType, T defaultValue, string description, int order = -1)
		{
			return Bind(new ConfigDefinition(section, key, order), settingType, defaultValue, new ConfigDescription(description, null));
		}

		internal void OnSettingChanged(object sender, ConfigEntryBase changedEntryBase)
		{
			if (changedEntryBase == null)
			{
				throw new ArgumentNullException("changedEntryBase");
			}
			if (SaveOnConfigSet)
			{
				Save();
			}
			EventHandler<SettingChangedEventArgs> settingChanged = this.SettingChanged;
			if (settingChanged == null)
			{
				return;
			}
			SettingChangedEventArgs e = new SettingChangedEventArgs(changedEntryBase);
			foreach (EventHandler<SettingChangedEventArgs> item in settingChanged.GetInvocationList().Cast<EventHandler<SettingChangedEventArgs>>())
			{
				try
				{
					item(sender, e);
				}
				catch (Exception ex)
				{
					EditorMod.MLS.Log((LogLevel)2, (object)ex);
				}
			}
		}

		private void OnConfigReloaded()
		{
			EventHandler configReloaded = this.ConfigReloaded;
			if (configReloaded == null)
			{
				return;
			}
			foreach (EventHandler item in configReloaded.GetInvocationList().Cast<EventHandler>())
			{
				try
				{
					item(this, EventArgs.Empty);
				}
				catch (Exception ex)
				{
					EditorMod.MLS.Log((LogLevel)2, (object)ex);
				}
			}
		}

		public IEnumerator<KeyValuePair<ConfigDefinition, ConfigEntryBase>> GetEnumerator()
		{
			return Entries.GetEnumerator();
		}

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

		void ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>.Add(KeyValuePair<ConfigDefinition, ConfigEntryBase> item)
		{
			lock (_ioLock)
			{
				Entries.Add(item.Key, item.Value);
			}
		}

		public bool Contains(KeyValuePair<ConfigDefinition, ConfigEntryBase> item)
		{
			lock (_ioLock)
			{
				return ((ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)Entries).Contains(item);
			}
		}

		void ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>.CopyTo(KeyValuePair<ConfigDefinition, ConfigEntryBase>[] array, int arrayIndex)
		{
			lock (_ioLock)
			{
				((ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)Entries).CopyTo(array, arrayIndex);
			}
		}

		bool ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>.Remove(KeyValuePair<ConfigDefinition, ConfigEntryBase> item)
		{
			lock (_ioLock)
			{
				return Entries.Remove(item.Key);
			}
		}

		public bool ContainsKey(ConfigDefinition key)
		{
			lock (_ioLock)
			{
				return Entries.ContainsKey(key);
			}
		}

		public void Add(ConfigDefinition key, ConfigEntryBase value)
		{
			throw new InvalidOperationException("Directly adding a config entry is not supported");
		}

		public bool Remove(ConfigDefinition key)
		{
			lock (_ioLock)
			{
				return Entries.Remove(key);
			}
		}

		public void Clear()
		{
			lock (_ioLock)
			{
				Entries.Clear();
			}
		}

		bool IDictionary<ConfigDefinition, ConfigEntryBase>.TryGetValue(ConfigDefinition key, out ConfigEntryBase value)
		{
			lock (_ioLock)
			{
				return Entries.TryGetValue(key, out value);
			}
		}
	}
}