Decompiled source of Drop That v3.1.5

Valheim.DropThat.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CreatureLevelControl;
using DropThat.Caches;
using DropThat.Commands;
using DropThat.Configuration;
using DropThat.Configuration.Sync;
using DropThat.Core;
using DropThat.Core.Configuration;
using DropThat.Core.Patches;
using DropThat.Creature;
using DropThat.Creature.DamageRecords;
using DropThat.Creature.StatusRecords;
using DropThat.Debugging;
using DropThat.Drop;
using DropThat.Drop.CharacterDropSystem;
using DropThat.Drop.CharacterDropSystem.Caches;
using DropThat.Drop.CharacterDropSystem.Conditions;
using DropThat.Drop.CharacterDropSystem.Conditions.ModSpecific.CLLC;
using DropThat.Drop.CharacterDropSystem.Conditions.ModSpecific.SpawnThat;
using DropThat.Drop.CharacterDropSystem.Configuration;
using DropThat.Drop.CharacterDropSystem.Configuration.Toml;
using DropThat.Drop.CharacterDropSystem.Debug;
using DropThat.Drop.CharacterDropSystem.Managers;
using DropThat.Drop.CharacterDropSystem.Models;
using DropThat.Drop.CharacterDropSystem.Patches;
using DropThat.Drop.CharacterDropSystem.Services;
using DropThat.Drop.CharacterDropSystem.Sync;
using DropThat.Drop.DropTableSystem;
using DropThat.Drop.DropTableSystem.Conditions;
using DropThat.Drop.DropTableSystem.Conditions.ModSpecific.CLLC;
using DropThat.Drop.DropTableSystem.Conditions.ModSpecific.SpawnThat;
using DropThat.Drop.DropTableSystem.Configuration;
using DropThat.Drop.DropTableSystem.Configuration.Toml;
using DropThat.Drop.DropTableSystem.Debug;
using DropThat.Drop.DropTableSystem.Managers;
using DropThat.Drop.DropTableSystem.Models;
using DropThat.Drop.DropTableSystem.Patches;
using DropThat.Drop.DropTableSystem.Services;
using DropThat.Drop.DropTableSystem.Sync;
using DropThat.Drop.Options;
using DropThat.Drop.Options.Modifiers;
using DropThat.Drop.Options.Modifiers.ModEpicLoot;
using DropThat.Integrations;
using DropThat.Integrations.CllcIntegration;
using DropThat.Integrations.EpicLootIntegration;
using DropThat.Locations;
using DropThat.Locations.Sync;
using DropThat.Utilities;
using DropThat.Utilities.Valheim;
using EpicLoot;
using EpicLoot.Data;
using EpicLoot.LegendarySystem;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ThatCore.Cache;
using ThatCore.Config;
using ThatCore.Config.Toml;
using ThatCore.Config.Toml.Mapping;
using ThatCore.Config.Toml.Parsers;
using ThatCore.Config.Toml.Schema;
using ThatCore.Config.Toml.Writers;
using ThatCore.Extensions;
using ThatCore.Lifecycle;
using ThatCore.Lifecycle.Patches;
using ThatCore.Logging;
using ThatCore.Models;
using ThatCore.Network;
using ThatCore.Utilities.Files;
using ThatCore.Utilities.Valheim;
using UnityEngine;
using Valheim.DropThat.Drop.DropTableSystem.Patches;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ASharpPen/Valheim.SpawnThat")]
[assembly: AssemblyTitle("Valheim.DropThat")]
[assembly: AssemblyProduct("Valheim.DropThat")]
[assembly: AssemblyInformationalVersion("3.1.5+8ce08c9a114ca5eb28ba0cf56d13eaa54d5b048b")]
[assembly: AssemblyFileVersion("3.1.5.0")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCompany("A Sharp Pen")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: InternalsVisibleTo("Valheim.DropThat.Tests")]
[assembly: AssemblyDescription("Valheim mod and tool for configuring loot drops.")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("3.1.5.0")]
[module: RefSafetyRules(11)]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[<296f7d6c-da35-4841-bb06-68137b2ba19b>Embedded]
	internal sealed class <296f7d6c-da35-4841-bb06-68137b2ba19b>EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[<296f7d6c-da35-4841-bb06-68137b2ba19b>Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	[CompilerGenerated]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ThatCore.Models
{
	public struct Optional<T>
	{
		public T Value { get; private set; }

		public bool IsSet { get; private set; }

		public Optional()
		{
			IsSet = false;
			Value = default(T);
		}

		public Optional(T value)
		{
			IsSet = false;
			Value = default(T);
			Set(value);
		}

		public void Set(T value)
		{
			IsSet = true;
			Value = value;
		}

		public static implicit operator Optional<T>(T value)
		{
			return new Optional<T>(value);
		}

		public static implicit operator T(Optional<T> optional)
		{
			return optional.Value;
		}

		public void DoIfSet(Action<T> action)
		{
			if (IsSet)
			{
				action(Value);
			}
		}

		public void AssignIfSet(ref T dest)
		{
			if (IsSet)
			{
				dest = Value;
			}
		}
	}
	public class TypeSet<TKey> : IEnumerable<TKey>, IEnumerable, ICollection<TKey>
	{
		private Dictionary<Type, TKey> _dic = new Dictionary<Type, TKey>();

		public TKey this[TKey item]
		{
			get
			{
				return _dic[item.GetType()];
			}
			set
			{
				_dic[item.GetType()] = item;
			}
		}

		public int Count => _dic.Count;

		public bool IsReadOnly { get; }

		public TypeSet()
		{
		}

		public TypeSet(TypeSet<TKey> input)
		{
			_dic = new Dictionary<Type, TKey>(input._dic);
		}

		public T GetOrCreate<T>() where T : TKey, new()
		{
			if (_dic.TryGetValue(typeof(T), out var value) && value is T)
			{
				return (T)(object)value;
			}
			T val = new T();
			_dic[typeof(T)] = (TKey)(object)val;
			return val;
		}

		public T GetOrDefault<T>() where T : TKey
		{
			if (_dic.TryGetValue(typeof(T), out var value) && value is T)
			{
				return (T)(object)value;
			}
			return default(T);
		}

		public void Set(TKey item)
		{
			_dic[item.GetType()] = item;
		}

		public bool Remove<T>() where T : TKey
		{
			return _dic.Remove(typeof(T));
		}

		public bool Remove(Type type)
		{
			return _dic.Remove(type);
		}

		public TypeSet<TKey> Clone()
		{
			return new TypeSet<TKey>
			{
				_dic = new Dictionary<Type, TKey>(_dic)
			};
		}

		public IEnumerator<TKey> GetEnumerator()
		{
			return _dic.Values.GetEnumerator();
		}

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

		public void Add(TKey item)
		{
			_dic.Add(item.GetType(), item);
		}

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

		public bool Contains(TKey item)
		{
			return _dic.ContainsKey(item.GetType());
		}

		public void CopyTo(TKey[] array, int arrayIndex)
		{
			_dic.Values.CopyTo(array, arrayIndex);
		}

		public bool Remove(TKey item)
		{
			return _dic.Remove(item.GetType());
		}
	}
}
namespace ThatCore.Lifecycle
{
	internal enum GameState
	{
		Invalid,
		Singleplayer,
		Multiplayer,
		DedicatedServer
	}
	internal static class LifecycleManager
	{
		private static HashSet<Action> OnWorldInitActions = new HashSet<Action>();

		public static GameState GameState { get; private set; }

		public static event Action OnWorldInit;

		public static event Action OnLateInit;

		public static event Action OnSinglePlayerInit;

		public static event Action OnMultiplayerInit;

		public static event Action OnDedicatedServerInit;

		public static event Action OnFindSpawnPointFirstTime;

		public static event Action<ZNetPeer> OnNewConnection;

		public static event Action OnZnetUpdate;

		public static void SubscribeToWorldInit(Action onInit)
		{
			OnWorldInitActions.Add(onInit);
		}

		private static void WorldInit()
		{
			Log.Debug?.Log("Running world init actions");
			LifecycleManager.OnWorldInit.Raise("Error during world init event");
			foreach (Action onWorldInitAction in OnWorldInitActions)
			{
				onWorldInitAction();
			}
		}

		internal static void InitSingleplayer()
		{
			GameState = GameState.Singleplayer;
			WorldInit();
			Log.Debug?.Log("Running singleplayer init actions");
			LifecycleManager.OnSinglePlayerInit.Raise("Error during singleplayer init event");
			LifecycleManager.OnLateInit.Raise("Error during singleplayer late init event");
		}

		internal static void InitMultiplayer()
		{
			GameState = GameState.Multiplayer;
			WorldInit();
			Log.Debug?.Log("Running multiplayer init actions");
			LifecycleManager.OnMultiplayerInit.Raise("Error during multiplayer init event");
			LifecycleManager.OnLateInit.Raise("Error during multiplayer late init event");
		}

		internal static void InitDedicated()
		{
			GameState = GameState.DedicatedServer;
			WorldInit();
			Log.Debug?.Log("Running dedicated server init actions");
			LifecycleManager.OnDedicatedServerInit.Raise("Error during dedicated server init event");
			LifecycleManager.OnLateInit.Raise("Error during dedicated server late init event");
		}

		internal static void InitFindSpawnPointFirstTime()
		{
			LifecycleManager.OnFindSpawnPointFirstTime.Raise("Error during OnFindSpawnPointFirstTime event");
		}

		internal static void PeerConnected(ZNetPeer peer)
		{
			Log.Trace?.Log($"Running OnNewConnection actions for new peer '{peer.m_playerName}:{peer.m_uid}'");
			LifecycleManager.OnNewConnection.Raise(peer);
		}

		internal static void ZnetUpdate()
		{
			LifecycleManager.OnZnetUpdate.Raise();
		}
	}
	internal static class PatchManager
	{
		public static void ApplyPatches(this Harmony harmony)
		{
			harmony.PatchAll(typeof(FejdStartup_TriggerLifecycle_Patch));
			harmony.PatchAll(typeof(Game_FindSpawnPoint_TriggerLifecycle_Patch));
			harmony.PatchAll(typeof(ZNet_OnNewConnection_TriggerSync_Patch));
			harmony.PatchAll(typeof(ZNet_Update_Patch));
		}
	}
}
namespace ThatCore.Lifecycle.Patches
{
	[HarmonyPatch]
	internal static class FejdStartup_TriggerLifecycle_Patch
	{
		[HarmonyPatch(typeof(FejdStartup), "OnWorldStart")]
		[HarmonyPrefix]
		private static void InitSingleplayer()
		{
			LifecycleManager.InitSingleplayer();
		}

		[HarmonyPatch(typeof(FejdStartup), "JoinServer")]
		[HarmonyPrefix]
		private static void InitMultiplayer()
		{
			LifecycleManager.InitMultiplayer();
		}

		[HarmonyPatch(typeof(FejdStartup), "ParseServerArguments")]
		[HarmonyPrefix]
		private static void InitDedicated()
		{
			LifecycleManager.InitDedicated();
		}
	}
	[HarmonyPatch(typeof(Game))]
	internal static class Game_FindSpawnPoint_TriggerLifecycle_Patch
	{
		private static bool FirstTime;

		static Game_FindSpawnPoint_TriggerLifecycle_Patch()
		{
			FirstTime = true;
			LifecycleManager.SubscribeToWorldInit(delegate
			{
				FirstTime = true;
			});
		}

		[HarmonyPostfix]
		[HarmonyPatch("FindSpawnPoint")]
		private static void TriggerLifecycle()
		{
			if (FirstTime)
			{
				FirstTime = false;
				LifecycleManager.InitFindSpawnPointFirstTime();
			}
		}
	}
	[HarmonyPatch(typeof(ZNet))]
	internal static class ZNet_OnNewConnection_TriggerSync_Patch
	{
		[HarmonyPatch("OnNewConnection")]
		[HarmonyPostfix]
		private static void TriggerLifecycle(ZNetPeer peer)
		{
			LifecycleManager.PeerConnected(peer);
		}
	}
	[HarmonyPatch]
	internal static class ZNet_Update_Patch
	{
		[HarmonyPatch(typeof(ZNet), "Update")]
		[HarmonyPostfix]
		private static void HookZnetUpdate()
		{
			try
			{
				LifecycleManager.ZnetUpdate();
			}
			catch (Exception e)
			{
				Log.Error?.Log("Error during ZnetUpdate for ZNet.Update", e);
			}
		}
	}
}
namespace ThatCore.Utilities.Valheim
{
	internal static class HeightmapBiomeExtensions
	{
		public static List<Biome> Split(this Biome biomeMask)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			List<Biome> list = new List<Biome>();
			foreach (Biome value in Enum.GetValues(typeof(Biome)))
			{
				if ((int)value == 895)
				{
					if ((int)biomeMask == 895)
					{
						list.Add(value);
					}
				}
				else if ((biomeMask & value) > 0)
				{
					list.Add(value);
				}
			}
			return list;
		}

		public static Biome ToBitmask(this IList<Biome> biomes)
		{
			//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)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Biome val = (Biome)0;
			for (int i = 0; i < biomes.Count; i++)
			{
				val |= biomes[i];
			}
			return val;
		}

		public static Biome ToBitmask(this IEnumerable<Biome> biomes)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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)
			//IL_0012: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			Biome val = (Biome)0;
			foreach (Biome biome in biomes)
			{
				val |= biome;
			}
			return val;
		}
	}
	internal static class HitDataDamageTypeExtensions
	{
		public static DamageType ToBitmask(this IList<DamageType> damageTypes)
		{
			//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)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			DamageType val = (DamageType)0;
			for (int i = 0; i < damageTypes.Count; i++)
			{
				val |= damageTypes[i];
			}
			return val;
		}

		public static DamageType ToBitmask(this IEnumerable<DamageType> damageTypes)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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)
			//IL_0012: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			DamageType val = (DamageType)0;
			foreach (DamageType damageType in damageTypes)
			{
				val |= damageType;
			}
			return val;
		}
	}
	internal static class ZdoExtensions
	{
		private static int NoiseHash = StringExtensionMethods.GetStableHashCode("noise");

		private static int TamedHash = StringExtensionMethods.GetStableHashCode("tamed");

		private static int EventCreatureHash = StringExtensionMethods.GetStableHashCode("EventCreature");

		private static int HuntPlayerHash = StringExtensionMethods.GetStableHashCode("huntplayer");

		private static int FactionHash = StringExtensionMethods.GetStableHashCode("faction");

		public static float GetNoise(this ZDO zdo)
		{
			return zdo.GetFloat(NoiseHash, 0f);
		}

		public static bool GetTamed(this ZDO zdo)
		{
			return zdo.GetBool(TamedHash, false);
		}

		public static bool GetEventCreature(this ZDO zdo)
		{
			return zdo.GetBool(EventCreatureHash, false);
		}

		public static Faction? GetFaction(this ZDO zdo)
		{
			int num = zdo.GetInt(FactionHash, -1);
			if (num < 0)
			{
				return null;
			}
			return (Faction)num;
		}

		public static void SetFaction(this ZDO zdo, Faction faction)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected I4, but got Unknown
			zdo.Set(FactionHash, (int)faction, false);
		}

		public static bool GetHuntPlayer(this ZDO zdo)
		{
			return zdo.GetBool(HuntPlayerHash, false);
		}

		public static void SetHuntPlayer(this ZDO zdo, bool huntPlayer)
		{
			zdo.Set(HuntPlayerHash, huntPlayer);
		}
	}
}
namespace ThatCore.Utilities.Files
{
	internal static class FileUtils
	{
		public static void EnsureDirectoryExistsForFile(string filePath)
		{
			string directoryName = Path.GetDirectoryName(filePath);
			if (!Directory.Exists(directoryName))
			{
				Log.Trace?.Log("Creating missing folders in path.");
				Directory.CreateDirectory(directoryName);
			}
		}
	}
}
namespace ThatCore.Network
{
	internal interface IMessage
	{
		void Initialize();

