Decompiled source of LethalModDataLib v1.2.2

BepInEx/plugins/LethalModDataLib/MaxWasUnavailable.LethalModDataLib.dll

Decompiled 8 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using LethalModDataLib.Attributes;
using LethalModDataLib.Enums;
using LethalModDataLib.Events;
using LethalModDataLib.Features;
using LethalModDataLib.Helpers;
using LethalModDataLib.Interfaces;
using LethalModDataLib.Models;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("ClientNetworkTransform")]
[assembly: IgnoresAccessChecksTo("DissonanceVoip")]
[assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")]
[assembly: IgnoresAccessChecksTo("Unity.Burst")]
[assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")]
[assembly: IgnoresAccessChecksTo("Unity.Collections")]
[assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Jobs")]
[assembly: IgnoresAccessChecksTo("Unity.Mathematics")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")]
[assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")]
[assembly: IgnoresAccessChecksTo("Unity.Services.QoS")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Relay")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: AssemblyCompany("MaxWasUnavailable")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A library for Lethal Company, providing a standardised way to save and load modded data.")]
[assembly: AssemblyFileVersion("1.2.2.0")]
[assembly: AssemblyInformationalVersion("1.2.2+3f6ed0364c80cbd6b139486b6efd40e4051ff736")]
[assembly: AssemblyProduct("MaxWasUnavailable.LethalModDataLib")]
[assembly: AssemblyTitle("MaxWasUnavailable.LethalModDataLib")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/MaxWasUnavailable/LobbyCompatibility")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LethalModDataLib
{
	[BepInPlugin("MaxWasUnavailable.LethalModDataLib", "MaxWasUnavailable.LethalModDataLib", "1.2.2")]
	public class LethalModDataLib : BaseUnityPlugin
	{
		private const string ModVersionKey = "LMDLVersion";

		private bool _isPatched;

		private Harmony? Harmony { get; set; }

		internal static ManualLogSource? Logger { get; private set; }

		public static LethalModDataLib? Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			PatchAll();
			MiscEvents.PostInitializeGameEvent += OnGameInitialized;
			Logger.LogInfo((object)"Plugin MaxWasUnavailable.LethalModDataLib is loaded!");
		}

		private static void VersionCheck()
		{
			string text = SaveLoadHandler.LoadData<string>("LMDLVersion", SaveLocation.GeneralSave);
			if (string.IsNullOrEmpty(text))
			{
				ManualLogSource? logger = Logger;
				if (logger != null)
				{
					logger.LogInfo((object)"No saved LethalModDataLib version found. This is normal if this is the first time you are running the game with LethalModDataLib installed.");
				}
			}
			else if (text == "1.2.2")
			{
				ManualLogSource? logger2 = Logger;
				if (logger2 != null)
				{
					logger2.LogDebug((object)("LethalModDataLib version (1.2.2) matches last saved version (" + text + ")."));
				}
			}
			else
			{
				SaveLoadHandler.SaveData(text, "LMDLVersion_old", SaveLocation.GeneralSave);
				ManualLogSource? logger3 = Logger;
				if (logger3 != null)
				{
					logger3.LogWarning((object)("Mismatch between last saved LethalModDataLib version (" + text + ") and current version (1.2.2). This is normal if you have updated LethalModDataLib. Make sure to check the changelog for breaking changes. "));
				}
			}
			SaveLoadHandler.SaveData("1.2.2", "LMDLVersion", SaveLocation.GeneralSave);
		}

		private static void OnGameInitialized()
		{
			ModDataHandler.Initialise();
			MiscEvents.PostInitializeGameEvent -= OnGameInitialized;
			VersionCheck();
		}

		private void PatchAll()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_004c: Expected O, but got Unknown
			if (_isPatched)
			{
				ManualLogSource? logger = Logger;
				if (logger != null)
				{
					logger.LogWarning((object)"Already patched!");
				}
				return;
			}
			ManualLogSource? logger2 = Logger;
			if (logger2 != null)
			{
				logger2.LogDebug((object)"Patching...");
			}
			if (Harmony == null)
			{
				Harmony val = new Harmony("MaxWasUnavailable.LethalModDataLib");
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
			_isPatched = true;
			ManualLogSource? logger3 = Logger;
			if (logger3 != null)
			{
				logger3.LogDebug((object)"Patched!");
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "MaxWasUnavailable.LethalModDataLib";

		public const string PLUGIN_NAME = "MaxWasUnavailable.LethalModDataLib";

		public const string PLUGIN_VERSION = "1.2.2";
	}
}
namespace LethalModDataLib.Patches
{
	[HarmonyPatch(typeof(ES3))]
	[HarmonyPriority(800)]
	[HarmonyWrapSafe]
	internal static class ES3Patches
	{
		[HarmonyPostfix]
		[HarmonyPatch("DeleteFile", new Type[] { typeof(string) })]
		private static void DeleteFilePostfix(string filePath)
		{
			if (!filePath.Contains(".moddata"))
			{
				SaveLoadEvents.OnPostDeleteSave(filePath);
			}
		}
	}
	[HarmonyPatch(typeof(GameNetworkManager))]
	[HarmonyPriority(800)]
	[HarmonyWrapSafe]
	internal static class GameNetworkManagerPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("SaveGame")]
		private static void SaveGamePostfix(GameNetworkManager __instance)
		{
			SaveLoadEvents.OnPostSaveGame(StartOfRound.Instance.isChallengeFile, __instance.currentSaveFileName);
		}

		[HarmonyPostfix]
		[HarmonyPatch("ResetSavedGameValues")]
		private static void PostResetSavedGameValues(GameNetworkManager __instance)
		{
			SaveLoadEvents.OnPostResetSavedGameValues();
		}
	}
	[HarmonyPatch(typeof(InitializeGame))]
	[HarmonyPriority(800)]
	[HarmonyWrapSafe]
	internal static class InitializeGamePatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void StartPostfix()
		{
			MiscEvents.OnPostInitializeGame();
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	[HarmonyPriority(800)]
	[HarmonyWrapSafe]
	internal static class StartOfRoundPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("AutoSaveShipData")]
		private static void PostAutoSaveShipData(StartOfRound __instance)
		{
			SaveLoadEvents.OnPostAutoSave(__instance.isChallengeFile, GameNetworkManager.Instance.currentSaveFileName);
		}

		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void PostStart(StartOfRound __instance)
		{
			SaveLoadEvents.OnPostLoadGame(__instance.isChallengeFile, GameNetworkManager.Instance.currentSaveFileName);
		}
	}
}
namespace LethalModDataLib.Models
{
	public record FieldKey(FieldInfo FieldInfo, object? Instance = null) : IModDataKey
	{
		public string Name => FieldInfo.Name;

		public string? AssemblyQualifiedName => FieldInfo.FieldType.AssemblyQualifiedName;

		public Assembly Assembly => FieldInfo.FieldType.Assembly;

		public bool TryGetValue(out object? value)
		{
			value = FieldInfo.GetValue(Instance);
			return true;
		}

		public bool TrySetValue(object? value)
		{
			FieldInfo.SetValue(Instance, value);
			return true;
		}

		public ModDataAttribute GetModDataAttribute()
		{
			return FieldInfo.GetCustomAttribute<ModDataAttribute>();
		}
	}
	public record ModDataValue(ModDataAttribute ModDataAttribute, string? KeySuffix = null, object? OriginalValue = null)
	{
		public string? BaseKey
		{
			get
			{
				return ModDataAttribute.BaseKey;
			}
			set
			{
				ModDataAttribute.BaseKey = value;
			}
		}

		public SaveWhen SaveWhen => ModDataAttribute.SaveWhen;

		public LoadWhen LoadWhen => ModDataAttribute.LoadWhen;

		public ResetWhen ResetWhen => ModDataAttribute.ResetWhen;

		public SaveLocation SaveLocation => ModDataAttribute.SaveLocation;
	}
	public record PropertyKey(PropertyInfo PropertyInfo, object? Instance = null) : IModDataKey
	{
		public string Name => PropertyInfo.Name;

		public string? AssemblyQualifiedName => PropertyInfo.PropertyType.AssemblyQualifiedName;

		public Assembly Assembly => PropertyInfo.PropertyType.Assembly;

		public bool TryGetValue(out object? value)
		{
			if (PropertyInfo.GetGetMethod(nonPublic: true) == null)
			{
				value = null;
				return false;
			}
			value = PropertyInfo.GetValue(Instance);
			return true;
		}

		public bool TrySetValue(object? value)
		{
			if (PropertyInfo.GetSetMethod(nonPublic: true) == null)
			{
				return false;
			}
			PropertyInfo.SetValue(Instance, value);
			return true;
		}

		public ModDataAttribute GetModDataAttribute()
		{
			return PropertyInfo.GetCustomAttribute<ModDataAttribute>();
		}
	}
}
namespace LethalModDataLib.Interfaces
{
	public interface IModDataKey
	{
		string Name { get; }

		string? AssemblyQualifiedName { get; }

		Assembly Assembly { get; }

		object? Instance { get; }

		bool TryGetValue(out object? value);

		bool TrySetValue(object? value);

		ModDataAttribute GetModDataAttribute();
	}
}
namespace LethalModDataLib.Helpers
{
	public static class AssemblyExtensions
	{
		public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
		{
			if (assembly == null)
			{
				throw new ArgumentNullException("assembly");
			}
			try
			{
				return assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				return ex.Types.Where((Type t) => t != null);
			}
		}
	}
	public static class ModDataHelper
	{
		private static readonly Dictionary<Assembly, string> PluginGuids = new Dictionary<Assembly, string>();

		public static IModDataKey? GetModDataKey(object instanceOrType, string fieldPropertyName)
		{
			object instance = null;
			Type type2;
			if (instanceOrType is Type type)
			{
				type2 = type;
			}
			else
			{
				instance = instanceOrType;
				type2 = instanceOrType.GetType();
			}
			FieldInfo field = type2.GetField(fieldPropertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				if (field.IsStatic)
				{
					instance = null;
				}
				return new FieldKey(field, instance);
			}
			PropertyInfo property = type2.GetProperty(fieldPropertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null)
			{
				throw new ArgumentException("Field or property " + fieldPropertyName + " does not exist in " + type2.AssemblyQualifiedName + "!");
			}
			if (property.GetGetMethod(nonPublic: true).IsStatic)
			{
				instance = null;
			}
			return new PropertyKey(property, instance);
		}

		public static void TriggerSaveEvent(SaveWhen saveWhen)
		{
			Assembly callingAssembly = Assembly.GetCallingAssembly();
			foreach (IModDataKey item in ModDataHandler.ModDataValues.Keys.Where((IModDataKey modDataKey) => modDataKey.Assembly == callingAssembly))
			{
				ModDataValue modDataValue = ModDataHandler.ModDataValues[item];
				if ((modDataValue.SaveWhen & saveWhen) != 0)
				{
					item.HandleSaveModData();
				}
				if (modDataValue.SaveWhen.HasFlag(SaveWhen.Manual) && saveWhen.HasFlag(SaveWhen.Manual))
				{
					item.HandleSaveModData();
				}
			}
		}

		public static void TriggerLoadEvent(LoadWhen loadWhen)
		{
			Assembly callingAssembly = Assembly.GetCallingAssembly();
			foreach (IModDataKey item in ModDataHandler.ModDataValues.Keys.Where((IModDataKey modDataKey) => modDataKey.Assembly == callingAssembly))
			{
				ModDataValue modDataValue = ModDataHandler.ModDataValues[item];
				if ((modDataValue.LoadWhen & loadWhen) != 0)
				{
					item.HandleLoadModData();
				}
				if (modDataValue.LoadWhen.HasFlag(LoadWhen.Manual) && loadWhen.HasFlag(LoadWhen.Manual))
				{
					item.HandleLoadModData();
				}
			}
		}

		internal static string GetCurrentSaveFileName()
		{
			return GameNetworkManager.Instance.currentSaveFileName;
		}

		internal static string GetGeneralSaveFileName()
		{
			return "LCGeneralSaveData";
		}

		internal static bool IsHost()
		{
			return GameNetworkManager.Instance.isHostingGame;
		}

		internal static bool IsKBackingField(FieldInfo fieldInfo)
		{
			return fieldInfo.Name.Contains("k__BackingField");
		}

		internal static string GenerateBaseKey(Type type, string guid)
		{
			return guid + "." + type.FullName;
		}

		internal static string GetCallingPluginGuid(Assembly assembly)
		{
			Assembly assembly2 = assembly;
			if (PluginGuids.TryGetValue(assembly2, out string value))
			{
				return value;
			}
			PluginInfo val = ((IEnumerable<PluginInfo>)Chainloader.PluginInfos.Values).FirstOrDefault((Func<PluginInfo, bool>)((PluginInfo pluginInfo) => ((object)pluginInfo.Instance)?.GetType().Assembly == assembly2));
			if (val == null)
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to get plugin info for assembly " + assembly2.FullName + "!"));
				}
			}
			Dictionary<Assembly, string> pluginGuids = PluginGuids;
			Assembly key = assembly2;
			object obj;
			if (val == null)
			{
				obj = null;
			}
			else
			{
				BepInPlugin metadata = val.Metadata;
				obj = ((metadata != null) ? metadata.GUID : null);
			}
			if (obj == null)
			{
				obj = "Unknown";
			}
			pluginGuids.Add(key, (string)obj);
			return PluginGuids[assembly2];
		}
	}
}
namespace LethalModDataLib.Features
{
	public static class ModDataAttributeCollector
	{
		internal static void RegisterModDataAttributes()
		{
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				foreach (Type loadableType in ((object)value.Instance).GetType().Assembly.GetLoadableTypes())
				{
					RegisterModDataAttributes(value.Metadata.GUID, loadableType);
				}
			}
		}

		internal static void DeRegisterModDataAttributes()
		{
			foreach (Type item in Chainloader.PluginInfos.Values.SelectMany((PluginInfo pluginInfo) => ((object)pluginInfo.Instance).GetType().Assembly.GetTypes()))
			{
				DeRegisterModDataAttributes(item);
			}
		}

		internal static void RegisterModDataAttributes(string guid, Type type, object? instance = null, string? keySuffix = null)
		{
			try
			{
				AddModDataFields(guid, type, instance, keySuffix);
				AddModDataProperties(guid, type, instance, keySuffix);
			}
			catch (Exception ex)
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogError((object)("Failed to register ModData attributes in " + type.FullName + " from " + guid + " plugin: " + ex.Message));
				}
			}
		}

		private static void DeRegisterModDataAttributes(Type type)
		{
			Type type2 = type;
			foreach (IModDataKey item in ModDataHandler.ModDataValues.Keys.Where((IModDataKey key) => key.Assembly == type2.Assembly).ToList())
			{
				ModDataHandler.ModDataValues.Remove(item);
			}
		}

		internal static void DeRegisterModDataAttributes(object instance)
		{
			object instance2 = instance;
			foreach (IModDataKey item in ModDataHandler.ModDataValues.Keys.Where((IModDataKey key) => key.Instance == instance2).ToList())
			{
				ModDataHandler.ModDataValues.Remove(item);
			}
		}

		private static BindingFlags GetBindingFlags(object? instance = null)
		{
			if (instance != null)
			{
				return BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			}
			return BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
		}

		private static void AddModDataFields(string guid, Type type, object? instance = null, string? keySuffix = null)
		{
			FieldInfo[] fields = type.GetFields(GetBindingFlags(instance));
			foreach (FieldInfo fieldInfo in fields)
			{
				if (Attribute.IsDefined(fieldInfo, typeof(ModDataAttribute)))
				{
					FieldKey modDataKey = new FieldKey(fieldInfo, instance);
					ModDataHandler.AddModData(guid, type, modDataKey, keySuffix);
				}
			}
		}

		private static void AddModDataProperties(string guid, Type type, object? instance = null, string? keySuffix = null)
		{
			PropertyInfo[] properties = type.GetProperties(GetBindingFlags(instance));
			foreach (PropertyInfo propertyInfo in properties)
			{
				if (Attribute.IsDefined(propertyInfo, typeof(ModDataAttribute)))
				{
					PropertyKey modDataKey = new PropertyKey(propertyInfo, instance);
					ModDataHandler.AddModData(guid, type, modDataKey, keySuffix);
				}
			}
		}
	}
	public static class ModDataHandler
	{
		internal static Dictionary<IModDataKey, ModDataValue> ModDataValues { get; } = new Dictionary<IModDataKey, ModDataValue>();


		internal static string ToES3KeyString(this IModDataKey iModDataKey)
		{
			if (!ModDataValues.TryGetValue(iModDataKey, out ModDataValue value))
			{
				throw new ArgumentException("Field or property with name " + iModDataKey.Name + " from " + iModDataKey.AssemblyQualifiedName + " is not registered with the ModDataAttribute!");
			}
			if (value.BaseKey == null)
			{
				throw new ArgumentException("Field or property with name " + iModDataKey.Name + " from " + iModDataKey.AssemblyQualifiedName + " has no base key!");
			}
			string text = value.BaseKey + ".";
			if (!string.IsNullOrEmpty(value.KeySuffix))
			{
				return text + value.KeySuffix + "." + iModDataKey.Name;
			}
			return text + iModDataKey.Name;
		}

		public static void RegisterInstance(object instance, string? keySuffix = null)
		{
			ManualLogSource? logger = LethalModDataLib.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("Registering instance " + instance.GetType().FullName + "..."));
			}
			ModDataAttributeCollector.RegisterModDataAttributes(ModDataHelper.GetCallingPluginGuid(Assembly.GetCallingAssembly()), instance.GetType(), instance, keySuffix);
		}

		public static void DeRegisterInstance(object instance)
		{
			ManualLogSource? logger = LethalModDataLib.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("De-registering instance " + instance.GetType().FullName + "..."));
			}
			ModDataAttributeCollector.DeRegisterModDataAttributes(instance);
		}

		internal static void AddModData(string guid, Type type, IModDataKey modDataKey, string? keySuffix = null)
		{
			if (ModDataValues.ContainsKey(modDataKey))
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Field or property with name " + modDataKey.Name + " from " + type.AssemblyQualifiedName + " is already registered!"));
				}
				return;
			}
			modDataKey.TryGetValue(out object value);
			ModDataValues.Add(modDataKey, new ModDataValue(modDataKey.GetModDataAttribute(), keySuffix, value));
			ModDataValue modDataValue = ModDataValues[modDataKey];
			if (modDataValue.BaseKey == null)
			{
				string text2 = (modDataValue.BaseKey = ModDataHelper.GenerateBaseKey(type, guid));
			}
			ManualLogSource? logger2 = LethalModDataLib.Logger;
			if (logger2 != null)
			{
				logger2.LogDebug((object)("Added field or property with name " + modDataKey.Name + " from " + type.AssemblyQualifiedName + " to the mod data system!"));
			}
			if (ModDataValues[modDataKey].LoadWhen.HasFlag(LoadWhen.OnRegister))
			{
				modDataKey.HandleLoadModData();
			}
		}

		internal static void Initialise()
		{
			ManualLogSource? logger = LethalModDataLib.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)"Registering ModDataAttribute fields...");
			}
			ModDataAttributeCollector.RegisterModDataAttributes();
			ManualLogSource? logger2 = LethalModDataLib.Logger;
			if (logger2 != null)
			{
				logger2.LogInfo((object)"Hooking up save, load and delete events...");
			}
			SaveLoadEvents.PostSaveGameEvent += OnSave;
			SaveLoadEvents.PostAutoSaveEvent += OnAutoSave;
			SaveLoadEvents.PostLoadGameEvent += OnLoad;
			SaveLoadEvents.PostDeleteSaveEvent += OnDeleteSave;
			SaveLoadEvents.PostResetSavedGameValuesEvent += OnGameOver;
			ManualLogSource? logger3 = LethalModDataLib.Logger;
			if (logger3 != null)
			{
				logger3.LogInfo((object)"ModDataHandler initialised!");
			}
		}

		internal static void HandleSaveModData(this IModDataKey modDataKey)
		{
			if (!SaveLoadHandler.SaveData(modDataKey))
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to save field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "!"));
				}
			}
		}

		internal static void HandleLoadModData(this IModDataKey modDataKey)
		{
			if (!SaveLoadHandler.LoadData(modDataKey))
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to load field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "!"));
				}
			}
		}

		private static void DeleteModDataFile(string saveName)
		{
			saveName += ".moddata";
			try
			{
				ES3.DeleteFile(saveName);
			}
			catch (Exception arg)
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogError((object)$"Failed to delete file {saveName}! Exception: {arg}");
				}
			}
		}

		private static void ResetModData(this IModDataKey modDataKey)
		{
			if (!modDataKey.TrySetValue(ModDataValues[modDataKey].OriginalValue))
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to reset field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "!"));
				}
			}
		}

		private static void OnSave(bool isChallengeFile, string saveFileName)
		{
			foreach (IModDataKey item in ModDataValues.Keys.Where((IModDataKey modDataKey) => ModDataValues[modDataKey].SaveWhen.HasFlag(SaveWhen.OnSave)))
			{
				item.HandleSaveModData();
			}
		}

		private static void OnAutoSave(bool isChallengeFile, string saveFileName)
		{
			foreach (IModDataKey item in ModDataValues.Keys.Where((IModDataKey modDataKey) => ModDataValues[modDataKey].SaveWhen.HasFlag(SaveWhen.OnAutoSave)))
			{
				item.HandleSaveModData();
			}
		}

		private static void OnLoad(bool isChallengeFile, string saveFileName)
		{
			foreach (IModDataKey item in ModDataValues.Keys.Where((IModDataKey modDataKey) => ModDataValues[modDataKey].LoadWhen.HasFlag(LoadWhen.OnLoad)))
			{
				item.HandleLoadModData();
			}
		}

		private static void OnDeleteSave(string filePath)
		{
			DeleteModDataFile(filePath);
		}

		private static void OnGameOver()
		{
			foreach (IModDataKey item in ModDataValues.Keys.Where((IModDataKey modDataKey) => ModDataValues[modDataKey].ResetWhen.HasFlag(ResetWhen.OnGameOver)))
			{
				item.ResetModData();
			}
		}
	}
	public static class SaveLoadHandler
	{
		private static void VerifyKeyAndFileName(string key, string fileName)
		{
			if (string.IsNullOrEmpty(key))
			{
				throw new ArgumentException("Key cannot be null or empty!");
			}
			if (string.IsNullOrEmpty(fileName))
			{
				throw new ArgumentException("File name cannot be null or empty!");
			}
		}

		private static T? LoadData<T>(string key, string fileName, T? defaultValue = default(T?))
		{
			VerifyKeyAndFileName(key, fileName);
			fileName += ".moddata";
			try
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogDebug((object)("Loading data from file " + fileName + " with key " + key + "..."));
				}
				return (T?)(ES3.KeyExists(key, fileName) ? ((object)ES3.Load<T>(key, fileName)) : ((object)defaultValue));
			}
			catch (Exception arg)
			{
				ManualLogSource? logger2 = LethalModDataLib.Logger;
				if (logger2 != null)
				{
					logger2.LogError((object)$"Failed to load data from file {fileName} with key {key}! Exception: {arg}");
				}
				return defaultValue;
			}
		}

		public static T? LoadData<T>(string key, SaveLocation saveLocation = SaveLocation.CurrentSave, T? defaultValue = default(T?), bool autoAddGuid = true)
		{
			if (saveLocation == SaveLocation.CurrentSave && !ModDataHelper.IsHost())
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogDebug((object)("Not loading " + key + " from current save file as we're not the host!"));
				}
				return defaultValue;
			}
			if (autoAddGuid)
			{
				string callingPluginGuid = ModDataHelper.GetCallingPluginGuid(Assembly.GetCallingAssembly());
				if (!key.StartsWith(callingPluginGuid))
				{
					key = callingPluginGuid + "." + key;
				}
			}
			string key2 = key;
			return LoadData(key2, saveLocation switch
			{
				SaveLocation.CurrentSave => ModDataHelper.GetCurrentSaveFileName(), 
				SaveLocation.GeneralSave => ModDataHelper.GetGeneralSaveFileName(), 
				_ => throw new ArgumentOutOfRangeException("saveLocation", saveLocation, "Invalid load location!"), 
			}, defaultValue);
		}

		public static bool LoadData(IModDataKey modDataKey)
		{
			if (!ModDataHandler.ModDataValues.TryGetValue(modDataKey, out ModDataValue value))
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + " has no registered mod data attribute! If this is an instance of a class, make sure to register it using ModDataHandler.RegisterInstance!"));
				}
				return false;
			}
			string key = modDataKey.ToES3KeyString();
			SaveLocation saveLocation = value.SaveLocation;
			object value2 = LoadData(key, saveLocation, value.OriginalValue, autoAddGuid: false);
			if (modDataKey.TrySetValue(value2))
			{
				return true;
			}
			ManualLogSource? logger2 = LethalModDataLib.Logger;
			if (logger2 != null)
			{
				logger2.LogDebug((object)("Failed to set value for field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "! Does it not have a setter?"));
			}
			return false;
		}

		private static bool SaveData<T>(T? data, string key, string fileName)
		{
			VerifyKeyAndFileName(key, fileName);
			fileName += ".moddata";
			try
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogDebug((object)("Saving data to file " + fileName + " with key " + key + "..."));
				}
				ES3.Save<T>(key, data, fileName);
				return true;
			}
			catch (Exception arg)
			{
				ManualLogSource? logger2 = LethalModDataLib.Logger;
				if (logger2 != null)
				{
					logger2.LogError((object)$"Failed to save data to file {fileName} with key {key}! Exception: {arg}");
				}
				return false;
			}
		}

		public static bool SaveData<T>(T? data, string key, SaveLocation saveLocation = SaveLocation.CurrentSave, bool autoAddGuid = true)
		{
			if (saveLocation == SaveLocation.CurrentSave && !ModDataHelper.IsHost())
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogDebug((object)("Not saving " + key + " to current save file as we're not the host!"));
				}
				return false;
			}
			if (autoAddGuid)
			{
				string callingPluginGuid = ModDataHelper.GetCallingPluginGuid(Assembly.GetCallingAssembly());
				if (!key.StartsWith(callingPluginGuid))
				{
					key = callingPluginGuid + "." + key;
				}
			}
			string key2 = key;
			return SaveData(data, key2, saveLocation switch
			{
				SaveLocation.CurrentSave => ModDataHelper.GetCurrentSaveFileName(), 
				SaveLocation.GeneralSave => ModDataHelper.GetGeneralSaveFileName(), 
				_ => throw new ArgumentOutOfRangeException("saveLocation", saveLocation, "Invalid save location!"), 
			});
		}

		public static bool SaveData(IModDataKey modDataKey)
		{
			if (!ModDataHandler.ModDataValues.TryGetValue(modDataKey, out ModDataValue value))
			{
				ManualLogSource? logger = LethalModDataLib.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + " has no registered mod data attribute! If this is an instance of a class, make sure to register it using ModDataHandler.RegisterInstance!"));
				}
				return false;
			}
			string key = modDataKey.ToES3KeyString();
			SaveLocation saveLocation = value.SaveLocation;
			if (modDataKey.TryGetValue(out object value2))
			{
				return SaveData(value2, key, saveLocation, autoAddGuid: false);
			}
			ManualLogSource? logger2 = LethalModDataLib.Logger;
			if (logger2 != null)
			{
				logger2.LogDebug((object)("Failed to get value from field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "! Does it not have a getter?"));
			}
			return false;
		}
	}
}
namespace LethalModDataLib.Events
{
	public static class MiscEvents
	{
		public delegate void PostInitializeGameEventHandler();