		void AfterUnpack();
	}
	internal class Serializer
	{
		private class TypeResolver : INodeTypeResolver
		{
			public HashSet<Type> WhitelistedTypes { get; }

			public TypeResolver(HashSet<Type> whitelistedTypes)
			{
				WhitelistedTypes = whitelistedTypes;
			}

			public bool Resolve(NodeEvent nodeEvent, ref Type currentType)
			{
				if (nodeEvent.Tag.IsEmpty || string.IsNullOrWhiteSpace(nodeEvent.Tag.Value))
				{
					return false;
				}
				Type type = Type.GetType(nodeEvent.Tag.Value.Substring(1));
				if ((object)type == null)
				{
					return false;
				}
				if (WhitelistedTypes.Any((Type x) => x.IsAssignableFrom(type)))
				{
					currentType = type;
					return true;
				}
				return false;
			}
		}

		private HashSet<Type> RegisteredTypes { get; } = new HashSet<Type>();

		private ISerializer CachedSerializer { get; set; }

		private IDeserializer CachedDeserializer { get; set; }

		private bool RefreshSerializerCache { get; set; } = true;

		private bool RefreshDeserializerCache { get; set; } = true;

		public Serializer RegisterType(Type type)
		{
			if (RegisteredTypes.Add(type))
			{
				RefreshSerializerCache = true;
			}
			return this;
		}

		public Serializer RegisterTypes(IEnumerable<Type> types)
		{
			foreach (Type type in types)
			{
				RegisterType(type);
			}
			return this;
		}

		public ISerializer GetSerializer()
		{
			if (RefreshSerializerCache)
			{
				CachedSerializer = ConfigureSerializer();
				RefreshSerializerCache = false;
			}
			return CachedSerializer;
		}

		public IDeserializer GetDeserializer()
		{
			if (RefreshDeserializerCache)
			{
				CachedDeserializer = ConfigureDeserializer();
				RefreshDeserializerCache = false;
			}
			return CachedDeserializer;
		}

		private ISerializer ConfigureSerializer()
		{
			SerializerBuilder serializerBuilder = new SerializerBuilder();
			if (RegisteredTypes != null)
			{
				foreach (Type registeredType in RegisteredTypes)
				{
					serializerBuilder.WithTagMapping("!" + registeredType.AssemblyQualifiedName, registeredType);
				}
			}
			return serializerBuilder.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults | DefaultValuesHandling.OmitEmptyCollections).WithTypeConverter(new YamlEnumWriter()).Build();
		}

		public IDeserializer ConfigureDeserializer()
		{
			return new DeserializerBuilder().WithNodeTypeResolver(new TypeResolver(RegisteredTypes)).Build();
		}

		public byte[] SerializeAndCompress(object obj)
		{
			string s = GetSerializer().Serialize(obj);
			using MemoryStream memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(s));
			using MemoryStream memoryStream2 = new MemoryStream();
			using (GZipStream destination = new GZipStream(memoryStream2, CompressionLevel.Optimal))
			{
				memoryStream.CopyTo(destination);
			}
			return memoryStream2.ToArray();
		}

		public object DeserializeCompressed(Type type, Stream stream)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true))
			{
				gZipStream.CopyTo(memoryStream);
			}
			memoryStream.Position = 0L;
			using TextReader input = new StreamReader(memoryStream, Encoding.Unicode);
			return GetDeserializer().Deserialize(input, type);
		}

		public object DeserializeCompressed(Type type, byte[] content)
		{
			using MemoryStream stream = new MemoryStream(content);
			using MemoryStream memoryStream = new MemoryStream();
			using (GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true))
			{
				gZipStream.CopyTo(memoryStream);
			}
			memoryStream.Position = 0L;
			using TextReader input = new StreamReader(memoryStream, Encoding.Unicode);
			return GetDeserializer().Deserialize(input, type);
		}

		public T DeserializeCompressed<T>(Stream stream)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true))
			{
				gZipStream.CopyTo(memoryStream);
			}
			memoryStream.Position = 0L;
			using TextReader input = new StreamReader(memoryStream, Encoding.Unicode);
			return GetDeserializer().Deserialize<T>(input);
		}
	}
	internal static class SerializerManager
	{
		private static Dictionary<Type, Serializer> TypeSerializers { get; } = new Dictionary<Type, Serializer>();

		public static Serializer GetSerializer<T>()
		{
			return GetSerializer(typeof(T));
		}

		public static Serializer GetSerializer(Type type)
		{
			if (TypeSerializers.TryGetValue(type, out var value))
			{
				return value;
			}
			return TypeSerializers[type] = new Serializer();
		}
	}
	[Serializable]
	internal class SplitMessage
	{
		public int SplitIndex { get; set; }

		public int SplitCount { get; set; }

		public Guid TransferId { get; set; }

		public byte[] Content { get; set; }
	}
	internal static class SplitMessageManager
	{
		internal static int MaxPackageSize = 100000;

		public static List<SplitMessage> Pack(IMessage message)
		{
			byte[] array = SerializerManager.GetSerializer(message.GetType()).SerializeAndCompress(message);
			int num = Math.Max(1, (int)Math.Ceiling((double)array.Count() / (double)MaxPackageSize));
			Guid transferId = Guid.NewGuid();
			List<SplitMessage> list = new List<SplitMessage>(num);
			for (int i = 0; i < num; i++)
			{
				int num2 = array.Count() - i * MaxPackageSize;
				int num3 = Math.Min(num2, MaxPackageSize);
				int num4 = i * MaxPackageSize;
				byte[] array2 = new byte[num3];
				Array.Copy(array, num4, array2, 0, num3);
				list.Add(new SplitMessage
				{
					SplitIndex = i,
					SplitCount = num,
					TransferId = transferId,
					Content = array2
				});
				Log.Development?.Log("Created package:" + $"\n\t\ti: {i}" + $"\n\t\tRemaining: {num2}" + $"\n\t\tItems: {num3}" + $"\n\t\tStart: {num4}" + $"\n\t\tSplit Length: {array2.Length}" + $"\n\t\tTransferId: {list.Last().TransferId}");
			}
			Log.Development?.Log($"Split package into {list.Count} pieces");
			return list;
		}

		public static IMessage CombineAndUnpackSplits(Type type, Dictionary<int, SplitMessage> splits)
		{
			using MemoryStream memoryStream = new MemoryStream(splits.OrderBy((KeyValuePair<int, SplitMessage> x) => x.Key).SelectMany((KeyValuePair<int, SplitMessage> x) => x.Value.Content).ToArray());
			Log.Development?.Log($"Deserializing '{splits.Count}' split packages with total size of '{memoryStream.Length}' bytes");
			Log.Debug?.Log($"Deserializing package size: {memoryStream.Length} bytes");
			return SerializerManager.GetSerializer(type).DeserializeCompressed(type, memoryStream) as IMessage;
		}

		public static T CombineAndUnpackSplits<T>(Dictionary<int, SplitMessage> splits)
		{
			using MemoryStream memoryStream = new MemoryStream(splits.OrderBy((KeyValuePair<int, SplitMessage> x) => x.Key).SelectMany((KeyValuePair<int, SplitMessage> x) => x.Value.Content).ToArray());
			Log.Development?.Log($"Deserializing '{splits.Count}' split packages with total size of '{memoryStream.Length}' bytes");
			Log.Debug?.Log($"Deserializing package size: {memoryStream.Length} bytes");
			return SerializerManager.GetSerializer<T>().DeserializeCompressed<T>(memoryStream);
		}
	}
	internal class YamlEnumWriter : IYamlTypeConverter
	{
		public bool Accepts(Type type)
		{
			return type.IsEnum;
		}

		public object ReadYaml(IParser parser, Type type)
		{
			throw new NotImplementedException();
		}

		public void WriteYaml(IEmitter emitter, object value, Type type)
		{
			YamlDotNet.Core.Events.Scalar scalar = new YamlDotNet.Core.Events.Scalar(((int)value).ToString());
			emitter.Emit(scalar);
		}
	}
	internal class IncomingMessage
	{
		public Guid TransferId { get; }

		public int ExpectedSplits { get; }

		public DateTimeOffset SessionStarted { get; } = DateTimeOffset.UtcNow;

		public DateTimeOffset LastSplit { get; set; } = DateTimeOffset.UtcNow;

		public Dictionary<int, SplitMessage> Splits { get; }

		public HashSet<int> MissingSplits { get; }

		public IncomingMessage(SplitMessage initialMessage)
		{
			TransferId = initialMessage.TransferId;
			ExpectedSplits = initialMessage.SplitCount;
			MissingSplits = Enumerable.Range(0, ExpectedSplits).ToHashSet();
			Splits = new Dictionary<int, SplitMessage> { { initialMessage.SplitIndex, initialMessage } };
			MissingSplits.Remove(initialMessage.SplitIndex);
		}

		public void AddPackage(SplitMessage split)
		{
			if (!ValidPackage(split))
			{
				Log.Debug?.Log($"Received unexpected misformed package '{split.TransferId}:{split.SplitIndex}' for session '{split.TransferId}'. Dropping package.");
				return;
			}
			Splits[split.SplitIndex] = split;
			MissingSplits.Remove(split.SplitIndex);
			LastSplit = DateTimeOffset.UtcNow;
		}

		private bool ValidPackage(SplitMessage split)
		{
			if (TransferId == split.TransferId)
			{
				return ExpectedSplits == split.SplitCount;
			}
			return false;
		}
	}
	internal static class IncomingMessageService
	{
		private static Dictionary<Guid, IncomingMessage> MessageCache { get; }

		private static Dictionary<Guid, DateTimeOffset> RecentlyDroppedTransfers { get; }

		private static TimeSpan RetentionTime { get; }

		private static TimeSpan DroppedRetentionTime { get; }

		private static Queue<IMessage> ReceivedMessages { get; }

		private static BinaryFormatter BinaryFormatter { get; }

		static IncomingMessageService()
		{
			MessageCache = new Dictionary<Guid, IncomingMessage>();
			RecentlyDroppedTransfers = new Dictionary<Guid, DateTimeOffset>();
			RetentionTime = TimeSpan.FromMinutes(1.0);
			DroppedRetentionTime = TimeSpan.FromMinutes(2.0);
			ReceivedMessages = new Queue<IMessage>();
			BinaryFormatter = new BinaryFormatter();
			LifecycleManager.OnZnetUpdate += UpdateCache;
			LifecycleManager.OnZnetUpdate += DispatchMessage;
			LifecycleManager.SubscribeToWorldInit(delegate
			{
				MessageCache.Clear();
				RecentlyDroppedTransfers.Clear();
				ReceivedMessages.Clear();
			});
		}

		private static void UpdateCache()
		{
			if (RecentlyDroppedTransfers.Count > 0)
			{
				DateTimeOffset dateTimeOffset = DateTimeOffset.UtcNow - DroppedRetentionTime;
				KeyValuePair<Guid, DateTimeOffset>[] array = RecentlyDroppedTransfers.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					KeyValuePair<Guid, DateTimeOffset> keyValuePair = array[i];
					if (keyValuePair.Value < dateTimeOffset)
					{
						RecentlyDroppedTransfers.Remove(keyValuePair.Key);
					}
				}
			}
			if (MessageCache.Count <= 0)
			{
				return;
			}
			DateTimeOffset dateTimeOffset2 = DateTimeOffset.UtcNow - RetentionTime;
			IncomingMessage[] array2 = MessageCache.Values.ToArray();
			foreach (IncomingMessage incomingMessage in array2)
			{
				if (incomingMessage.LastSplit < dateTimeOffset2)
				{
					Log.Trace?.Log($"Removing dead package session with transfer id '{incomingMessage.TransferId}' and {incomingMessage.Splits.Count}/{incomingMessage.ExpectedSplits} received packages");
					MessageCache.Remove(incomingMessage.TransferId);
					RecentlyDroppedTransfers[incomingMessage.TransferId] = DateTimeOffset.UtcNow;
				}
			}
		}

		private static void DispatchMessage()
		{
			try
			{
				IMessage message;
				lock (ReceivedMessages)
				{
					if (ReceivedMessages.Count == 0)
					{
						return;
					}
					message = ReceivedMessages.Dequeue();
				}
				Log.Development?.Log($"[IncomingMessageService:DispatchMessage] Running followup unpacking of message '{message.GetType()}'");
				message?.AfterUnpack();
			}
			catch (Exception e)
			{
				Log.Error?.Log("Error while attempting to process received package.", e);
			}
		}

		public static Task ReceiveMessageAsync<T>(ZPackage package)
		{
			using MemoryStream serializationStream = new MemoryStream(package.ReadByteArray());
			ReceiveMessage<T>(BinaryFormatter.Deserialize(serializationStream) as SplitMessage);
			return Task.CompletedTask;
		}

		public static void ReceiveMessage<T>(SplitMessage split)
		{
			ReceiveMessage(typeof(T), split);
		}

		public static void ReceiveMessage(Type messageType, SplitMessage splitMessage)
		{
			if (RecentlyDroppedTransfers.ContainsKey(splitMessage.TransferId))
			{
				Log.Trace?.Log($"[IncommingMessageService:ReceiveMessage] Received part '{splitMessage.SplitIndex}' of message '{splitMessage.TransferId}' which was recently given up on.");
				RecentlyDroppedTransfers[splitMessage.TransferId] = DateTimeOffset.UtcNow;
				return;
			}
			if (!MessageCache.TryGetValue(splitMessage.TransferId, out var value))
			{
				value = (MessageCache[splitMessage.TransferId] = new IncomingMessage(splitMessage));
			}
			else
			{
				value.AddPackage(splitMessage);
			}
			Log.Development?.Log($"[IncommingMessageService:ReceiveMessage] Missing splits {value.MissingSplits.Count} for transfer '{splitMessage.TransferId}'");
			if (value.MissingSplits.Count != 0)
			{
				return;
			}
			IMessage item = SplitMessageManager.CombineAndUnpackSplits(messageType, value.Splits);
			MessageCache.Remove(value.TransferId);
			lock (ReceivedMessages)
			{
				ReceivedMessages.Enqueue(item);
			}
		}
	}
	internal class OutgoingMessage
	{
		public ZPackage Package { get; set; }

		public ZRpc ZRpc { get; set; }

		public string Target { get; set; }

		public int Retries { get; set; }
	}
	internal static class OutgoingMessageService
	{
		[HarmonyPatch(typeof(ZNetPeer))]
		internal static class Cleanup
		{
			[HarmonyPatch("Dispose")]
			[HarmonyPrefix]
			private static void Dispose(ZNetPeer __instance)
			{
				string endPointString = __instance.m_socket.GetEndPointString();
				lock (SocketQueues)
				{
					SocketQueues.Remove(endPointString);
				}
			}
		}

		private const int MaxQueueSizeForDispatch = 5000;

		private static Dictionary<string, Queue<OutgoingMessage>> SocketQueues { get; }

		private static BinaryFormatter BinaryFormatter { get; }

		static OutgoingMessageService()
		{
			SocketQueues = new Dictionary<string, Queue<OutgoingMessage>>();
			BinaryFormatter = new BinaryFormatter();
			LifecycleManager.OnZnetUpdate += Dispatch;
			LifecycleManager.SubscribeToWorldInit(delegate
			{
				SocketQueues.Clear();
			});
		}

		public static void AddToQueue(IMessage dto, string rpcRoute, ZRpc zrpc)
		{
			AddToQueueAsync(dto, rpcRoute, zrpc);
		}

		private static Task AddToQueueAsync(IMessage message, string rpcRoute, ZRpc zrpc)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			message.Initialize();
			List<SplitMessage> list = SplitMessageManager.Pack(message);
			List<OutgoingMessage> list2 = new List<OutgoingMessage>();
			foreach (SplitMessage item in list)
			{
				using MemoryStream memoryStream = new MemoryStream();
				BinaryFormatter.Serialize(memoryStream, item);
				ZPackage val = new ZPackage();
				val.Write(memoryStream.ToArray());
				list2.Add(new OutgoingMessage
				{
					Package = val,
					ZRpc = zrpc,
					Target = rpcRoute
				});
			}
			string endPointString = zrpc.GetSocket().GetEndPointString();
			lock (SocketQueues)
			{
				if (!SocketQueues.TryGetValue(endPointString, out var value))
				{
					value = (SocketQueues[endPointString] = new Queue<OutgoingMessage>());
				}
				foreach (OutgoingMessage item2 in list2)
				{
					value.Enqueue(item2);
				}
			}
			return Task.CompletedTask;
		}

		private static void Dispatch()
		{
			List<ZNetPeer> peers = ZNet.instance.GetPeers();
			int count = peers.Count;
			for (int i = 0; i < count; i++)
			{
				DispatchSocket(peers[i].m_socket);
			}
		}

		private static void DispatchSocket(ISocket socket)
		{
			if (socket == null)
			{
				return;
			}
			string endPointString = socket.GetEndPointString();
			Queue<OutgoingMessage> value;
			lock (SocketQueues)
			{
				if (!SocketQueues.TryGetValue(endPointString, out value) || value.Count == 0)
				{
					return;
				}
			}
			int sendQueueSize = socket.GetSendQueueSize();
			if (sendQueueSize > 5000)
			{
				Log.Development?.Log("Package queue size: " + sendQueueSize);
				return;
			}
			OutgoingMessage outgoingMessage;
			lock (SocketQueues)
			{
				outgoingMessage = value.Dequeue();
			}
			try
			{
				outgoingMessage.ZRpc.Invoke(outgoingMessage.Target, new object[1] { outgoingMessage.Package });
				Log.Trace?.Log($"Sending package with size '{outgoingMessage.Package.Size()}' to '{outgoingMessage.Target}'");
			}
			catch (Exception e)
			{
				if (outgoingMessage.Retries > 3)
				{
					Log.Warning?.Log("Error while trying to send package. Too many retries, will stop trying.", e);
					return;
				}
				lock (SocketQueues)
				{
					outgoingMessage.Retries++;
					value.Enqueue(outgoingMessage);
				}
			}
		}
	}
}
namespace ThatCore.Logging
{
	internal class BepInExLogger : ILogger
	{
		private readonly ManualLogSource _logger;

		public BepInExLogger(ManualLogSource logger)
		{
			_logger = logger;
		}

		public void LogTrace(string message)
		{
			_logger.LogDebug((object)message);
		}

		public void LogDebug(string message)
		{
			_logger.LogInfo((object)message);
		}

		public void LogInfo(string message)
		{
			_logger.LogMessage((object)message);
		}

		public void LogWarning(string message)
		{
			_logger.LogWarning((object)message);
		}

		public void LogError(string message)
		{
			_logger.LogError((object)message);
		}

		public void Log(string message, LogLevel level)
		{
			switch (level)
			{
			case LogLevel.Error:
				_logger.LogError((object)message);
				break;
			case LogLevel.Warning:
				_logger.LogWarning((object)message);
				break;
			case LogLevel.Info:
				_logger.LogMessage((object)message);
				break;
			case LogLevel.Debug:
				_logger.LogInfo((object)message);
				break;
			case LogLevel.Trace:
				_logger.LogDebug((object)message);
				break;
			}
		}
	}
	internal class ConsoleLogger : ILogger
	{
		public void LogTrace(string message)
		{
			Console.WriteLine("[Trace  : ThatCore] " + message);
		}

		public void LogDebug(string message)
		{
			Console.WriteLine("[Debug  : ThatCore] " + message);
		}

		public void LogInfo(string message)
		{
			Console.WriteLine("[Info   : ThatCore] " + message);
		}

		public void LogWarning(string message)
		{
			Console.WriteLine("[Warning: ThatCore] " + message);
		}

		public void LogError(string message)
		{
			Console.WriteLine("[Error  : ThatCore] " + message);
		}

		public void Log(string message, LogLevel level)
		{
			switch (level)
			{
			case LogLevel.Error:
				LogError(message);
				break;
			case LogLevel.Warning:
				LogWarning(message);
				break;
			case LogLevel.Info:
				LogInfo(message);
				break;
			case LogLevel.Debug:
				LogDebug(message);
				break;
			case LogLevel.Trace:
				LogTrace(message);
				break;
			}
		}
	}
	internal interface ILogger
	{
		void LogTrace(string message);

		void LogDebug(string message);

		void LogInfo(string message);

		void LogWarning(string message);

		void LogError(string message);

		void Log(string message, LogLevel logLevel);
	}
	internal static class Log
	{
		private static ILogger _logger;

		private static LogWriter _traceLogger;

		private static LogWriter _debugLogger;

		private static LogWriter _infoLogger;

		private static LogWriter _warningLogger;

		private static LogWriter _errorLogger;

		public static bool DevelopmentEnabled { get; set; }

		public static bool TraceEnabled { get; set; }

		public static bool DebugEnabled { get; set; }

		public static LogWriter Development
		{
			get
			{
				if (!DevelopmentEnabled)
				{
					return null;
				}
				return _debugLogger;
			}
		}

		public static LogWriter Trace
		{
			get
			{
				if (!TraceEnabled)
				{
					return null;
				}
				return _traceLogger;
			}
		}

		public static LogWriter Debug
		{
			get
			{
				if (!DebugEnabled)
				{
					return null;
				}
				return _debugLogger;
			}
		}

		public static LogWriter Info => _infoLogger;

		public static LogWriter Warning => _warningLogger;

		public static LogWriter Error => _errorLogger;

		public static void SetLogger(ILogger logger)
		{
			_logger = logger;
			_traceLogger = new LogWriter(logger, LogLevel.Trace);
			_debugLogger = new LogWriter(logger, LogLevel.Debug);
			_infoLogger = new LogWriter(logger, LogLevel.Info);
			_warningLogger = new LogWriter(logger, LogLevel.Warning);
			_errorLogger = new LogWriter(logger, LogLevel.Error);
		}
	}
	internal class LogWriter
	{
		private ILogger _logger;

		private LogLevel _logLevel;

		public LogWriter(ILogger logger, LogLevel logLevel)
		{
			_logger = logger;
			_logLevel = logLevel;
		}

		public void Log(string message)
		{
			_logger.Log(message, _logLevel);
		}