		public static event PostInitializeGameEventHandler? PostInitializeGameEvent;

		internal static void OnPostInitializeGame()
		{
			MiscEvents.PostInitializeGameEvent?.Invoke();
		}
	}
	public static class SaveLoadEvents
	{
		public delegate void PostAutoSaveEventHandler(bool isChallenge, string saveFileName);

		public delegate void PostDeleteSaveEventHandler(string saveFileName);

		public delegate void PostLoadGameEventHandler(bool isChallenge, string saveFileName);

		public delegate void PostResetSavedGameValuesEventHandler();

		public delegate void PostSaveGameEventHandler(bool isChallenge, string saveFileName);

		public static event PostSaveGameEventHandler? PostSaveGameEvent;

		public static event PostAutoSaveEventHandler? PostAutoSaveEvent;

		public static event PostLoadGameEventHandler? PostLoadGameEvent;

		public static event PostDeleteSaveEventHandler? PostDeleteSaveEvent;

		public static event PostResetSavedGameValuesEventHandler? PostResetSavedGameValuesEvent;

		internal static void OnPostSaveGame(bool isChallenge, string saveFileName)
		{
			SaveLoadEvents.PostSaveGameEvent?.Invoke(isChallenge, saveFileName);
		}

		internal static void OnPostAutoSave(bool isChallenge, string saveFileName)
		{
			SaveLoadEvents.PostAutoSaveEvent?.Invoke(isChallenge, saveFileName);
		}