		public void Log(string message, Exception e)
		{
			_logger.Log(message + "\n" + e?.Message + "\n" + e?.StackTrace, _logLevel);
		}
	}
	internal enum LogLevel
	{
		Invalid,
		Error,
		Warning,
		Info,
		Debug,
		Trace
	}
}
namespace ThatCore.Extensions
{
	internal static class CodeInstructionExtensions
	{
		public static CodeInstruction GetLdlocFromStLoc(this CodeInstruction instruction)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			if (instruction.opcode == OpCodes.Stloc_0)
			{
				return new CodeInstruction(OpCodes.Ldloc_0, (object)null);
			}
			if (instruction.opcode == OpCodes.Stloc_1)
			{
				return new CodeInstruction(OpCodes.Ldloc_1, (object)null);
			}
			if (instruction.opcode == OpCodes.Stloc_2)
			{
				return new CodeInstruction(OpCodes.Ldloc_2, (object)null);
			}
			if (instruction.opcode == OpCodes.Stloc_3)
			{
				return new CodeInstruction(OpCodes.Ldloc_3, (object)null);
			}
			if (instruction.opcode == OpCodes.Stloc_S)
			{
				return new CodeInstruction(OpCodes.Ldloca_S, instruction.operand);
			}
			if (instruction.opcode == OpCodes.Stloc)
			{
				return new CodeInstruction(OpCodes.Ldloc, instruction.operand);
			}
			throw new ArgumentException($"Unexpected instruction '{instruction}' encountered. Expected an {OpCodes.Stloc}.");
		}

		public static CodeInstruction GetStlocFromLdloc(this CodeInstruction instruction)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			if (instruction.opcode == OpCodes.Ldloc_0)
			{
				return new CodeInstruction(OpCodes.Stloc_0, (object)null);
			}
			if (instruction.opcode == OpCodes.Ldloc_1)
			{
				return new CodeInstruction(OpCodes.Stloc_1, (object)null);
			}
			if (instruction.opcode == OpCodes.Ldloc_2)
			{
				return new CodeInstruction(OpCodes.Stloc_2, (object)null);
			}
			if (instruction.opcode == OpCodes.Ldloc_3)
			{
				return new CodeInstruction(OpCodes.Stloc_3, (object)null);
			}
			if (instruction.opcode == OpCodes.Ldloca_S)
			{
				return new CodeInstruction(OpCodes.Stloc_S, instruction.operand);
			}
			if (instruction.opcode == OpCodes.Ldloc)
			{
				return new CodeInstruction(OpCodes.Stloc, instruction.operand);
			}
			throw new ArgumentException($"Unexpected instruction '{instruction}' encountered. Expected an {OpCodes.Ldloc}.");
		}
	}
	internal static class CodeMatcherExtensions
	{
		public static CodeMatcher GetPosition(this CodeMatcher codeMatcher, out int position)
		{
			position = codeMatcher.Pos;
			return codeMatcher;
		}

		public static CodeMatcher InsertAndAdvance(this CodeMatcher codeMatcher, OpCode opcode, object operand = null)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1]
			{
				new CodeInstruction(opcode, operand)
			});
			return codeMatcher;
		}

		public static CodeMatcher InsertAndAdvance(this CodeMatcher codeMatcher, MethodInfo method)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1]
			{
				new CodeInstruction(OpCodes.Call, (object)method)
			});
			return codeMatcher;
		}

		public static CodeMatcher InsertAndAdvance<T>(this CodeMatcher codeMatcher, string methodName)
		{
			return codeMatcher.InsertAndAdvance(AccessTools.Method(typeof(T), methodName, (Type[])null, (Type[])null));
		}

		public static CodeMatcher InsertAndAdvance<T>(this CodeMatcher codeMatcher, T action) where T : Delegate
		{
			return codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<T>(action) });
		}

		public static CodeMatcher GetOpcode(this CodeMatcher codeMatcher, out OpCode opcode)
		{
			opcode = codeMatcher.Opcode;
			return codeMatcher;
		}

		public static CodeMatcher GetInstruction(this CodeMatcher codeMatcher, out CodeInstruction instruction)
		{
			instruction = codeMatcher.Instruction;
			return codeMatcher;
		}

		public static CodeMatcher GetOperand(this CodeMatcher codeMatcher, out object operand)
		{
			operand = codeMatcher.Operand;
			return codeMatcher;
		}

		public static CodeMatcher Print(this CodeMatcher codeMatcher, int before, int after)
		{
			if (!Log.DevelopmentEnabled)
			{
				return codeMatcher;
			}
			for (int i = -before; i <= after; i++)
			{
				int num = i;
				int num2 = codeMatcher.Pos + num;
				if (num2 > 0)
				{
					if (num2 >= codeMatcher.Length)
					{
						break;
					}
					try
					{
						CodeInstruction val = codeMatcher.InstructionAt(num);
						Log.Development?.Log($"[{num}] " + ((object)val).ToString());
					}
					catch (Exception ex)
					{
						Log.Error?.Log(ex.Message);
					}
				}
			}
			return codeMatcher;
		}
	}
	internal static class DictionaryExtensions
	{
		public static TValue GetOrCreate<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key) where TValue : new()
		{
			if (!dic.TryGetValue(key, out var value))
			{
				value = (dic[key] = new TValue());
			}
			return value;
		}

		public static TValue GetOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key)
		{
			if (dic.TryGetValue(key, out var value))
			{
				return value;
			}
			return default(TValue);
		}
	}
	internal static class EnumExtensions
	{
		public static List<TEnum> Split<TEnum>(this TEnum enumVal) where TEnum : struct, Enum
		{
			int num = Convert.ToInt32(enumVal);
			List<TEnum> list = new List<TEnum>();
			foreach (TEnum value in Enum.GetValues(typeof(TEnum)))
			{
				if ((num & Convert.ToInt32(value)) > 0)
				{
					list.Add(value);
				}
			}
			return list;
		}
	}
	internal static class EventExtensions
	{
		public static void Raise<T>(this Action<T> events, T arg, string messageOnError = null)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<T> action = (Action<T>)invocationList[i];
				try
				{
					action(arg);
				}
				catch (Exception e)
				{
					string text = action.Method.DeclaringType.Assembly.GetName().Name + "." + action.Method.DeclaringType.Name + "." + action.Method.Name;
					Log.Error?.Log("[" + text + "]: " + (messageOnError ?? string.Empty), e);
				}
			}
		}

		public static void Raise(this Action events, string messageOnError = null)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action = (Action)invocationList[i];
				try
				{
					action();
				}
				catch (Exception e)
				{
					string text = action.Method.DeclaringType.Assembly.GetName().Name + "." + action.Method.DeclaringType.Name + "." + action.Method.Name;
					Log.Error?.Log("[" + text + "]: " + (messageOnError ?? string.Empty), e);
				}
			}
		}
	}
	internal static class ExpressionExtensions
	{
		public static PropertyInfo GetPropertyInfo<T, K>(this Expression<Func<T, K>> propertySelector)
		{
			return (propertySelector.Body as MemberExpression)?.Member as PropertyInfo;
		}
	}
	internal static class ICollectionExtensions
	{
		public static void AddNullSafe<T>(this ICollection<T> collection, T newEntry) where T : class
		{
			if (collection != null && newEntry != null)
			{
				Object val = (Object)(object)((newEntry is Object) ? newEntry : null);
				if (val == null || Object.op_Implicit(val))
				{
					collection.Add(newEntry);
				}
			}
		}

		public static string Join(this ICollection<float> list)
		{
			return list.Join((float x) => x.ToString(CultureInfo.InvariantCulture));
		}

		public static string Join(this ICollection<double> list)
		{
			return list.Join((double x) => x.ToString(CultureInfo.InvariantCulture));
		}

		public static string Join<T>(this ICollection<T> list)
		{
			if (list == null || list.Count == 0)
			{
				return "";
			}
			return string.Join(", ", list);
		}

		public static string Join<T>(this ICollection<T> list, Func<T, string> tostring)
		{
			if (list == null || list.Count == 0)
			{
				return string.Empty;
			}
			return string.Join(", ", list.Select((T x) => tostring(x)));
		}

		public static string Join<T, K>(this ICollection<T> list, Func<T, K> selector)
		{
			if (list == null || list.Count == 0)
			{
				return string.Empty;
			}
			return string.Join(", ", list.Select((T x) => selector(x)));
		}
	}
	internal static class IEnumerableExtensions
	{
		public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
		{
			foreach (T item in enumerable)
			{
				action(item);
			}
		}
	}
	internal static class ListExtensions
	{
		public static void AddOrReplaceByType<T, TEntry>(this List<T> list, TEntry entry) where T : class where TEntry : class, T
		{
			Type typeFromHandle = typeof(TEntry);
			int num = -1;
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].GetType() == typeFromHandle)
				{
					num = i;
					break;
				}
			}
			if (num < 0)
			{
				list.Add((T)entry);
			}
			else
			{
				list[num] = (T)entry;
			}
		}

		public static void AddOrConfigureByType<T, TEntry>(this List<T> list, Action<TEntry> configure) where T : class where TEntry : T, new()
		{
			TEntry val = list.OfType<TEntry>().FirstOrDefault();
			if (val != null)
			{
				configure(val);
				return;
			}
			TEntry val2 = new TEntry();
			configure(val2);
			list.Add((T)(object)val2);
		}
	}
	internal enum Separator
	{
		Comma,
		Slash,
		Dot,
		Newline
	}
	internal static class StringExtensions
	{
		private static readonly char[] Comma = new char[1] { ',' };

		private static readonly char[] Slash = new char[2] { '/', '\\' };

		private static readonly char[] Dot = new char[1] { '.' };

		private static readonly char[] Newline = new char[1] { '\n' };

		public static List<string> SplitBy(this string value, Separator separator, bool toUpper = false)
		{
			return value.SplitBy(separator switch
			{
				Separator.Comma => Comma, 
				Separator.Slash => Slash, 
				Separator.Dot => Dot, 
				Separator.Newline => Newline, 
				_ => throw new NotSupportedException("Separator"), 
			}, toUpper).ToList();
		}

		public static List<string> SplitByComma(this string value, bool toUpper = false)
		{
			return value.SplitBy(Comma, toUpper).ToList();
		}

		public static string[] SplitBySlash(this string value, bool toUpper = false)
		{
			return value.SplitBy(Slash, toUpper).ToArray();
		}

		public static IEnumerable<string> SplitBy(this string value, char[] chars, bool toUpper = false)
		{
			if (value == null)
			{
				return Enumerable.Empty<string>();
			}
			string[] array = value.Trim().Split(chars, StringSplitOptions.RemoveEmptyEntries);
			if (array == null || array.Length == 0)
			{
				return Enumerable.Empty<string>();
			}
			return array.Select(Clean);
			string Clean(string x)
			{
				string text = x.Trim();
				if (toUpper)
				{
					return text.ToUpperInvariant();
				}
				return text;
			}
		}

		public static bool IsEmpty(this string s)
		{
			return string.IsNullOrWhiteSpace(s);
		}

		public static bool IsNotEmpty(this string s)
		{
			if (string.IsNullOrWhiteSpace(s))
			{
				return false;
			}
			return true;
		}

		public static string Normalize(this string s)
		{
			return s?.Trim() ?? string.Empty;
		}
	}
	internal static class StringHashExtensions
	{
		public static int HashInteger(this string str)
		{
			int num = 5381;
			int num2 = num;
			for (int i = 0; i < str.Length && str[i] != 0; i += 2)
			{
				num = ((num << 5) + num) ^ str[i];
				if (i == str.Length - 1 || str[i + 1] == '\0')
				{
					break;
				}
				num2 = ((num2 << 5) + num2) ^ str[i + 1];
			}
			return num + num2 * 1566083941;
		}

		public static long Hash(this string str)
		{
			long num = 5381L;
			long num2 = num;
			for (int i = 0; i < str.Length && str[i] != 0; i += 2)
			{
				num = ((num << 5) + num) ^ str[i];
				if (i == str.Length - 1 || str[i + 1] == '\0')
				{
					break;
				}
				num2 = ((num2 << 5) + num2) ^ str[i + 1];
			}
			return num + num2 * 1566083941;
		}

		public static long Hash(this IEnumerable<string> strs)
		{
			long num = 0L;
			foreach (long item in from x in strs.Select((string x) => x.Hash()).Distinct()
				orderby x
				select x)
			{
				num += item;
			}
			return num;
		}
	}
	internal static class UnityObjectExtensions
	{
		private static Regex NameRegex = new Regex("^[^$(]*(?=$|[(])", RegexOptions.Compiled);

		public static bool IsNull(this Object obj)
		{
			if (obj == (Object)null || !Object.op_Implicit(obj))
			{
				return true;
			}
			return false;
		}

		public static bool IsNotNull(this Object obj)
		{
			if (obj != (Object)null && Object.op_Implicit(obj))
			{
				return true;
			}
			return false;
		}

		public static string GetName(this Object obj)
		{
			if (!obj.IsNotNull())
			{
				return string.Empty;
			}
			return obj.name;
		}

		public static string GetCleanedName(this Object obj, bool toUpper = false)
		{
			if (obj.IsNull())
			{
				return null;
			}
			Match match = NameRegex.Match(obj.name);
			if (!match.Success)
			{
				return null;
			}
			string text = match.Value.Trim();
			if (toUpper)
			{
				text = text?.ToUpperInvariant();
			}
			return text;
		}
	}
}
namespace ThatCore.Config
{
	public interface IFileMappingBuilder
	{
	}
	public interface IFileMappingBuilder<TTarget> : IFileMappingBuilder
	{
		IFileMappingBuilder<TTarget> Map<TOption>(string configName, TOption defaultValue, string description, Action<TOption, TTarget> fileToTargetMapping);

		IFileMappingBuilder<TSubTarget> Using<TSubTarget>(Func<TTarget, TSubTarget> selector, bool skipIfNull = true);
	}
	public interface IHaveOptions<TTarget, TSource>
	{
		IOptionBuilder<TTarget, TSource> AddOption();
	}
	public interface IOptionBuilder<TTarget, TSource> : IOptionBuilderActions<TTarget, TSource>
	{
		IOptionBuilder<TTarget, TSource> AddOption();
	}
	public interface IOptionBuilderActions<TTarget, TSource>
	{
		IOptionBuilder<TTarget, TSource> FromFile(Action<IFileMappingBuilder<TTarget>> configure);

		IOptionBuilder<TTarget, TSource> ToFile(Action<ISourceMappingBuilder<TSource>> configure);
	}
	public interface ISourceMappingBuilder
	{
	}
	public interface ISourceMappingBuilder<TSource> : ISourceMappingBuilder
	{
		ISourceMappingBuilder<TSource> Map<T>(string configName, Func<TSource, T> selector);

		ISourceMappingBuilder<TSubSource> Using<TSubSource>(Func<TSource, TSubSource> selector, bool skipIfNull = true);
	}
}
namespace ThatCore.Config.Toml
{
	internal interface ITomlSetting
	{
		string Name { get; }

		string Description { get; }

		Type SettingType { get; }

		bool IsSet { get; set; }

		object GetValue();

		ITomlSetting Clone(bool includeValue = true);
	}
	internal interface ITomlSetting<T> : ITomlSetting
	{
		T Value { get; set; }

		T DefaultValue { get; set; }
	}
	internal class TomlConfig
	{
		public Dictionary<string, ITomlSetting> Settings = new Dictionary<string, ITomlSetting>();

		public Dictionary<TomlPathSegment, TomlConfig> Sections = new Dictionary<TomlPathSegment, TomlConfig>();

		public TomlPathSegment PathSegment { get; set; } = TomlPathSegment.Default;

		public TomlConfig CreateSubsection(TomlPathSegment segment)
		{
			if (Sections.TryGetValue(segment, out var value))
			{
				return value;
			}
			TomlPathSegment pathSegment = new TomlPathSegment(segment.SegmentType, segment.Name, PathSegment);
			return Sections[segment] = new TomlConfig
			{
				PathSegment = pathSegment
			};
		}

		public void SetSettings(IReadOnlyDictionary<string, ITomlSetting> settings)
		{
			foreach (KeyValuePair<string, ITomlSetting> setting in settings)
			{
				Settings[setting.Key] = setting.Value;
			}
		}

		public void SetSetting(string name, ITomlSetting setting)
		{
			Settings[name] = setting;
		}

		public T GetSettingValue<T>(string name, T defaultValue = default(T))
		{
			if (Settings.TryGetValue(name, out var value) && value is TomlSetting<T> tomlSetting)
			{
				return tomlSetting;
			}
			return defaultValue;
		}

		public TomlSetting<T> GetSetting<T>(string name)
		{
			if (Settings.TryGetValue(name, out var value) && value is TomlSetting<T> result)
			{
				return result;
			}
			return null;
		}

		public bool TryGetSetting<T>(string name, out TomlSetting<T> setting)
		{
			if (Settings.TryGetValue(name, out var value) && value is TomlSetting<T> tomlSetting)
			{
				setting = tomlSetting;
				return true;
			}
			setting = null;
			return false;
		}

		public TomlConfig SetSetting<T>(string name, T value, string description = null)
		{
			if (Settings.TryGetValue(name, out var value2) && value2 is TomlSetting<T> tomlSetting)
			{
				tomlSetting.Set(value);
			}
			else
			{
				Settings[name] = new TomlSetting<T>(name, default(T), description)
				{
					Value = value
				};
			}
			return this;
		}

		public static TomlConfig FindSection(TomlConfig root, List<TomlPathSegment> path)
		{
			TomlConfig value = root;
			foreach (TomlPathSegment item in path)
			{
				if (!value.Sections.TryGetValue(item, out value))
				{
					return null;
				}
			}
			return value;
		}
	}
	internal static class TomlConfigMerger
	{
		public static void Merge(TomlConfig source, TomlConfig destination)
		{
			MergeSettings(source, destination);
			foreach (KeyValuePair<TomlPathSegment, TomlConfig> section in source.Sections)
			{
				TomlConfig destination2 = destination.CreateSubsection(section.Key);
				Merge(section.Value, destination2);
			}
		}

		private static void MergeSettings(TomlConfig source, TomlConfig destination)
		{
			foreach (KeyValuePair<string, ITomlSetting> setting in source.Settings)
			{
				if (setting.Value.IsSet)
				{
					destination.SetSetting(setting.Key, setting.Value);
				}
			}
		}
	}
	internal static class TomlConfigWriter
	{
		public static string WriteToString(TomlConfig config, TomlWriterSettings settings)
		{
			StringBuilder stringBuilder = new StringBuilder();
			if (!string.IsNullOrWhiteSpace(settings.Header))
			{
				foreach (string item in settings.Header.SplitBy(Separator.Newline))
				{
					stringBuilder.AppendLine(item);
				}
				stringBuilder.AppendLine();
			}
			WriteSubsectionsRecursively(stringBuilder, config, settings);
			return stringBuilder.ToString();
		}

		private static void WriteSubsectionsRecursively(StringBuilder builder, TomlConfig config, TomlWriterSettings settings)
		{
			foreach (KeyValuePair<TomlPathSegment, TomlConfig> section in config.Sections)
			{
				Dictionary<string, ITomlSetting> settings2 = section.Value.Settings;
				if (settings2 != null && settings2.Count > 0)
				{
					builder.AppendLine("[" + section.Value.PathSegment.GetPath() + "]");
					WriteSettings(builder, section.Value, settings);
					builder.AppendLine();
				}
				WriteSubsectionsRecursively(builder, section.Value, settings);
			}
		}

		private static void WriteSettings(StringBuilder builder, TomlConfig config, TomlWriterSettings options)
		{
			foreach (KeyValuePair<string, ITomlSetting> setting in config.Settings)
			{
				string key = setting.Key;
				ITomlSetting value = setting.Value;
				if (!value.IsSet)
				{
					continue;
				}
				if (options != null && options.AddComments)
				{
					foreach (string item in value.Description.SplitBy(Separator.Newline))
					{
						builder.AppendLine("# " + item);
					}
				}
				try
				{
					string text = value.Write();
					builder.AppendLine(key + " = " + text);
				}
				catch (Exception e)
				{
					Log.Warning?.Log("Error while attempting to write setting '" + key + "' to file '" + options.FileName + "'. Skipping setting.", e);
				}
			}
		}
	}
	internal class TomlLine
	{
		public string FileName { get; set; }

		public int LineNr { get; set; }

		public string Value { get; set; }
	}
	internal static class TomlPath
	{
		public static TomlPathSegment Create(TomlPathSegmentType segmentType, string name = null)
		{
			return new TomlPathSegment(segmentType, name);
		}

		public static TomlPathSegment Chain(this TomlPathSegment parent, TomlPathSegmentType segmentType, string name = null)
		{
			return new TomlPathSegment(segmentType, name, parent);
		}
	}
	internal class TomlPathSegment : IEquatable<TomlPathSegment>
	{
		internal static TomlPathSegment Default { get; } = new TomlPathSegment(TomlPathSegmentType.Start);

		public string Name { get; }

		public string NormalizedName { get; }

		public TomlPathSegmentType SegmentType { get; }

		public TomlPathSegment Parent { get; }

		public TomlPathSegment(TomlPathSegmentType type, string name = null, TomlPathSegment parent = null)
		{
			Name = name;
			SegmentType = type;
			Parent = parent;
			NormalizedName = ThatCore.Extensions.StringExtensions.Normalize(Name);
		}

		public string GetPath()
		{
			StringBuilder stringBuilder = new StringBuilder();
			TomlPathSegment tomlPathSegment = this;
			do
			{
				if (tomlPathSegment.SegmentType != TomlPathSegmentType.Start)
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Insert(0, ".");
					}
					stringBuilder.Insert(0, tomlPathSegment.Name);
				}
				tomlPathSegment = tomlPathSegment.Parent;
			}
			while (tomlPathSegment != null);
			return stringBuilder.ToString();
		}

		public bool Equals(TomlPathSegment other)
		{
			if (base.Equals(other))
			{
				return true;
			}
			if (SegmentType == other.SegmentType)
			{
				return NormalizedName == other.NormalizedName;
			}
			return false;
		}

		public bool Equals(TomlPathSegment x, TomlPathSegment y)
		{
			return x.Equals(y);
		}

		public override int GetHashCode()
		{
			return NormalizedName.GetHashCode() + SegmentType.GetHashCode();
		}
	}
	internal static class TomlPathSegmentExtensions
	{
		public static TomlPathSegment FindClosest(this TomlPathSegment segment, TomlPathSegmentType segmentType)
		{
			while (segment != null)
			{
				if (segment.SegmentType == segmentType)
				{
					return segment;
				}
				segment = segment.Parent;
			}
			return null;
		}
	}
	internal enum TomlPathSegmentType
	{
		Invalid,
		Start,
		Named,
		Collection
	}
	internal class TomlSetting<T> : ITomlSetting<T>, ITomlSetting
	{
		private T value;

		public string Name { get; set; }

		public Type SettingType { get; } = typeof(T);

		public T Value
		{
			get
			{
				return value;
			}
			set
			{
				this.value = value;
				IsSet = true;
			}
		}

		public T DefaultValue { get; set; }

		public string Description { get; set; }

		public bool IsSet { get; set; }

		internal TomlSetting()
		{
		}

		public TomlSetting(string name, T defaultValue, string description = null)
		{
			Name = name;
			DefaultValue = defaultValue;
			Description = description;
		}

		public object GetValue()
		{
			return Value;
		}

		public void Set(T value)
		{
			Value = value;
			IsSet = true;
		}

		public static implicit operator T(TomlSetting<T> entry)
		{
			if (!entry.IsSet)
			{
				return entry.DefaultValue;
			}
			return entry.Value;
		}

		public ITomlSetting Clone(bool includeValue = true)
		{
			TomlSetting<T> tomlSetting = new TomlSetting<T>(Name, DefaultValue, Description);
			if (includeValue && IsSet)
			{
				tomlSetting.IsSet = true;
				tomlSetting.Value = Value;
			}
			return tomlSetting;
		}
	}
}
namespace ThatCore.Config.Toml.Writers
{
	internal class BoolWriter : ValueWriter<bool>
	{
		protected override string WriteInternal(ITomlSetting<bool> entry)
		{
			if (!entry.Value)
			{
				return "false";
			}
			return "true";
		}
	}
	internal class NullableBoolWriter : ValueWriter<bool?>
	{
		protected override string WriteInternal(ITomlSetting<bool?> entry)
		{
			if (entry.IsSet && entry.Value.HasValue)
			{
				if (!entry.Value.Value)
				{
					return "false";
				}
				return "true";
			}
			return string.Empty;
		}
	}
	internal class DoubleWriter : ValueWriter<double>
	{
		protected override string WriteInternal(ITomlSetting<double> entry)
		{
			return entry.Value.ToString(CultureInfo.InvariantCulture);
		}
	}
	internal class NullableDoubleWriter : ValueWriter<double?>
	{
		protected override string WriteInternal(ITomlSetting<double?> entry)
		{
			if (entry.IsSet && entry.Value.HasValue)
			{
				return entry.Value.Value.ToString(CultureInfo.InvariantCulture);
			}
			return string.Empty;
		}
	}
	internal class EnumListWriter<T> : ValueWriter<List<T>> where T : struct, Enum
	{
		protected override string WriteInternal(ITomlSetting<List<T>> entry)
		{
			if (entry.IsSet && entry.Value != null)
			{
				return GeneralExtensions.Join<string>(entry.Value.Select((T x) => x.ToString()), (Func<string, string>)null, ", ");
			}
			return string.Empty;
		}
	}
	internal class EnumWriter<T> : ValueWriter<T> where T : struct, Enum
	{
		protected override string WriteInternal(ITomlSetting<T> entry)
		{
			return entry.Value.ToString();
		}
	}
	internal class NullableEnumWriter<T> : ValueWriter<T?> where T : struct, Enum
	{
		protected override string WriteInternal(ITomlSetting<T?> entry)
		{
			if (entry.IsSet && entry.Value.HasValue)
			{
				return entry.Value.Value.ToString();
			}
			return string.Empty;
		}
	}
	internal class FloatWriter : ValueWriter<float>
	{
		protected override string WriteInternal(ITomlSetting<float> entry)
		{
			return entry.Value.ToString(CultureInfo.InvariantCulture);
		}
	}
	internal class NullableFloatWriter : ValueWriter<float?>
	{
		protected override string WriteInternal(ITomlSetting<float?> entry)
		{
			if (entry.IsSet && entry.Value.HasValue)
			{
				return entry.Value.Value.ToString(CultureInfo.InvariantCulture);
			}
			return string.Empty;
		}
	}
	internal class IntListWriter : ValueWriter<List<int>>
	{
		protected override string WriteInternal(ITomlSetting<List<int>> entry)
		{
			if (entry.IsSet && entry.Value != null)
			{
				return GeneralExtensions.Join<string>(entry.Value.Select((int x) => x.ToString(CultureInfo.InvariantCulture)), (Func<string, string>)null, ", ");
			}
			return string.Empty;
		}
	}
	internal class IntWriter : ValueWriter<int>
	{
		protected override string WriteInternal(ITomlSetting<int> entry)
		{
			return entry.Value.ToString(CultureInfo.InvariantCulture);
		}
	}
	internal class NullableIntWriter : ValueWriter<int?>
	{
		protected override string WriteInternal(ITomlSetting<int?> entry)
		{
			if (entry.IsSet && entry.Value.HasValue)
			{
				return entry.Value.Value.ToString(CultureInfo.InvariantCulture);
			}
			return string.Empty;
		}
	}
	internal interface IValueWriter
	{
		string Write(ITomlSetting entry);
	}
	internal abstract class ValueWriter<T> : IValueWriter
	{
		protected Type WriterType { get; }