		internal static void OnPostLoadGame(bool isChallenge, string saveFileName)
		{
			SaveLoadEvents.PostLoadGameEvent?.Invoke(isChallenge, saveFileName);
		}

		internal static void OnPostDeleteSave(string saveFileName)
		{
			SaveLoadEvents.PostDeleteSaveEvent?.Invoke(saveFileName);
		}

		internal static void OnPostResetSavedGameValues()
		{
			SaveLoadEvents.PostResetSavedGameValuesEvent?.Invoke();
		}
	}
}
namespace LethalModDataLib.Enums
{
	[Flags]
	public enum IgnoreFlags
	{
		None = 0,
		OnSave = 1,
		OnLoad = 2,
		IfNull = 4,
		IfDefault = 8
	}
	[Flags]
	public enum LoadWhen
	{
		Manual = 0,
		OnLoad = 1,
		OnRegister = 2
	}
	[Flags]
	public enum ResetWhen
	{
		Manual = 0,
		OnGameOver = 1
	}
	public enum SaveLocation
	{
		CurrentSave,
		GeneralSave
	}
	[Flags]
	public enum SaveWhen
	{
		Manual = 0,
		OnAutoSave = 1,
		OnSave = 2
	}
}
namespace LethalModDataLib.Base
{
	public abstract class ModDataContainer
	{
		protected virtual SaveLocation SaveLocation { get; set; }