		protected ValueWriter()
		{
			WriterType = typeof(T);
		}

		public string Write(ITomlSetting entry)
		{
			if (entry is ITomlSetting<T> entry2)
			{
				return WriteInternal(entry2);
			}
			throw new InvalidOperationException("Unable to write config entry with type " + typeof(T).Name + " using writer " + GetType().Name + ".");
		}

		protected abstract string WriteInternal(ITomlSetting<T> entry);
	}
	internal class StringListWriter : ValueWriter<List<string>>
	{
		protected override string WriteInternal(ITomlSetting<List<string>> entry)
		{
			if (entry.IsSet && entry.Value != null)
			{
				return entry.Value.Join();
			}
			return string.Empty;
		}
	}
	internal class StringWriter : ValueWriter<string>
	{
		protected override string WriteInternal(ITomlSetting<string> entry)
		{
			return entry.Value ?? string.Empty;
		}
	}
	internal static class TomlWriterFactory
	{
		private static Dictionary<Type, IValueWriter> writers;

		private static Type ListType;

		private static Type EnumListWriterType;

		private static Type EnumWriterType;

		private static Type NullableEnumWriterType;

		static TomlWriterFactory()
		{
			writers = new Dictionary<Type, IValueWriter>();
			ListType = typeof(List<>);
			EnumListWriterType = typeof(EnumListWriter<>);
			EnumWriterType = typeof(EnumWriter<>);
			NullableEnumWriterType = typeof(NullableEnumWriter<>);
			writers = new Dictionary<Type, IValueWriter>
			{
				{
					typeof(bool),
					new BoolWriter()
				},
				{
					typeof(bool?),
					new NullableBoolWriter()
				},
				{
					typeof(double),
					new DoubleWriter()
				},
				{
					typeof(double?),
					new NullableDoubleWriter()
				},
				{
					typeof(float),
					new FloatWriter()
				},
				{
					typeof(float?),
					new NullableFloatWriter()
				},
				{
					typeof(int),
					new IntWriter()
				},
				{
					typeof(int?),
					new NullableIntWriter()
				},
				{
					typeof(string),
					new StringWriter()
				},
				{
					typeof(List<int>),
					new IntListWriter()
				},
				{
					typeof(List<string>),
					new StringListWriter()
				}
			};
		}

		public static string Write<T>(this T entry) where T : ITomlSetting
		{
			if (writers.TryGetValue(entry.SettingType, out var value))
			{
				return value.Write(entry);
			}
			if (entry.SettingType.IsGenericType && entry.SettingType.GetGenericTypeDefinition() == ListType)
			{
				if (entry.SettingType.GenericTypeArguments.First().IsEnum)
				{
					Type type = entry.SettingType.GenericTypeArguments.First();
					value = (IValueWriter)Activator.CreateInstance(EnumListWriterType.MakeGenericType(type));
					writers[entry.SettingType] = value;
					return value.Write(entry);
				}
				throw new NotSupportedException("Unable to write config entries of type '" + entry.SettingType.Name + "'.");
			}
			if (entry.SettingType.IsEnum)
			{
				value = (IValueWriter)Activator.CreateInstance(EnumWriterType.MakeGenericType(entry.SettingType));
				writers[entry.SettingType] = value;
				return value.Write(entry);
			}
			Type? underlyingType = Nullable.GetUnderlyingType(entry.SettingType);
			if ((object)underlyingType != null && underlyingType.IsEnum)
			{
				Type underlyingType2 = Nullable.GetUnderlyingType(entry.SettingType);
				value = (IValueWriter)Activator.CreateInstance(NullableEnumWriterType.MakeGenericType(underlyingType2));
				writers[entry.SettingType] = value;
				return value.Write(entry);
			}
			throw new NotSupportedException();
		}
	}
	internal class TomlWriterSettings
	{
		public string Header { get; set; }

		public bool AddComments { get; set; }

		public string FileName { get; set; }
	}
}
namespace ThatCore.Config.Toml.Schema
{
	internal interface ITomlSchemaLayer
	{
		ITomlSchemaNode ParentNode { get; }

		ITomlSchemaCollectionLayer AsIndexed();

		ITomlSchemaNamedLayer AsNamed();
	}
	internal interface ITomlSchemaNamedLayer : ITomlSchemaLayer
	{
		Dictionary<string, ITomlSchemaNode> Nodes { get; }
	}
	internal interface ITomlSchemaCollectionLayer : ITomlSchemaLayer
	{
		ITomlSchemaNode Node { get; }
	}
	internal interface ITomlSchemaNode
	{
		Dictionary<string, ITomlSetting> Settings { get; }

		ITomlSchemaLayer NextLayer { get; }

		ITomlSchemaLayer ParentLayer { get; }

		TomlPathSegment PathSegment { get; }
	}
	internal abstract class TomlSchemaLayer : ITomlSchemaLayer
	{
		public ITomlSchemaNode ParentNode { get; set; }

		public ITomlSchemaCollectionLayer AsIndexed()
		{
			return this as ITomlSchemaCollectionLayer;
		}

		public ITomlSchemaNamedLayer AsNamed()
		{
			return this as ITomlSchemaNamedLayer;
		}
	}
	internal class SchemaNamedLayer : TomlSchemaLayer, ITomlSchemaNamedLayer, ITomlSchemaLayer
	{
		public Dictionary<string, ITomlSchemaNode> Nodes { get; set; } = new Dictionary<string, ITomlSchemaNode>();
	}
	internal class NamedSchemaNode : ITomlSchemaNode
	{
		public string Name { get; set; }

		public Dictionary<string, ITomlSetting> Settings { get; set; } = new Dictionary<string, ITomlSetting>();

		public ITomlSchemaLayer NextLayer { get; set; }

		public ITomlSchemaLayer ParentLayer { get; set; }

		public TomlPathSegment PathSegment { get; set; }

		public NamedSchemaNode(TomlPathSegment segment)
		{
			Name = segment.Name;
			PathSegment = segment;
		}
	}
	internal class SchemaCollectionLayer : TomlSchemaLayer, ITomlSchemaCollectionLayer, ITomlSchemaLayer
	{
		public ITomlSchemaNode Node { get; set; } = new SchemaCollectionNode();
	}
	internal class SchemaCollectionNode : ITomlSchemaNode
	{
		public Dictionary<string, ITomlSetting> Settings { get; set; }

		public ITomlSchemaLayer NextLayer { get; set; }

		public ITomlSchemaLayer ParentLayer { get; set; }

		public TomlPathSegment PathSegment { get; set; }
	}
	internal interface ITomlSchemaBuilder
	{
		ITomlSchemaLayerBuilder FirstLayer { get; }

		ITomlNamedSchemaLayerBuilder SetLayerAsNamed();

		ITomlSchemaCollectionLayerBuilder SetLayerAsCollection();

		ITomlSchemaLayer Build();
	}
	internal interface ITomlSchemaLayerBuilder
	{
		ITomlSchemaLayer Build(ITomlSchemaNode parent);
	}
	internal interface ITomlNamedSchemaLayerBuilder : ITomlSchemaLayerBuilder
	{
		ITomlSchemaNodeBuilder AddNode(string name);

		ITomlSchemaNamedLayer BuildNamed(ITomlSchemaNode parent);
	}
	internal interface ITomlSchemaCollectionLayerBuilder : ITomlSchemaLayerBuilder
	{
		ITomlSchemaNodeBuilder GetNode();

		ITomlSchemaCollectionLayer BuildCollection(ITomlSchemaNode parent);
	}
	internal interface ITomlSchemaNodeBuilder
	{
		ITomlSchemaNodeBuilder AddSetting(ITomlSetting entry);

		ITomlSchemaNodeBuilder AddSetting<T>(string name, T defaultValue = default(T), string description = null);

		ITomlNamedSchemaLayerBuilder SetNextLayerAsNamed();

		ITomlSchemaCollectionLayerBuilder SetNextLayerAsCollection();

		ITomlSchemaNode Build(ITomlSchemaLayer parent);
	}
	internal class TomlSchemaBuilder : ITomlSchemaBuilder
	{
		private ITomlSchemaLayerBuilder _firstLayer = new TomlNamedSchemaLayerBuilder();

		public ITomlSchemaLayerBuilder FirstLayer => _firstLayer;

		public ITomlNamedSchemaLayerBuilder SetLayerAsNamed()
		{
			return (ITomlNamedSchemaLayerBuilder)(_firstLayer = new TomlNamedSchemaLayerBuilder());
		}

		public ITomlSchemaCollectionLayerBuilder SetLayerAsCollection()
		{
			return (ITomlSchemaCollectionLayerBuilder)(_firstLayer = new TomlSchemaCollectionLayerBuilder());
		}

		public ITomlSchemaLayer Build()
		{
			return _firstLayer.Build(null);
		}
	}
	internal class TomlNamedSchemaLayerBuilder : ITomlNamedSchemaLayerBuilder, ITomlSchemaLayerBuilder
	{
		private Dictionary<string, TomlSchemaNodeBuilder> Nodes { get; } = new Dictionary<string, TomlSchemaNodeBuilder>();

		public ITomlSchemaNodeBuilder AddNode(string name)
		{
			string key = ThatCore.Extensions.StringExtensions.Normalize(name);
			if (Nodes.TryGetValue(key, out var value))
			{
				return value;
			}
			return Nodes[key] = new TomlSchemaNodeBuilder(name);
		}

		public ITomlSchemaLayer Build(ITomlSchemaNode parent)
		{
			return BuildNamed(parent);
		}

		public ITomlSchemaNamedLayer BuildNamed(ITomlSchemaNode parent)
		{
			SchemaNamedLayer layer = new SchemaNamedLayer();
			layer.ParentNode = parent;
			layer.Nodes = Nodes.ToDictionary((KeyValuePair<string, TomlSchemaNodeBuilder> x) => ThatCore.Extensions.StringExtensions.Normalize(x.Value.Name), (KeyValuePair<string, TomlSchemaNodeBuilder> x) => x.Value.Build(layer));
			return layer;
		}
	}
	internal class TomlSchemaCollectionLayerBuilder : ITomlSchemaCollectionLayerBuilder, ITomlSchemaLayerBuilder
	{
		private TomlSchemaNodeBuilder Node { get; } = new TomlSchemaNodeBuilder();

		public ITomlSchemaNodeBuilder GetNode()
		{
			return Node;
		}

		public ITomlSchemaLayer Build(ITomlSchemaNode parent)
		{
			return BuildCollection(parent);
		}

		public ITomlSchemaCollectionLayer BuildCollection(ITomlSchemaNode parent)
		{
			SchemaCollectionLayer schemaCollectionLayer = new SchemaCollectionLayer();
			schemaCollectionLayer.ParentNode = parent;
			schemaCollectionLayer.Node = Node.Build(schemaCollectionLayer);
			return schemaCollectionLayer;
		}
	}
	internal class TomlSchemaNodeBuilder : ITomlSchemaNodeBuilder
	{
		public string Name { get; }

		public Dictionary<string, ITomlSetting> Settings { get; } = new Dictionary<string, ITomlSetting>();

		public ITomlSchemaLayerBuilder NextLayer { get; set; }

		public TomlSchemaNodeBuilder(string name = null)
		{
			Name = name;
		}

		public ITomlSchemaNodeBuilder AddSetting(ITomlSetting entry)
		{
			Settings[ThatCore.Extensions.StringExtensions.Normalize(entry.Name)] = entry;
			return this;
		}

		public ITomlSchemaNodeBuilder AddSetting<T>(string name, T defaultValue = default(T), string description = null)
		{
			TomlSetting<T> tomlSetting = new TomlSetting<T>(name, defaultValue, description)
			{
				Value = defaultValue
			};
			Settings[ThatCore.Extensions.StringExtensions.Normalize(tomlSetting.Name)] = tomlSetting;
			return this;
		}

		public ITomlSchemaCollectionLayerBuilder SetNextLayerAsCollection()
		{
			return (ITomlSchemaCollectionLayerBuilder)(NextLayer = new TomlSchemaCollectionLayerBuilder());
		}

		public ITomlNamedSchemaLayerBuilder SetNextLayerAsNamed()
		{
			return (ITomlNamedSchemaLayerBuilder)(NextLayer = new TomlNamedSchemaLayerBuilder());
		}

		public ITomlSchemaNode Build(ITomlSchemaLayer parent)
		{
			if (Name == null)
			{
				SchemaCollectionNode schemaCollectionNode = new SchemaCollectionNode();
				schemaCollectionNode.Settings = new Dictionary<string, ITomlSetting>(Settings);
				schemaCollectionNode.ParentLayer = parent;
				schemaCollectionNode.NextLayer = NextLayer?.Build(schemaCollectionNode);
				schemaCollectionNode.PathSegment = new TomlPathSegment(TomlPathSegmentType.Collection, null, parent?.ParentNode?.PathSegment);
				return schemaCollectionNode;
			}
			NamedSchemaNode namedSchemaNode = new NamedSchemaNode(new TomlPathSegment(TomlPathSegmentType.Named, Name, parent?.ParentNode?.PathSegment));
			namedSchemaNode.Settings = new Dictionary<string, ITomlSetting>(Settings);
			namedSchemaNode.ParentLayer = parent;
			namedSchemaNode.NextLayer = NextLayer?.Build(namedSchemaNode);
			return namedSchemaNode;
		}
	}
	internal static class TomlSchemaFileLoader
	{
		private static readonly Regex SectionHeader = new Regex("^\\s*[[](.+)[]]", RegexOptions.Compiled);

		private static readonly Regex SectionSanitizer = new Regex("[^\\p{L}\\d.\\[\\]_]|\\s", RegexOptions.Compiled);

		private static readonly Regex SettingIdentifier = new Regex("^\\s*([\\p{L}.\\d]+)\\s*=(.*)", RegexOptions.Compiled);

		private static readonly Regex CommentIdentifier = new Regex("^\\s*(//|#|--)", RegexOptions.Compiled);

		public static TomlConfig LoadFile(string path, ITomlSchemaLayer schema)
		{
			return Load(File.ReadAllLines(path), Path.GetFileName(path), schema);
		}

		public static TomlConfig Load(IList<string> lines, string fileName, ITomlSchemaLayer schema)
		{
			TomlConfig tomlConfig = new TomlConfig();
			int i = 1;
			string value = null;
			TomlConfig tomlConfig2 = tomlConfig;
			ITomlSchemaNode tomlSchemaNode = null;
			TomlLine tomlLine = new TomlLine();
			for (; i <= lines.Count; i++)
			{
				string text = lines[i - 1];
				if (string.IsNullOrWhiteSpace(text) || CommentIdentifier.IsMatch(text))
				{
					continue;
				}
				tomlLine.FileName = fileName;
				tomlLine.LineNr = i;
				tomlLine.Value = text;
				Match match = SectionHeader.Match(text);
				if (match.Success)
				{
					string value2 = match.Groups[1].Value;
					string text2 = SectionSanitizer.Replace(value2, "");
					if (string.IsNullOrWhiteSpace(text2))
					{
						Log.Warning?.Log($"{fileName}, Line {i}: Section name '{value2}' empty after sanitizing. Unable to load section.");
						value = null;
						continue;
					}
					value = text2;
					List<TomlPathSegment> list = ConvertToPath(text2, schema, tomlLine);
					tomlConfig2 = GetOrCreateSection(tomlConfig, list);
					tomlSchemaNode = FindNode(schema, list);
					if (tomlConfig2 == null)
					{
						Log.Warning?.Log($"{fileName}, Line {i}: Unable to find valid config for section '{text2}'.");
					}
					continue;
				}
				Match match2 = SettingIdentifier.Match(text);
				if (match2.Success)
				{
					string value3 = match2.Groups[1].Value;
					string value4 = match2.Groups[2].Value;
					ITomlSetting value5;
					ITomlSetting value6;
					if (string.IsNullOrWhiteSpace(value))
					{
						Log.Warning?.Log($"{fileName}, Line {i}: Setting '{value3}' was not inside any section. Ignoring setting.");
					}
					else if (tomlConfig2 == null)
					{
						Log.Warning?.Log($"{fileName}, Line {i}: Skipping setting '{value3}' due to not finding valid config for section.");
					}
					else if (tomlConfig2.Settings.TryGetValue(ThatCore.Extensions.StringExtensions.Normalize(value3), out value5))
					{
						ParseSetting(value5, value4, tomlLine);
					}
					else if (tomlSchemaNode != null && tomlSchemaNode.Settings.TryGetValue(ThatCore.Extensions.StringExtensions.Normalize(value3), out value6))
					{
						ITomlSetting setting = value6.Clone(includeValue: false);
						ParseSetting(setting, value4, tomlLine);
						tomlConfig2.SetSetting(value3, setting);
					}
					else
					{
						Log.Warning?.Log($"{fileName}, Line {i}: Setting '{value3}' did not match any known setting for section '{tomlConfig2.PathSegment.GetPath()}'. Ignoring setting.");
					}
				}
				else
				{
					Log.Warning?.Log($"{fileName}, Line {i}: Unknown text '{text}' did not match any known format. Ignoring setting.");
				}
			}
			return tomlConfig;
			static void ParseSetting(ITomlSetting entry, string value7, TomlLine fileLine)
			{
				try
				{
					entry.Read(new TomlLine
					{
						FileName = fileLine.FileName,
						LineNr = fileLine.LineNr,
						Value = value7
					});
				}
				catch (Exception e)
				{
					Log.Warning?.Log(fileLine.FileName + ", Line " + fileLine.Value + ": Unexpected error during parsing.", e);
				}
			}
		}

		private static List<TomlPathSegment> ConvertToPath(string header, ITomlSchemaLayer schema, TomlLine fileLine)
		{
			List<string> list = header.SplitBy(Separator.Dot);
			List<TomlPathSegment> list2 = new List<TomlPathSegment>(list.Count + 1);
			ITomlSchemaLayer tomlSchemaLayer = schema;
			TomlPathSegment tomlPathSegment = TomlPathSegment.Default;
			list2.Add(tomlPathSegment);
			foreach (string item in list)
			{
				if (!(tomlSchemaLayer is ITomlSchemaNamedLayer tomlSchemaNamedLayer))
				{
					if (tomlSchemaLayer is ITomlSchemaCollectionLayer tomlSchemaCollectionLayer)
					{
						tomlPathSegment = new TomlPathSegment(TomlPathSegmentType.Collection, item, tomlPathSegment);
						list2.Add(tomlPathSegment);
						tomlSchemaLayer = tomlSchemaCollectionLayer.Node.NextLayer;
					}
					continue;
				}
				if (tomlSchemaNamedLayer.Nodes.TryGetValue(ThatCore.Extensions.StringExtensions.Normalize(item), out var value))
				{
					tomlPathSegment = new TomlPathSegment(TomlPathSegmentType.Named, item, tomlPathSegment);
					list2.Add(tomlPathSegment);
					tomlSchemaLayer = value.NextLayer;
					continue;
				}
				Log.Warning?.Log($"{fileLine.FileName}, Line {fileLine.LineNr}: Unable to find valid section with name '{header}'.");
				return null;
			}
			return list2;
		}

		private static TomlConfig GetOrCreateSection(TomlConfig configFile, List<TomlPathSegment> path)
		{
			TomlConfig tomlConfig = configFile;
			foreach (TomlPathSegment item in path.Skip(1))
			{
				tomlConfig = tomlConfig.CreateSubsection(item);
			}
			return tomlConfig;
		}

		private static ITomlSchemaNode FindNode(ITomlSchemaLayer schema, List<TomlPathSegment> sectionParts)
		{
			ITomlSchemaLayer tomlSchemaLayer = schema;
			ITomlSchemaNode result = null;
			foreach (TomlPathSegment item in sectionParts.Skip(1))
			{
				if (!(tomlSchemaLayer is ITomlSchemaCollectionLayer tomlSchemaCollectionLayer))
				{
					if (!(tomlSchemaLayer is ITomlSchemaNamedLayer tomlSchemaNamedLayer))
					{
						return null;
					}
					if (tomlSchemaNamedLayer.Nodes.TryGetValue(item.NormalizedName, out var value))
					{
						tomlSchemaLayer = value.NextLayer;
						result = value;
					}
				}
				else
				{
					tomlSchemaLayer = tomlSchemaCollectionLayer.Node.NextLayer;
					result = tomlSchemaCollectionLayer.Node;
				}
			}
			return result;
		}
	}
	internal static class TomlSchemaWriter
	{
		public static string WriteToString(ITomlSchemaLayer schema, TomlWriterSettings settings)
		{
			StringBuilder stringBuilder = new StringBuilder();
			if (!string.IsNullOrWhiteSpace(settings.Header))
			{
				foreach (string item in settings.Header.SplitBy(Separator.Newline))
				{
					stringBuilder.AppendLine(item);
				}
				stringBuilder.AppendLine();
			}
			WriteSectionsRecursively(stringBuilder, schema, settings, string.Empty);
			return stringBuilder.ToString();
		}

		private static void WriteSectionsRecursively(StringBuilder builder, ITomlSchemaLayer schema, TomlWriterSettings settings, string currentPath)
		{
			if (!(schema is ITomlSchemaNamedLayer tomlSchemaNamedLayer))
			{
				if (schema is ITomlSchemaCollectionLayer tomlSchemaCollectionLayer)
				{
					string text = "ID";
					ITomlSchemaNode node = tomlSchemaCollectionLayer.Node;
					string text2 = (string.IsNullOrWhiteSpace(currentPath) ? text : string.Join(".", currentPath, text));
					WriteSectionHeader(text2);
					WriteOptions(builder, node, settings);
					builder.AppendLine();
					if (node.NextLayer != null)
					{
						WriteSectionsRecursively(builder, node.NextLayer, settings, text2);
					}
					return;
				}
				throw new NotSupportedException(schema.GetType().FullName);
			}
			foreach (KeyValuePair<string, ITomlSchemaNode> node2 in tomlSchemaNamedLayer.Nodes)
			{
				string key = node2.Key;
				ITomlSchemaNode value = node2.Value;
				string text3 = (string.IsNullOrWhiteSpace(currentPath) ? key : string.Join(".", currentPath, key));
				WriteSectionHeader(text3);
				WriteOptions(builder, value, settings);
				builder.AppendLine();
				if (value.NextLayer != null)
				{
					WriteSectionsRecursively(builder, value.NextLayer, settings, text3);
				}
			}
			void WriteSectionHeader(string sectionName)
			{
				builder.AppendLine("[" + sectionName + "]");
			}
		}