		protected virtual string OptionalPrefixSuffix { get; set; } = string.Empty;


		private List<FieldInfo> GetFields()
		{
			return (from field in GetType().GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
				where !ModDataHelper.IsKBackingField(field)
				select field).ToArray().ToList();
		}

		private List<PropertyInfo> GetProperties()
		{
			return (from property in GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
				where property.Name != "SaveLocation" && property.Name != "OptionalPrefixSuffix"
				select property).ToArray().ToList();
		}

		protected virtual string GetPrefix()
		{
			Type type = GetType();
			string text = string.Empty;
			if (!string.IsNullOrEmpty(OptionalPrefixSuffix))
			{
				text = OptionalPrefixSuffix + ".";
			}
			return type.Assembly.GetName().Name + "." + type.Name + "." + text;
		}

		private void SaveFields()
		{
			string prefix = GetPrefix();
			foreach (FieldInfo field in GetFields())
			{
				ModDataIgnoreAttribute customAttribute = field.GetCustomAttribute<ModDataIgnoreAttribute>();
				if (customAttribute != null)
				{
					if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.None) || customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.OnSave) || (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfNull) && field.GetValue(this) == null))
					{
						continue;
					}
					if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfDefault))
					{
						object obj = (field.FieldType.IsValueType ? Activator.CreateInstance(field.FieldType) : null);
						if (field.GetValue(this).Equals(obj))
						{
							continue;
						}
					}
				}
				SaveLoadHandler.SaveData(field.GetValue(this), prefix + field.Name, SaveLocation, autoAddGuid: false);
			}
		}

		private void SaveProperties()
		{
			string prefix = GetPrefix();
			foreach (PropertyInfo property in GetProperties())
			{
				if (property.GetGetMethod(nonPublic: true) == null)
				{
					continue;
				}
				ModDataIgnoreAttribute customAttribute = property.GetCustomAttribute<ModDataIgnoreAttribute>();
				if (customAttribute != null)
				{
					if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.None) || customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.OnSave) || (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfNull) && property.GetValue(this) == null))
					{
						continue;
					}
					if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfDefault))
					{
						object obj = (property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null);
						if (property.GetValue(this).Equals(obj))
						{
							continue;
						}
					}
				}
				SaveLoadHandler.SaveData(property.GetValue(this), prefix + property.Name, SaveLocation, autoAddGuid: false);
			}
		}

		public void Save()
		{
			PreSave();
			SaveFields();
			SaveProperties();
			PostSave();
		}

		protected virtual void PreSave()
		{
		}

		protected virtual void PostSave()
		{
		}

		private void LoadFields()
		{
			string prefix = GetPrefix();
			foreach (FieldInfo field in GetFields())
			{
				ModDataIgnoreAttribute customAttribute = field.GetCustomAttribute<ModDataIgnoreAttribute>();
				if (customAttribute != null)
				{
					if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.None) || customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.OnLoad) || (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfNull) && field.GetValue(this) == null))
					{
						continue;
					}
					if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfDefault))
					{
						object obj = (field.FieldType.IsValueType ? Activator.CreateInstance(field.FieldType) : null);
						if (field.GetValue(this).Equals(obj))
						{
							continue;
						}
					}
				}
				object value = SaveLoadHandler.LoadData<object>(prefix + field.Name, SaveLocation, null, autoAddGuid: false);
				field.SetValue(this, value);
			}
		}

		private void LoadProperties()
		{
			string prefix = GetPrefix();
			foreach (PropertyInfo property in GetProperties())
			{
				if (property.GetSetMethod(nonPublic: true) == null)
				{
					continue;
				}
				ModDataIgnoreAttribute customAttribute = property.GetCustomAttribute<ModDataIgnoreAttribute>();
				if (customAttribute != null)
				{
					if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.None) || customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.OnLoad) || (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfNull) && property.GetValue(this) == null))
					{
						continue;
					}
					if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfDefault))
					{
						object obj = (property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null);
						if (property.GetValue(this).Equals(obj))
						{
							continue;
						}
					}
				}
				object value = SaveLoadHandler.LoadData(prefix + property.Name, SaveLocation, property.GetValue(this), autoAddGuid: false);
				property.SetValue(this, value);
			}
		}

		public void Load()
		{
			PreLoad();
			LoadFields();
			LoadProperties();
			PostLoad();
		}

		protected virtual void PreLoad()
		{
		}

		protected virtual void PostLoad()
		{
		}
	}
}
namespace LethalModDataLib.Attributes
{
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
	public class ModDataAttribute : Attribute
	{
		public LoadWhen LoadWhen { get; set; } = LoadWhen.OnLoad;


		public SaveWhen SaveWhen { get; set; } = SaveWhen.OnSave;


		public ResetWhen ResetWhen { get; set; }

		public SaveLocation SaveLocation { get; set; }

		public string? BaseKey { get; set; }

		public ModDataAttribute(SaveWhen saveWhen = SaveWhen.OnSave, LoadWhen loadWhen = LoadWhen.OnLoad, SaveLocation saveLocation = SaveLocation.CurrentSave, ResetWhen resetWhen = ResetWhen.Manual, string? baseKey = null)
		{
			SaveWhen = saveWhen;
			LoadWhen = loadWhen;
			SaveLocation = saveLocation;
			ResetWhen = resetWhen;
			BaseKey = baseKey;
		}

		public ModDataAttribute()
		{
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
	public class ModDataIgnoreAttribute : Attribute
	{
		public IgnoreFlags IgnoreFlags { get; }

		public ModDataIgnoreAttribute(IgnoreFlags ignoreFlags = IgnoreFlags.None)
		{
			IgnoreFlags = ignoreFlags;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
	internal static class IsExternalInit
	{
	}
}