		private static void WriteOptions(StringBuilder builder, ITomlSchemaNode node, TomlWriterSettings settings)
		{
			foreach (KeyValuePair<string, ITomlSetting> setting in node.Settings)
			{
				string key = setting.Key;
				ITomlSetting value = setting.Value;
				if (settings != null && settings.AddComments)
				{
					foreach (string item in value.Description.SplitBy(Separator.Newline))
					{
						builder.AppendLine("# " + item);
					}
				}
				try
				{
					string text = value.Write();
					builder.AppendLine(key + " = " + text);
				}
				catch (Exception e)
				{
					Log.Warning?.Log("Error while attempting to write setting '" + key + "' to file '" + settings.FileName + "'. Skipping setting.", e);
				}
			}
		}
	}
}
namespace ThatCore.Config.Toml.Parsers
{
	internal class BoolParser : ValueParser<bool>
	{
		protected override void ParseInternal(ITomlSetting<bool> entry, TomlLine line)
		{
			if (bool.TryParse(line.Value, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}' as a boolean. Verify spelling. Valid values are \"true\" and \"false\".");
				entry.IsSet = false;
			}
		}
	}
	internal class NullableBoolParser : ValueParser<bool?>
	{
		protected override void ParseInternal(ITomlSetting<bool?> entry, TomlLine line)
		{
			if (bool.TryParse(line.Value, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}' as a boolean. Verify spelling. Valid values are \"true\" and \"false\".");
				entry.IsSet = false;
			}
		}
	}
	internal class DoubleParser : ValueParser<double>
	{
		protected override void ParseInternal(ITomlSetting<double> entry, TomlLine line)
		{
			if (double.TryParse(line.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}' as a double. Verify spelling. Valid example values are \"0.5\" and \"123\".");
				entry.IsSet = false;
			}
		}
	}
	internal class NullableDoubleParser : ValueParser<double?>
	{
		protected override void ParseInternal(ITomlSetting<double?> entry, TomlLine line)
		{
			if (double.TryParse(line.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}' as a double. Verify spelling. Valid example values are \"0.5\" and \"123\".");
				entry.IsSet = false;
			}
		}
	}
	internal class EnumListParser<T> : ValueParser<List<T>> where T : struct, Enum
	{
		protected override void ParseInternal(ITomlSetting<List<T>> entry, TomlLine line)
		{
			List<string> list = ThatCore.Extensions.StringExtensions.SplitByComma(line.Value);
			List<T> list2 = new List<T>(list.Count);
			foreach (string item in list)
			{
				if (Enum.TryParse<T>(item, ignoreCase: true, out var result))
				{
					list2.Add(result);
				}
				else
				{
					Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{item}'. Verify spelling.");
				}
			}
			if (list.Count > 0 && list2.Count == 0)
			{
				entry.IsSet = false;
				return;
			}
			entry.Value = list2;
			entry.IsSet = true;
		}
	}
	internal class EnumParser<T> : ValueParser<T> where T : struct, Enum
	{
		protected override void ParseInternal(ITomlSetting<T> entry, TomlLine line)
		{
			if (Enum.TryParse<T>(line.Value, ignoreCase: true, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}'. Verify spelling.");
				entry.IsSet = false;
			}
		}
	}
	internal class NullableEnumParser<T> : ValueParser<T?> where T : struct, Enum
	{
		protected override void ParseInternal(ITomlSetting<T?> entry, TomlLine line)
		{
			if (Enum.TryParse<T>(line.Value, ignoreCase: true, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}'. Verify spelling.");
				entry.IsSet = false;
			}
		}
	}
	internal class FloatParser : ValueParser<float>
	{
		protected override void ParseInternal(ITomlSetting<float> entry, TomlLine line)
		{
			if (float.TryParse(line.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}'. Expected decimal number. Eg., 0.5 or 3.");
				entry.IsSet = false;
			}
		}
	}
	internal class NullableFloatParser : ValueParser<float?>
	{
		protected override void ParseInternal(ITomlSetting<float?> entry, TomlLine line)
		{
			if (float.TryParse(line.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}'. Expected decimal number. Eg., 0.5 or 3.");
				entry.IsSet = false;
			}
		}
	}
	internal class IntListParser : ValueParser<List<int>>
	{
		protected override void ParseInternal(ITomlSetting<List<int>> entry, TomlLine line)
		{
			List<string> list = ThatCore.Extensions.StringExtensions.SplitByComma(line.Value);
			List<int> list2 = (from x in list.Select(delegate(string x)
				{
					if (int.TryParse(x, out var result))
					{
						return result;
					}
					Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{x}'. Expected integer number. Eg., 0, 1 or 3.");
					return (int?)null;
				})
				where x.HasValue
				select x.Value).ToList();
			if (list.Count > 0 && list2.Count == 0)
			{
				entry.IsSet = false;
				return;
			}
			entry.Value = list2;
			entry.IsSet = true;
		}
	}
	internal class IntParser : ValueParser<int>
	{
		protected override void ParseInternal(ITomlSetting<int> entry, TomlLine line)
		{
			if (int.TryParse(line.Value, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}'. Expected integer number. Eg., 0, 1 or 3.");
				entry.IsSet = false;
			}
		}
	}
	internal class NullableIntParser : ValueParser<int?>
	{
		protected override void ParseInternal(ITomlSetting<int?> entry, TomlLine line)
		{
			if (int.TryParse(line.Value, out var result))
			{
				entry.Value = result;
				entry.IsSet = true;
			}
			else
			{
				Log.Warning?.Log($"{line.FileName}, Line {line.LineNr}: Unable to parse '{line.Value}'. Expected integer number. Eg., 0, 1 or 3.");
				entry.IsSet = false;
			}
		}
	}
	internal interface IValueParser
	{
		void Parse(ITomlSetting entry, TomlLine line);
	}
	internal abstract class ValueParser<T> : IValueParser
	{
		protected Type ParserType { get; }

		private Type ConfigEntryParserType { get; }

		protected ValueParser()
		{
			ParserType = typeof(T);
			ConfigEntryParserType = typeof(ITomlSetting<>).GetGenericTypeDefinition().MakeGenericType(ParserType);
		}

		public void Parse(ITomlSetting entry, TomlLine line)
		{
			if (entry is ITomlSetting<T> tomlSetting)
			{
				if (string.IsNullOrWhiteSpace(line.Value))
				{
					tomlSetting.IsSet = true;
				}
				else
				{
					ParseInternal(tomlSetting, line);
				}
				return;
			}
			throw new InvalidOperationException("Unable to parse config entry with type " + typeof(T).Name + " using parser " + GetType().Name + ".");
		}

		protected abstract void ParseInternal(ITomlSetting<T> entry, TomlLine line);
	}
	internal class StringListParser : ValueParser<List<string>>
	{
		protected override void ParseInternal(ITomlSetting<List<string>> entry, TomlLine line)
		{
			entry.Value = ThatCore.Extensions.StringExtensions.SplitByComma(line.Value);
			entry.IsSet = true;
		}
	}
	internal class StringParser : ValueParser<string>
	{
		protected override void ParseInternal(ITomlSetting<string> entry, TomlLine line)
		{
			entry.Value = line.Value.Trim();
			entry.IsSet = true;
		}
	}
	internal static class TomlParserFactory
	{
		private static Dictionary<Type, IValueParser> parsers;

		private static Type ListType;

		private static Type EnumListParserType;

		private static Type EnumParserType;

		private static Type NullableEnumParserType;

		static TomlParserFactory()
		{
			parsers = new Dictionary<Type, IValueParser>();
			ListType = typeof(List<>);
			EnumListParserType = typeof(EnumListParser<>);
			EnumParserType = typeof(EnumParser<>);
			NullableEnumParserType = typeof(NullableEnumParser<>);
			parsers = new Dictionary<Type, IValueParser>
			{
				{
					typeof(bool),
					new BoolParser()
				},
				{
					typeof(double),
					new DoubleParser()
				},
				{
					typeof(float),
					new FloatParser()
				},
				{
					typeof(int),
					new IntParser()
				},
				{
					typeof(bool?),
					new NullableBoolParser()
				},
				{
					typeof(double?),
					new NullableDoubleParser()
				},
				{
					typeof(float?),
					new NullableFloatParser()
				},
				{
					typeof(int?),
					new NullableIntParser()
				},
				{
					typeof(string),
					new StringParser()
				},
				{
					typeof(List<string>),
					new StringListParser()
				},
				{
					typeof(List<int>),
					new IntListParser()
				}
			};
		}

		public static void Read<T>(this T entry, TomlLine line) where T : ITomlSetting
		{
			if (parsers.TryGetValue(entry.SettingType, out var value))
			{
				value.Parse(entry, line);
				return;
			}
			if (entry.SettingType.IsGenericType && entry.SettingType.GetGenericTypeDefinition() == ListType)
			{
				if (entry.SettingType.GenericTypeArguments.First().IsEnum)
				{
					Type type = entry.SettingType.GenericTypeArguments.First();
					value = (IValueParser)Activator.CreateInstance(EnumListParserType.MakeGenericType(type));
					parsers[entry.SettingType] = value;
					value.Parse(entry, line);
					return;
				}
				throw new NotSupportedException("Unable to parse config entries of type '" + entry.SettingType.Name + "'.");
			}
			if (entry.SettingType.IsEnum)
			{
				value = (IValueParser)Activator.CreateInstance(EnumParserType.MakeGenericType(entry.SettingType));
				parsers[entry.SettingType] = value;
				value.Parse(entry, line);
				return;
			}
			Type? underlyingType = Nullable.GetUnderlyingType(entry.SettingType);
			if ((object)underlyingType != null && underlyingType.IsEnum)
			{
				Type underlyingType2 = Nullable.GetUnderlyingType(entry.SettingType);
				value = (IValueParser)Activator.CreateInstance(NullableEnumParserType.MakeGenericType(underlyingType2));
				parsers[entry.SettingType] = value;
				value.Parse(entry, line);
				return;
			}
			throw new NotSupportedException();
		}
	}
}
namespace ThatCore.Config.Toml.Mapping
{
	internal class ConfigToObjectMapper<T>
	{
		public List<TomlPathSegment> Path { get; set; }

		public MappingInstantiation<T> Instantiation { get; set; }

		public List<IMappingInstantiationForParent<T>> SubInstantiations { get; set; }

		public T Execute(TomlConfig config)
		{
			TomlConfig tomlConfig = config;
			foreach (TomlPathSegment item in Path)
			{
				if (item.SegmentType == TomlPathSegmentType.Collection)
				{
					throw new NotSupportedException("Cannot handle enumerated segments in initial path.");
				}
				tomlConfig = tomlConfig.Sections[item];
			}
			T val = Instantiation.Execute(tomlConfig);
			foreach (IMappingInstantiationForParent<T> subInstantiation in SubInstantiations)
			{
				subInstantiation.Execute(val, tomlConfig);
			}
			return val;
		}
	}
	internal class MappingInstantiation<T>
	{
		public Func<TomlConfig, T> Instantiation;

		public List<Action<TomlConfig, T>> InstanceActions { get; set; } = new List<Action<TomlConfig, T>>();

		public T Execute(TomlConfig config)
		{
			T val = Instantiation(config);
			foreach (Action<TomlConfig, T> instanceAction in InstanceActions)
			{
				instanceAction(config, val);
			}
			return val;
		}
	}
	internal class MappingInstantiation<TParent, T>
	{
		public Func<TParent, TomlConfig, T> Instantiation;

		public List<Action<TomlConfig, T>> InstanceActions { get; set; } = new List<Action<TomlConfig, T>>();

		public T Execute(TParent parent, TomlConfig config)
		{
			T val = Instantiation(parent, config);
			foreach (Action<TomlConfig, T> instanceAction in InstanceActions)
			{
				instanceAction(config, val);
			}
			return val;
		}
	}
	internal interface IMappingInstantiationForParent<TParent>
	{
		List<TomlPathSegment> SubPath { get; set; }

		void Execute(TParent parent, TomlConfig config);
	}
	internal class MappingInstantiationForParent<TParent, T> : IMappingInstantiationForParent<TParent>
	{
		public MappingInstantiation<TParent, T> Instantiation;

		public Action<TParent, T, TomlConfig> Action;

		public List<TomlPathSegment> SubPath { get; set; } = new List<TomlPathSegment>();

		public List<IMappingInstantiationForParent<T>> SubInstantiations { get; set; } = new List<IMappingInstantiationForParent<T>>();

		public void Execute(TParent parent, TomlConfig config)
		{
			RecursiveScan(SubPath, 0, config);
			void RecursiveScan(List<TomlPathSegment> path, int depth, TomlConfig section)
			{
				if (depth >= path.Count)
				{
					T val = Instantiation.Execute(parent, section);
					if (Action != null)
					{
						Action(parent, val, section);
					}
					{
						foreach (IMappingInstantiationForParent<T> subInstantiation in SubInstantiations)
						{
							subInstantiation.Execute(val, section);
						}
						return;
					}
				}
				TomlPathSegment tomlPathSegment = path[depth];
				if (tomlPathSegment.SegmentType == TomlPathSegmentType.Collection)
				{
					foreach (KeyValuePair<TomlPathSegment, TomlConfig> section in section.Sections)
					{
						RecursiveScan(path, ++depth, section.Value);
					}
					return;
				}
				if (section.Sections.TryGetValue(tomlPathSegment, out var value))
				{
					RecursiveScan(path, ++depth, value);
				}
			}
		}
	}
	internal class FileMappingBuilder<TTarget> : IFileMappingBuilder<TTarget>, IFileMappingBuilder, IHaveMapping<TTarget>, IMappingExecuter<TTarget>
	{
		public Action<TomlConfig, TTarget> Mapping { get; set; }

		private ITomlSchemaNodeBuilder NodeBuilder { get; }

		private List<IHaveMapping<TTarget>> SubBuilders { get; } = new List<IHaveMapping<TTarget>>();

		public FileMappingBuilder(ITomlSchemaNodeBuilder nodeBuilder)
		{
			NodeBuilder = nodeBuilder;
		}

		public IFileMappingBuilder<TTarget> Map<TOption>(string confi