MTFO.dll

Decompiled 5 months ago
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using AK;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BepInEx.Unity.IL2CPP.Hook;
using CellMenu;
using ChainedPuzzles;
using GTFO.API;
using GameData;
using GameEvent;
using Gear;
using Globals;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppInterop.Runtime.InteropTypes.Fields;
using Il2CppInterop.Runtime.Runtime;
using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MTFO.API;
using MTFO.Custom;
using MTFO.Custom.CCP.Components;
using MTFO.CustomCP;
using MTFO.HotReload;
using MTFO.Managers;
using MTFO.NativeDetours;
using MTFO.Utilities;
using Player;
using SNetwork;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.Analytics;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("MTFO")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("© dakkhuza 2021")]
[assembly: AssemblyFileVersion("4.6.1")]
[assembly: AssemblyInformationalVersion("4.6.1+gitf831086-dirty-main")]
[assembly: AssemblyProduct("MTFO")]
[assembly: AssemblyTitle("MTFO")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("4.6.1.0")]
[module: UnverifiableCode]
namespace MTFO
{
	[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
	[DebuggerNonUserCode]
	[CompilerGenerated]
	internal class ConfigStrings
	{
		private static ResourceManager resourceMan;

		private static CultureInfo resourceCulture;

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static ResourceManager ResourceManager
		{
			get
			{
				if (resourceMan == null)
				{
					resourceMan = new ResourceManager("MTFO.ConfigStrings", typeof(ConfigStrings).Assembly);
				}
				return resourceMan;
			}
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static CultureInfo Culture
		{
			get
			{
				return resourceCulture;
			}
			set
			{
				resourceCulture = value;
			}
		}

		internal static string SECTION_DEBUG => ResourceManager.GetString("SECTION_DEBUG", resourceCulture);

		internal static string SECTION_DEV => ResourceManager.GetString("SECTION_DEV", resourceCulture);

		internal static string SECTION_GENERAL => ResourceManager.GetString("SECTION_GENERAL", resourceCulture);

		internal static string SETTING_DISABLE_ACHIEVEMENTS => ResourceManager.GetString("SETTING_DISABLE_ACHIEVEMENTS", resourceCulture);

		internal static string SETTING_DISABLE_ACHIEVEMENTS_DESC => ResourceManager.GetString("SETTING_DISABLE_ACHIEVEMENTS_DESC", resourceCulture);

		internal static string SETTING_DUMPDATA => ResourceManager.GetString("SETTING_DUMPDATA", resourceCulture);

		internal static string SETTING_DUMPDATA_DESC => ResourceManager.GetString("SETTING_DUMPDATA_DESC", resourceCulture);

		internal static string SETTING_DUMPDATA_MODE => ResourceManager.GetString("SETTING_DUMPDATA_MODE", resourceCulture);

		internal static string SETTING_DUMPDATA_MODE_DESC => ResourceManager.GetString("SETTING_DUMPDATA_MODE_DESC", resourceCulture);

		internal static string SETTING_HOTRELOAD => ResourceManager.GetString("SETTING_HOTRELOAD", resourceCulture);

		internal static string SETTING_HOTRELOAD_DESC => ResourceManager.GetString("SETTING_HOTRELOAD_DESC", resourceCulture);

		internal static string SETTING_RUNDOWNPACKAGE => ResourceManager.GetString("SETTING_RUNDOWNPACKAGE", resourceCulture);

		internal static string SETTING_RUNDOWNPACKAGE_DESC => ResourceManager.GetString("SETTING_RUNDOWNPACKAGE_DESC", resourceCulture);

		internal static string SETTING_USE_LEGACY_PATH => ResourceManager.GetString("SETTING_USE_LEGACY_PATH", resourceCulture);

		internal static string SETTING_USE_LEGACY_PATH_DESC => ResourceManager.GetString("SETTING_USE_LEGACY_PATH_DESC", resourceCulture);

		internal static string SETTING_VERBOSE => ResourceManager.GetString("SETTING_VERBOSE", resourceCulture);

		internal static string SETTING_VERBOSE_DESC => ResourceManager.GetString("SETTING_VERBOSE_DESC", resourceCulture);

		internal ConfigStrings()
		{
		}
	}
	[BepInPlugin("com.dak.MTFO", "MTFO", "4.6.1+gitf831086-dirty-main")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class MTFO : BasePlugin
	{
		public const string MODNAME = "MTFO";

		public const string AUTHOR = "dak";

		public const string GUID = "com.dak.MTFO";

		public const string VERSION = "4.6.1+gitf831086-dirty-main";

		public override void Load()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Analytics.enabled = false;
			Harmony harmony = new Harmony("com.dak.MTFO");
			AssetAPI.OnImplReady += delegate
			{
				if (ConfigManager.IsHotReloadEnabled)
				{
					ClassInjector.RegisterTypeInIl2Cpp<HotReloader>();
					harmony.PatchAll(typeof(HotReloadInjector));
				}
			};
			harmony.PatchAll();
			Detour_DataBlockBase.Patch();
		}
	}
	public static class DataDumperExtensions
	{
		public static int GetStableHashCode(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 class JsonNodeExtension
	{
		private static JsonDocumentOptions _JsonDocumentOptions = new JsonDocumentOptions
		{
			AllowTrailingCommas = true,
			CommentHandling = JsonCommentHandling.Skip
		};

		private static readonly JsonSerializerOptions _JsonSerializerOptions = new JsonSerializerOptions
		{
			AllowTrailingCommas = true,
			ReadCommentHandling = JsonCommentHandling.Skip,
			WriteIndented = true,
			Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
		};

		public static string ToJsonStringIndented(this JsonNode jsonNode)
		{
			return jsonNode.ToJsonString(_JsonSerializerOptions);
		}

		public static bool TryParseToJsonNode(this string json, out JsonNode jsonNode)
		{
			try
			{
				jsonNode = json.ToJsonNode();
				return jsonNode != null;
			}
			catch
			{
				jsonNode = null;
				return false;
			}
		}

		public static bool TryParseJsonNode(this Stream stream, bool dispose, out JsonNode jsonNode)
		{
			try
			{
				jsonNode = stream.ToJsonNode();
				return jsonNode != null;
			}
			catch
			{
				jsonNode = null;
				return false;
			}
			finally
			{
				if (dispose)
				{
					stream.Close();
				}
			}
		}

		public static JsonNode ToJsonNode(this string json)
		{
			return JsonNode.Parse(json, null, _JsonDocumentOptions);
		}

		public static JsonNode ToJsonNode(this Stream jsonStream)
		{
			return JsonNode.Parse(jsonStream, null, _JsonDocumentOptions);
		}

		public static bool TryAddJsonItem(this JsonArray jsonArray, string json)
		{
			try
			{
				if (jsonArray == null)
				{
					throw new Exception();
				}
				if (string.IsNullOrWhiteSpace(json))
				{
					throw new Exception();
				}
				if (!json.TryParseToJsonNode(out var jsonNode))
				{
					throw new Exception();
				}
				jsonArray.Add(jsonNode);
				return true;
			}
			catch
			{
				return false;
			}
		}
	}
	[GeneratedCode("VersionInfoGenerator", "2.0.0+git50a4b1a-master")]
	[CompilerGenerated]
	internal static class VersionInfo
	{
		public const string RootNamespace = "MTFO";

		public const string Version = "4.6.1";

		public const string VersionPrerelease = null;

		public const string VersionMetadata = "gitf831086-dirty-main";

		public const string SemVer = "4.6.1+gitf831086-dirty-main";

		public const string GitRevShort = "f831086-dirty";

		public const string GitRevLong = "f831086f4f6e1b696d810fe24edc29e810e53735-dirty";

		public const string GitBranch = "main";

		public const string GitTag = "v4.3.0";

		public const bool GitIsDirty = true;
	}
}
namespace MTFO.Utilities
{
	public class JsonSerializer
	{
		public JsonSerializerOptions Options;

		public JsonSerializer()
		{
			Options = new JsonSerializerOptions
			{
				AllowTrailingCommas = true,
				IncludeFields = true,
				PropertyNameCaseInsensitive = true,
				ReadCommentHandling = JsonCommentHandling.Skip,
				WriteIndented = true
			};
			Options.Converters.Add(new JsonStringEnumConverter());
		}

		public JsonSerializer(bool allowTrailingCommas = true, bool includeFields = true, bool propertyNameCaseInsensitive = true, bool writeIndented = true)
		{
			Options = new JsonSerializerOptions
			{
				AllowTrailingCommas = allowTrailingCommas,
				IncludeFields = includeFields,
				PropertyNameCaseInsensitive = propertyNameCaseInsensitive,
				ReadCommentHandling = JsonCommentHandling.Skip,
				WriteIndented = writeIndented
			};
			Options.Converters.Add(new JsonStringEnumConverter());
		}

		public void TryRead<T>(string path, out T output) where T : new()
		{
			if (File.Exists(path))
			{
				string json = File.ReadAllText(path);
				output = Deserialize<T>(json);
			}
			else
			{
				output = new T();
				string json = Serialize(output);
				File.WriteAllText(path, json);
			}
		}

		public string Serialize(object value)
		{
			return System.Text.Json.JsonSerializer.Serialize(value, Options);
		}

		public T Deserialize<T>(string json)
		{
			return System.Text.Json.JsonSerializer.Deserialize<T>(json, Options);
		}
	}
	public static class Log
	{
		private static readonly ManualLogSource logger;

		static Log()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			logger = new ManualLogSource("MTFO");
			Logger.Sources.Add((ILogSource)(object)logger);
		}

		public static void Verbose(object msg)
		{
			if (ConfigManager.IsVerbose)
			{
				logger.LogInfo(msg);
			}
		}

		public static void Debug(object msg)
		{
			logger.LogDebug(msg);
		}

		public static void Message(object msg)
		{
			logger.LogMessage(msg);
		}

		public static void Error(object msg)
		{
			logger.LogError(msg);
		}

		public static void Warn(object msg)
		{
			logger.LogWarning(msg);
		}
	}
	public enum BaseDirectory
	{
		BepInEx,
		Plugins,
		GameData
	}
	public static class PathUtil
	{
		public static string Prepare(BaseDirectory baseDir, params string[] subDirs)
		{
			string text = baseDir switch
			{
				BaseDirectory.BepInEx => Paths.BepInExRootPath, 
				BaseDirectory.Plugins => Paths.PluginPath, 
				BaseDirectory.GameData => Path.Combine(Paths.BepInExRootPath, "GameData"), 
				_ => throw new ArgumentOutOfRangeException("baseDir", $"{baseDir} is not a valid value for Argument: {"baseDir"}"), 
			};
			string obj = ((subDirs == null || subDirs.Length == 0) ? text : Path.Combine(text, Path.Combine(subDirs)));
			Directory.CreateDirectory(obj);
			return obj;
		}

		public static bool CheckCustomFile(string file, out string CombinedPath)
		{
			CombinedPath = Path.Combine(ConfigManager.CustomPath, file);
			if (File.Exists(CombinedPath))
			{
				return true;
			}
			return false;
		}

		public static bool CheckFile(string pathToFile)
		{
			if (File.Exists(pathToFile))
			{
				return true;
			}
			return false;
		}

		[Obsolete]
		public static string MakeRelativeDirectory(string path, bool createPath = true)
		{
			string text = Path.Combine(Path.Combine(Paths.ConfigPath, "Rundowns"), path);
			if (createPath && !Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			return text;
		}

		public static string MakeRelativeDirectory(string path, string folder)
		{
			string text = Path.Combine(path, folder);
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			return text;
		}

		public static void PrepareEmptyDirectory(string path)
		{
			if (Directory.Exists(path))
			{
				Directory.Delete(path, recursive: true);
			}
			Directory.CreateDirectory(path);
		}

		public static Stream OpenUtf8Stream(string filePath)
		{
			return new StreamReader(filePath, Encoding.UTF8).BaseStream;
		}
	}
}
namespace MTFO.Patches
{
	[HarmonyPatch(typeof(SteamUserStats), "SetAchievement")]
	internal static class Patch_SteamAPI_Achievement
	{
		private static bool Prefix(string pchName)
		{
			if (!ConfigManager.DisableAchievements)
			{
				return true;
			}
			Log.Error("Achievement Completion Blocked: " + pchName);
			return false;
		}
	}
	[HarmonyPatch(typeof(AnalyticsManager), "OnGameEvent", new Type[] { typeof(GameEventData) })]
	internal static class Patch_Analytics
	{
		public static bool Prefix(GameEventData data)
		{
			return false;
		}
	}
	[HarmonyPatch(typeof(SNet_Core_STEAM), "SetFriendsData", new Type[]
	{
		typeof(FriendsDataType),
		typeof(string)
	})]
	internal static class Patch_RichPresence2
	{
		public static void Prefix(FriendsDataType type, ref string data, SNet_Core_STEAM __instance)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if ((int)type != 1)
			{
				if ((int)type == 9)
				{
					data = "0";
					return;
				}
				Log.Verbose($"Lobby data\nType: {type} Data: {data}");
			}
			else
			{
				data = "MODDED - " + data;
			}
		}
	}
	[HarmonyPatch(typeof(GlowstickInstance), "Update")]
	internal static class Patch_Glowstick
	{
		public static void Postfix(GlowstickInstance __instance)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			if (ConfigManager.HasCustomContent && ConfigManager.CustomContent.GlowstickHolder != null && !((Object)(object)__instance == (Object)null) && (int)__instance.m_state != 3 && (int)__instance.m_state != 4 && ConfigManager.CustomContent.GlowstickHolder.GlowstickLookup.TryGetValue(((Item)__instance).PublicName, out var value) && (Object)(object)__instance.m_light != (Object)null)
			{
				((LightBase)__instance.m_light).Range = value.Range;
				((LightBase)__instance.m_light).Color = value.Color * __instance.m_progression;
				((LightBase)__instance.m_light).UpdateVisibility(true);
			}
		}
	}
	[HarmonyPatch(typeof(CP_Holopath_Spline), "Reveal")]
	internal static class Patch_InstantRevealPuzzlePath
	{
		public static bool Prefix(CP_Holopath_Spline __instance)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (__instance.m_revealSpeed < 0f)
			{
				if (!__instance.m_isSetup)
				{
					return false;
				}
				__instance.SetSplineProgress(1f);
				__instance.SetVisible(true);
				__instance.m_sound.UpdatePosition(__instance.TipPos);
				if (!__instance.m_didSetColor)
				{
					__instance.m_didSetColor = __instance.TrySetColor();
				}
				__instance.m_sound.Post(EVENTS.BIOSCAN_TUBE_EMITTER_STOP, true);
				Action onRevealDone = __instance.OnRevealDone;
				if (onRevealDone != null)
				{
					onRevealDone.Invoke();
				}
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(CM_PageLoadout), "UpdatePageData")]
	internal static class Patch_PageLoadoutUpdatePageData
	{
		public static void Prefix(CM_PageLoadout __instance)
		{
			((TMP_Text)((Component)((Transform)((CM_PageBase)__instance).m_movingContentHolder).Find("ShareServerId/ShareText")).gameObject.GetComponent<TextMeshPro>()).SetText("<color=red><b>Do not play modded content on the official GTFO server or online matchmake lobbies.</b></color>\n\nFeel free to join the unofficial discord server linked below and ask people to play.", true);
			__instance.m_discordButton.SetText("Mod Server");
			__instance.m_discordButton.OnBtnPressCallback = Action<int>.op_Implicit((Action<int>)delegate
			{
				Application.OpenURL("https://discord.com/invite/rRMPtv4FAh");
			});
		}
	}
	[HarmonyPatch(typeof(CM_PageLoadout), "UpdatePlayerBars")]
	internal static class Patch_PageLoadoutUpdatePlayerBars
	{
		public static void Prefix(CM_PageLoadout __instance)
		{
			foreach (CM_PlayerLobbyBar item in (Il2CppArrayBase<CM_PlayerLobbyBar>)(object)__instance.m_playerLobbyBars)
			{
				item.m_matchmakeButton.SetOnBtnPressCallback((Action<int>)null);
				item.m_matchmakeButton.SetText("DISABLED");
			}
		}
	}
	[HarmonyPatch(typeof(CM_PageRundown_New), "Intro_RevealRundown")]
	internal static class Patch_PageRundownNew
	{
		public static void Postfix(CM_PageRundown_New __instance)
		{
			__instance.m_discordButton.SetText("MOD SERVER");
			__instance.m_discordButton.OnBtnPressCallback = Action<int>.op_Implicit((Action<int>)delegate
			{
				Application.OpenURL("https://discord.com/invite/rRMPtv4FAh");
			});
			((RectTransformComp)__instance.m_matchmakeAllButton).SetVisible(false);
			__instance.m_matchmakeAllButton.SetText("MATCHMAKE DISABLED");
			__instance.m_matchmakeAllButton.OnBtnPressCallback = null;
			__instance.m_aboutTheRundownButton.SetText("THUNDERSTORE");
			__instance.m_aboutTheRundownButton.OnBtnPressCallback = Action<int>.op_Implicit((Action<int>)delegate
			{
				Application.OpenURL("https://gtfo.thunderstore.io/");
			});
		}
	}
	[HarmonyPatch(typeof(CM_PageRundown_New), "PlaceRundown")]
	internal static class Patch_RundownTierMarker
	{
		public static void Postfix(CM_PageRundown_New __instance)
		{
			ContentManager customContent = ConfigManager.CustomContent;
			if (customContent != null)
			{
				__instance.m_tierMarker1.m_name = customContent.TierNames.Tier1;
				__instance.m_tierMarker2.m_name = customContent.TierNames.Tier2;
				__instance.m_tierMarker3.m_name = customContent.TierNames.Tier3;
				__instance.m_tierMarker4.m_name = customContent.TierNames.Tier4;
				__instance.m_tierMarker5.m_name = customContent.TierNames.Tier5;
				__instance.m_tierMarker1.UpdateHeader();
				__instance.m_tierMarker2.UpdateHeader();
				__instance.m_tierMarker3.UpdateHeader();
				__instance.m_tierMarker4.UpdateHeader();
				__instance.m_tierMarker5.UpdateHeader();
			}
		}
	}
	[HarmonyPatch(typeof(PUI_Watermark), "UpdateWatermark")]
	internal static class Patch_WatermarkUpdateWatermark
	{
		public static void Postfix(PUI_Watermark __instance)
		{
			((TMP_Text)__instance.m_watermarkText).SetText("<color=red>MODDED</color> <color=orange>4.6.1</color>", true);
		}
	}
}
namespace MTFO.NativeDetours
{
	internal class DataBlockBaseWrapper
	{
		private static readonly HashSet<string> _PreferPartialDumpBlocks = new HashSet<string>
		{
			"RundownDataBlock".ToLower(),
			"LightSettingsDataBlock".ToLower(),
			"FogSettingsDataBlock".ToLower(),
			"LevelLayoutDataBlock".ToLower(),
			"WardenObjectiveDataBlock".ToLower(),
			"DimensionDataBlock".ToLower(),
			"EnemyDataBlock".ToLower(),
			"EnemySFXDataBlock".ToLower(),
			"EnemyBehaviorDataBlock".ToLower(),
			"EnemyBalancingDataBlock".ToLower(),
			"EnemyMovementDataBlock".ToLower(),
			"ArchetypeDataBlock".ToLower(),
			"PlayerOfflineGearDataBlock".ToLower(),
			"ComplexResourceSetDataBlock".ToLower(),
			"TextDataBlock".ToLower()
		};

		private IntPtr Ptr__m_fileNameNoExt;

		public IntPtr ClassPointer { get; private set; }

		public string FileName { get; private set; }

		public string BinaryFileName { get; private set; }

		public bool PreferPartialBlockOnDump { get; private set; }

		public unsafe DataBlockBaseWrapper(Il2CppMethodInfo* methodInfo)
		{
			INativeClassStruct val = UnityVersionHandler.Wrap(UnityVersionHandler.Wrap(methodInfo).Class);
			ClassPointer = ((INativeStruct)val).Pointer;
			Ptr__m_fileNameNoExt = IL2CPP.GetIl2CppField(((INativeStruct)val).Pointer, "m_fileNameNoExt");
			IntPtr zero = IntPtr.Zero;
			IL2CPP.il2cpp_field_static_get_value(Ptr__m_fileNameNoExt, (void*)(&zero));
			FileName = IL2CPP.Il2CppStringToManaged(zero).Replace('.', '_');
			BinaryFileName = FileName + "_bin";
			if (_PreferPartialDumpBlocks.Contains(FileName.ToLower().Replace("gamedata_", "")))
			{
				PreferPartialBlockOnDump = true;
			}
		}
	}
	internal static class Detour_DataBlockBase
	{
		private unsafe delegate IntPtr GetFileContentsDel(Il2CppMethodInfo* methodInfo);

		private static string _BasePathToDump;

		private static INativeDetour _Detour;

		private static GetFileContentsDel _Original;

		public unsafe static void Patch()
		{
			_BasePathToDump = Path.Combine(Paths.BepInExRootPath, "GameData-Dump", CellBuildData.GetRevision().ToString());
			if (ConfigManager.DumpGameData)
			{
				PathUtil.PrepareEmptyDirectory(_BasePathToDump);
			}
			_Detour = INativeDetour.CreateAndApply<GetFileContentsDel>((IntPtr)(nint)Il2CppAPI.GetIl2CppMethod<GameDataBlockBase<EnemyDataBlock>>("GetFileContents", typeof(string).FullName, false, Array.Empty<string>()), (GetFileContentsDel)Dtor_GetFileContents, ref _Original);
		}

		private unsafe static IntPtr Dtor_GetFileContents(Il2CppMethodInfo* methodInfo)
		{
			IntPtr intPtr = _Original(methodInfo);
			try
			{
				DataBlockBaseWrapper dataBlockBaseWrapper = new DataBlockBaseWrapper(methodInfo);
				string binaryFileName = dataBlockBaseWrapper.BinaryFileName;
				_ = binaryFileName + ".json";
				if (ConfigManager.DumpGameData)
				{
					DumpContent(dataBlockBaseWrapper, IL2CPP.Il2CppStringToManaged(intPtr));
				}
				Log.Verbose("GetFileContents Call of " + binaryFileName);
				Stream contentStream = GetContentStream(dataBlockBaseWrapper, intPtr);
				JsonNode jsonNode = contentStream.ToJsonNode();
				JsonArray jsonArray = jsonNode["Blocks"].AsArray();
				string path = Path.Combine(ConfigManager.GameDataPath, dataBlockBaseWrapper.FileName);
				if (Directory.Exists(path))
				{
					int num = 0;
					string[] files = Directory.GetFiles(path, "*.json");
					foreach (string text in files)
					{
						Log.Verbose(" - Trying to add PartialData [" + text + "]");
						if (File.Exists(text) && PathUtil.OpenUtf8Stream(text).TryParseJsonNode(dispose: true, out var jsonNode2))
						{
							jsonArray.Add(jsonNode2);
							num++;
						}
					}
					Log.Verbose($" - Added {num} partial data of {binaryFileName}");
				}
				List<string> jsonItemsToInject = new List<string>();
				MTFOGameDataAPI.Invoke_OnGameDataContentLoad(dataBlockBaseWrapper.FileName, contentStream, in jsonItemsToInject);
				foreach (string item2 in jsonItemsToInject)
				{
					try
					{
						JsonNode item = item2.ToJsonNode();
						jsonArray.Add(item);
					}
					catch (Exception ex)
					{
						Log.Error("Exception were found while reading Injected Json Data!");
						Log.Error(ex.ToString());
						Log.Error("Full Json:\n" + item2);
					}
				}
				uint num2 = 0u;
				foreach (JsonNode item3 in jsonNode["Blocks"].AsArray())
				{
					uint num3 = (uint)(JsonNode)item3["persistentID"].AsValue();
					if (num3 > num2)
					{
						num2 = num3;
					}
				}
				jsonNode["LastPersistentID"] = num2;
				string text2 = jsonNode.ToJsonStringIndented();
				IntPtr result = IL2CPP.ManagedStringToIl2Cpp(text2);
				MTFOGameDataAPI.Invoke_OnGameDataContentLoaded(dataBlockBaseWrapper.FileName, text2);
				return result;
			}
			catch (Exception ex2)
			{
				Log.Error("Exception were found while handling Detour;Falling back to original content!");
				Log.Error(ex2.ToString());
				return intPtr;
			}
		}

		private static Stream GetContentStream(DataBlockBaseWrapper datablock, IntPtr originalContentPtr)
		{
			string binaryFileName = datablock.BinaryFileName;
			string path = binaryFileName + ".json";
			string s = IL2CPP.Il2CppStringToManaged(originalContentPtr);
			string text = Path.Combine(ConfigManager.GameDataPath, path);
			if (File.Exists(text))
			{
				Log.Verbose("Opening filestream of [" + binaryFileName + "] from disk...");
				Log.Verbose(text);
				return PathUtil.OpenUtf8Stream(text);
			}
			Log.Verbose("No file found at [" + binaryFileName + "]");
			return new MemoryStream(Encoding.UTF8.GetBytes(s));
		}

		private static void DumpContent(DataBlockBaseWrapper datablock, string json)
		{
			if (!json.TryParseToJsonNode(out var jsonNode))
			{
				Log.Verbose("Unable to dump " + datablock.FileName + ", Invalid Json Content!");
				return;
			}
			bool flag = false;
			switch (ConfigManager.DumpMode)
			{
			case DumpGameDataMode.Single:
				flag = false;
				break;
			case DumpGameDataMode.PartialData:
				flag = datablock.PreferPartialBlockOnDump;
				break;
			case DumpGameDataMode.FullPartialData:
				flag = true;
				break;
			}
			if (flag)
			{
				JsonArray jsonArray = jsonNode["Blocks"].AsArray();
				string text = Path.Combine(path2: datablock.FileName, path1: _BasePathToDump);
				PathUtil.PrepareEmptyDirectory(text);
				foreach (JsonNode item in jsonArray)
				{
					string path = $"{item["persistentID"]}__{item["name"]}.json";
					string text2 = Path.Combine(text, path);
					File.WriteAllText(text2, item.ToJsonStringIndented());
					Log.Verbose(" - Save... " + text2);
				}
				jsonNode["Blocks"] = new JsonArray();
			}
			string path2 = datablock.BinaryFileName + ".json";
			File.WriteAllText(Path.Combine(_BasePathToDump, path2), jsonNode.ToJsonStringIndented());
			Log.Verbose(datablock.FileName + " has dumped to '" + _BasePathToDump + "'");
		}
	}
}
namespace MTFO.Managers
{
	public enum DumpGameDataMode
	{
		Single,
		PartialData,
		FullPartialData
	}
	public static class ConfigManager
	{
		private const string CUSTOM_FOLDER = "Custom";

		private static readonly ConfigEntry<bool> _enableHotReload;

		private static readonly ConfigEntry<bool> _dumpGameData;

		private static readonly ConfigEntry<bool> _isVerbose;

		private static readonly ConfigEntry<DumpGameDataMode> _dumpGameDataMode;

		private static readonly ConfigEntry<bool> _disableAchievements;

		public static int GAME_VERSION;

		public static ContentManager CustomContent;

		public static readonly string GameDataPath;

		public static readonly string CustomPath;

		public static bool HasGameDataPath;

		public static bool HasCustomContent;

		public static bool IsModded;

		public static bool IsPluginGameDataPath;

		public static bool IsVerbose => _isVerbose.Value;

		public static bool DisableAchievements => _disableAchievements.Value;

		public static bool IsHotReloadEnabled => _enableHotReload.Value;

		public static bool DumpGameData => _dumpGameData.Value;

		public static DumpGameDataMode DumpMode => _dumpGameDataMode.Value;

		private static string ResolveGameDataPath(string rootPath)
		{
			foreach (string item in Directory.GetFiles(rootPath, "GameData_*.json", SearchOption.AllDirectories).OrderBy(Path.GetDirectoryName))
			{
				if (Path.GetDirectoryName(item) != GameDataPath)
				{
					HasGameDataPath = true;
					return Path.GetDirectoryName(item);
				}
			}
			return null;
		}

		static ConfigManager()
		{
			//IL_0010: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "MTFO.cfg"), true);
			_enableHotReload = val.Bind<bool>(ConfigStrings.SECTION_DEV, ConfigStrings.SETTING_HOTRELOAD, false, ConfigStrings.SETTING_HOTRELOAD_DESC);
			_dumpGameData = val.Bind<bool>(ConfigStrings.SECTION_DEV, ConfigStrings.SETTING_DUMPDATA, false, ConfigStrings.SETTING_DUMPDATA_DESC);
			_dumpGameDataMode = val.Bind<DumpGameDataMode>(ConfigStrings.SECTION_DEV, ConfigStrings.SETTING_DUMPDATA_MODE, DumpGameDataMode.Single, ConfigStrings.SETTING_DUMPDATA_MODE_DESC);
			_isVerbose = val.Bind<bool>(ConfigStrings.SECTION_DEBUG, ConfigStrings.SETTING_VERBOSE, false, ConfigStrings.SETTING_VERBOSE_DESC);
			_disableAchievements = val.Bind<bool>(ConfigStrings.SECTION_GENERAL, ConfigStrings.SETTING_DISABLE_ACHIEVEMENTS, true, ConfigStrings.SETTING_DISABLE_ACHIEVEMENTS_DESC);
			GAME_VERSION = GetGameVersion();
			GameDataPath = ResolveGameDataPath(PathUtil.Prepare(BaseDirectory.GameData));
			IsPluginGameDataPath = string.IsNullOrEmpty(GameDataPath);
			if (IsPluginGameDataPath)
			{
				GameDataPath = ResolveGameDataPath(Paths.PluginPath);
				Log.Warn("Plugin paths for gamedata are under legacy support and will be removed in the future. Considering migrating to the '\\BepInEx\\GameData' folder.");
			}
			if (HasGameDataPath)
			{
				CustomPath = Path.Combine(GameDataPath, "Custom");
				HasCustomContent = Directory.Exists(CustomPath);
			}
			else
			{
				GameDataPath = string.Empty;
				CustomPath = string.Empty;
				HasCustomContent = false;
				IsPluginGameDataPath = false;
			}
			try
			{
				CustomContent = new ContentManager();
			}
			catch (Exception value)
			{
				HasCustomContent = false;
				Log.Error($"Failed to init custom content!\nIs your JSON valid?\n---- ERROR MESSAGE ---- {value} ---- END ERROR MESSAGE ----");
			}
			Log.Debug("---- DEBUG INFO ----");
			Log.Debug($"Time: {DateTime.Now}");
			Log.Debug($"Game Version: {GAME_VERSION}");
			Log.Debug("---- PATHS ----");
			Log.Debug("Path to rundown: " + GameDataPath);
			Log.Debug("Path to custom content: " + CustomPath);
			Log.Debug("---- FLAGS ----");
			Log.Debug($"Has GameData Path? {HasGameDataPath}");
			Log.Debug($"Using plugin GameData path? {IsPluginGameDataPath}");
			Log.Debug($"Has Custom Content? {HasCustomContent}");
			Log.Debug($"Hot Reload Enabled? {IsHotReloadEnabled}");
			Log.Debug($"Verbose Logging? {IsVerbose}");
			Log.Debug($"Dump Game Data? {DumpGameData}");
			Log.Debug($"Are Achievements Disabled? {DisableAchievements}");
			Log.Debug("---- DEBUG END ----");
		}

		private static int GetGameVersion()
		{
			return CellBuildData.GetRevision();
		}
	}
	public class ContentManager
	{
		private readonly global::MTFO.Utilities.JsonSerializer json = new global::MTFO.Utilities.JsonSerializer();

		private readonly Dictionary<string, Action<string>> Handlers;

		public ScanHolder ScanHolder;

		public GlowstickHolder GlowstickHolder;

		public TierNames TierNames;

		public ContentManager()
		{
			Handlers = new Dictionary<string, Action<string>>
			{
				{ "puzzletypes.json", SetupChainedPuzzles },
				{ "glowsticks.json", SetupGlowsticks },
				{ "tiernames.json", SetupTierNames }
			};
			Init();
		}

		private void Init()
		{
			if (!ConfigManager.HasCustomContent)
			{
				return;
			}
			foreach (string key in Handlers.Keys)
			{
				if (PathUtil.CheckCustomFile(key, out var CombinedPath))
				{
					Handlers.TryGetValue(key, out var value);
					try
					{
						value?.Invoke(CombinedPath);
					}
					catch (Exception msg)
					{
						Log.Error(msg);
					}
					Log.Debug(CombinedPath);
				}
			}
		}

		public void SetupChainedPuzzles(string path)
		{
			Log.Debug("Custom puzzles found");
			ScanHolder = json.Deserialize<ScanHolder>(File.ReadAllText(path));
		}

		public void SetupGlowsticks(string path)
		{
			Log.Debug("Custom glowsticks found");
			GlowstickHolder = json.Deserialize<GlowstickHolder>(File.ReadAllText(path));
			GlowstickHolder.Setup();
		}

		public void SetupTierNames(string path)
		{
			TierNames = json.Deserialize<TierNames>(File.ReadAllText(path));
		}
	}
}
namespace MTFO.HotReload
{
	internal class HotGameDataManager : IHotManager
	{
		public void OnHotReload(int id)
		{
			GameDataInit.ReInitialize();
			Log.Verbose("Reinitialized GameData");
		}
	}
	internal class HotGearManager : IHotManager
	{
		private readonly int gearSlotsTotal = 3;

		public void OnHotReload(int id)
		{
			GearManager.Current.m_offlineSetupDone = false;
			CleanGearIcons();
			CleanGearSlots();
			GearManager.Current.SetupGearContainers();
			LoadOfflineGearDatas();
			GearManager.GenerateAllGearIcons();
			GearManager.Current.m_offlineSetupDone = true;
			Log.Verbose("Reloaded Gear");
		}

		private void CleanGearIcons()
		{
			GearManager.m_allGearWithPostedIconJobs.Clear();
			foreach (Dictionary<uint, RenderTexture> item in (Il2CppArrayBase<Dictionary<uint, RenderTexture>>)(object)GearManager.Current.m_allGearIconTexturesPerInstanceKey)
			{
				item.Clear();
			}
		}

		[Obsolete]
		private void CleanGearLobbySlots()
		{
			if (!((Object)(object)CM_PageLoadout.Current != (Object)null))
			{
				return;
			}
			foreach (CM_InventorySlotItem componentsInChild in ((Component)CM_PageLoadout.Current.m_popupAlign).gameObject.GetComponentsInChildren<CM_InventorySlotItem>(true))
			{
				Object.Destroy((Object)(object)((Component)componentsInChild).gameObject);
			}
		}

		private void CleanGearSlots()
		{
			for (int i = 0; i < gearSlotsTotal; i++)
			{
				((Il2CppArrayBase<List<GearIDRange>>)(object)GearManager.Current.m_gearPerSlot)[i].Clear();
			}
		}

		private void LoadOfflineGearDatas()
		{
			Il2CppArrayBase<PlayerOfflineGearDataBlock> allBlocks = GameDataBlockBase<PlayerOfflineGearDataBlock>.GetAllBlocks();
			if (allBlocks == null)
			{
				Log.Warn("Unable to get Player Offline Gear blocks");
				return;
			}
			Log.Verbose($"Loading {allBlocks.Length} gear");
			foreach (PlayerOfflineGearDataBlock item in allBlocks)
			{
				OfflineGear.Load(item);
			}
		}
	}
	public class HotReloader : MonoBehaviour
	{
		public static HotReloader Current;

		private CM_Item button;

		private readonly string buttonLabel = "Reload Game Data";

		private readonly Vector3 buttonPosition = new Vector3(0f, 77f, 0f);

		private readonly List<IHotManager> managers = new List<IHotManager>();

		public HotReloader(IntPtr intPtr)
			: base(intPtr)
		{
		}//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)


		private void Awake()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			((Component)this).gameObject.transform.localPosition = buttonPosition;
			button = ((Component)this).gameObject.GetComponent<CM_Item>();
			button.SetText(buttonLabel);
			AddOnReloadListener(new HotGameDataManager());
			AddOnReloadListener(new HotRundownManager());
			AddOnReloadListener(new HotGearManager());
			button.OnBtnPressCallback += Action<int>.op_Implicit((Action<int>)delegate
			{
				MTFOHotReloadAPI.HotReloaded();
			});
		}

		public void AddOnReloadListener(IHotManager manager)
		{
			if (!managers.Contains(manager))
			{
				button.OnBtnPressCallback += Action<int>.op_Implicit((Action<int>)manager.OnHotReload);
				managers.Add(manager);
			}
		}

		public void RemoveOnReloadListener(IHotManager manager)
		{
			if (managers.Contains(manager))
			{
				button.OnBtnPressCallback -= Action<int>.op_Implicit((Action<int>)manager.OnHotReload);
				managers.Remove(manager);
			}
		}

		public static void Setup()
		{
			if (!((Object)(object)Current != (Object)null) && !((Object)(object)MainMenuGuiLayer.Current.PageRundownNew == (Object)null))
			{
				GameObject obj = Object.Instantiate<GameObject>(((Component)MainMenuGuiLayer.Current.PageRundownNew.m_discordButton).gameObject, ((Component)MainMenuGuiLayer.Current.PageRundownNew.m_discordButton).transform.parent, false);
				((Object)obj).name = "Button HotReload";
				Current = obj.AddComponent<HotReloader>();
				obj.SetActive(true);
				Log.Verbose("Created hot reload button");
			}
		}
	}
	public class HotReloadInjector
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(CM_PageRundown_New), "OnEnable")]
		public static void OnEnable()
		{
			HotReloader.Setup();
		}
	}
	internal class HotRundownManager : IHotManager
	{
		private CM_PageRundown_New rundownPage;

		private bool hasValidRundown => GameDataBlockBase<RundownDataBlock>.s_blockByID.ContainsKey(Global.RundownIdToLoad);

		private RundownDataBlock rundownDataCurrent => GameDataBlockBase<RundownDataBlock>.GetBlock(Global.RundownIdToLoad);

		public HotRundownManager()
		{
			rundownPage = MainMenuGuiLayer.Current.PageRundownNew;
		}

		public void OnHotReload(int id)
		{
			if (hasValidRundown)
			{
				rundownPage.m_dataIsSetup = false;
				CleanIconsOfTier();
				TryPlaceRundown();
			}
			else
			{
				Log.Warn($"Failed to place the rundown due to missing Rundown id {Global.RundownIdToLoad}");
			}
			Log.Verbose("Reloaded Rundown");
		}

		private void CleanIconsOfTier()
		{
			CleanIconsOfTier(rundownPage.m_expIconsTier1);
			CleanIconsOfTier(rundownPage.m_expIconsTier2);
			CleanIconsOfTier(rundownPage.m_expIconsTier3);
			CleanIconsOfTier(rundownPage.m_expIconsTier4);
			CleanIconsOfTier(rundownPage.m_expIconsTier5);
		}

		private void CleanIconsOfTier(List<CM_ExpeditionIcon_New> tier)
		{
			Enumerator<CM_ExpeditionIcon_New> enumerator = tier.GetEnumerator();
			while (enumerator.MoveNext())
			{
				Object.Destroy((Object)(object)((Component)enumerator.Current).gameObject);
			}
		}

		private void TryPlaceRundown()
		{
			rundownPage.m_currentRundownData = rundownDataCurrent;
			if (rundownPage.m_currentRundownData != null)
			{
				rundownPage.PlaceRundown(rundownPage.m_currentRundownData);
				rundownPage.m_dataIsSetup = true;
			}
			else
			{
				Log.Warn("Unable to place rundown due to null data during reload");
			}
		}
	}
	public interface IHotManager
	{
		void OnHotReload(int id);
	}
	public class OfflineGear
	{
		private GearIDRange gearIDRange;

		private ItemDataBlock itemData;

		private int inventorySlot;

		private uint persistentID;

		private OfflineGear(PlayerOfflineGearDataBlock block)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (TryParseGearJson(block.GearJSON, out gearIDRange))
			{
				gearIDRange.PlayfabItemId = ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name;
				gearIDRange.PlayfabItemInstanceId = $"OfflineGear_ID_{((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID}";
				gearIDRange.OfflineGearType = block.Type;
			}
			if (TryParseGearID(gearIDRange, out itemData, out inventorySlot))
			{
				persistentID = ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID;
				return;
			}
			Log.Warn($"Unable to construct Offline Gear [{((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID}] {((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name}");
		}

		public static void Load(PlayerOfflineGearDataBlock block)
		{
			if (TryParse(block, out var result) && TryStash(result))
			{
				Log.Verbose($"Loaded offline gear [{((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID}] {((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name}");
			}
		}

		private static bool TryParse(PlayerOfflineGearDataBlock block, out OfflineGear result)
		{
			//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_0007: 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_000b: Invalid comparison between Unknown and I4
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			eOfflineGearType type = block.Type;
			if (type - 1 <= 1)
			{
				result = new OfflineGear(block);
				return true;
			}
			result = null;
			Log.Warn($"Unimplemented Offline Gear Type [{((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID}] {block.Type}");
			return false;
		}

		private bool TryParseGearID(GearIDRange gearIDRange, out ItemDataBlock itemData, out int inventorySlot)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected I4, but got Unknown
			inventorySlot = 0;
			itemData = null;
			if (gearIDRange == null)
			{
				Log.Warn("Unable to parse GearIDRange due to it being null");
				return false;
			}
			uint compID = gearIDRange.GetCompID((eGearComponent)3);
			itemData = ((compID != 0) ? GameDataBlockBase<ItemDataBlock>.GetBlock(compID) : null);
			if (itemData == null)
			{
				Log.Warn($"Invalid ItemDataBlock for component in offlinear gear [c:{compID}]");
				return false;
			}
			inventorySlot = (int)itemData.inventorySlot;
			return true;
		}

		private bool TryParseGearJson(string gearJson, out GearIDRange gearIDRange)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			if (string.IsNullOrEmpty(gearJson))
			{
				gearIDRange = null;
				Log.Warn("Unable to assign GearIDRange due to null or empty GearJson");
				return false;
			}
			gearIDRange = new GearIDRange(gearJson);
			if (gearIDRange == null)
			{
				return false;
			}
			return true;
		}

		private static bool TryStash(OfflineGear gear)
		{
			if (gear == null)
			{
				Log.Warn("Unable to stash due to null offline gear");
				return false;
			}
			if (gear.gearIDRange == null)
			{
				Log.Warn($"Unable to stash offline gear due to null GearIDRange [{gear.persistentID}]");
				return false;
			}
			if (gear.itemData == null)
			{
				Log.Warn($"Unable to stash offline gear due to null ItemDataBlock [{gear.persistentID}]");
				return false;
			}
			((Il2CppArrayBase<List<GearIDRange>>)(object)GearManager.Current.m_gearPerSlot)[gear.inventorySlot].Add(gear.gearIDRange);
			return true;
		}
	}
}
namespace MTFO.CustomCP
{
	public static class CustomPuzzleManager
	{
		private static readonly Dictionary<uint, CustomBioScan> s_scanMap = new Dictionary<uint, CustomBioScan>();

		private static readonly Dictionary<uint, CustomClusterScan> s_clusterMap = new Dictionary<uint, CustomClusterScan>();

		private static bool s_initialized = false;

		public static bool TryGetScanByID(uint id, out CustomBioScan scan)
		{
			return s_scanMap.TryGetValue(id, out scan);
		}

		public static bool TryGetClusterByID(uint id, out CustomClusterScan cluster)
		{
			return s_clusterMap.TryGetValue(id, out cluster);
		}

		internal static void Initialize(ChainedPuzzleManager manager)
		{
			if (!s_initialized)
			{
				s_initialized = true;
				ClassInjector.RegisterTypeInIl2Cpp<CorePuzzleData>();
				ClassInjector.RegisterTypeInIl2Cpp<ClusterPuzzleData>();
				if (ConfigManager.CustomContent.ScanHolder != null)
				{
					InitScans(manager);
					InitClusters(manager);
				}
			}
		}

		private static void InitScans(ChainedPuzzleManager manager)
		{
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)manager == (Object)null)
			{
				Log.Error("Attempted to initialize custom bioscans with a null ChainedPuzzleManager. This should not happen!");
				throw new NullReferenceException("ChainedPuzzleManager was null");
			}
			foreach (CustomBioScan scan in ConfigManager.CustomContent.ScanHolder.Scans)
			{
				Log.Debug($"Adding scan with ID of [{scan.PersistentID}]");
				if (!manager.m_puzzleComponentPrefabs.ContainsKey(scan.BaseScan))
				{
					Log.Error($"Custom scan with persistent ID {scan.PersistentID} references a non-existing base scan of persistent ID {scan.BaseScan}");
					continue;
				}
				GameObject val = manager.m_puzzleComponentPrefabs[scan.BaseScan];
				if ((Object)(object)val.GetComponent<CP_Bioscan_Core>() == (Object)null)
				{
					Log.Error($"BaseScan id: {scan.PersistentID} does not have {"CP_Bioscan_Core"} component!");
					continue;
				}
				GameObject val2 = Object.Instantiate<GameObject>(val);
				val2.transform.position = new Vector3(10000f, 10000f, 10000f);
				CP_PlayerScanner component = val2.GetComponent<CP_PlayerScanner>();
				if ((Object)(object)component != (Object)null)
				{
					component.m_reduceSpeed = scan.ReduceSpeed;
					component.m_reduceWhenNoPlayer = scan.ReduceWhenNoPlayer;
					component.m_scanRadius = scan.ScanRadius;
					component.m_scanSpeeds = Il2CppStructArray<float>.op_Implicit(scan.PlayersInScanMulti);
					component.m_playerRequirement = scan.PlayerRequirement;
				}
				else
				{
					Log.Warn($"BaseScan id: {scan.BaseScan} does not have {"CP_PlayerScanner"} component! This will make following setting won't work!");
					Log.Warn(" - ReduceSpeed");
					Log.Warn(" - ReduceWhenNoPlayer");
					Log.Warn(" - ScanRadius");
					Log.Warn(" - PlayersInScanMulti");
					Log.Warn(" - PlayerRequirement");
				}
				CP_Bioscan_Graphics component2 = val2.GetComponent<CP_Bioscan_Graphics>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.m_radius = scan.BioScanGraphics.Radius;
					component2.m_colors = Il2CppReferenceArray<ColorModeColor>.op_Implicit(ConvertToColorMode(scan.BioScanGraphics.ColorModeColor));
					component2.SetText(scan.BioScanGraphics.ScanText);
				}
				else
				{
					Log.Warn($"BaseScan id: {scan.BaseScan} does not have {"CP_Bioscan_Graphics"} component! This will make {"BioScanGraphics"} setting won't work!");
				}
				val2.GetComponent<CP_Bioscan_Core>().m_playerAgents = new List<PlayerAgent>();
				val2.AddComponent<CorePuzzleData>().PersistentID.Set(scan.PersistentID);
				s_scanMap.Add(scan.PersistentID, scan);
				manager.m_puzzleComponentPrefabs.Add(scan.PersistentID, val2);
			}
		}

		private static void InitClusters(ChainedPuzzleManager manager)
		{
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)manager == (Object)null)
			{
				Log.Error("Attempted to initialize custom clusters with a null ChainedPuzzleManager. This should not happen!");
				throw new NullReferenceException("ChainedPuzzleManager was null");
			}
			foreach (CustomClusterScan cluster in ConfigManager.CustomContent.ScanHolder.Clusters)
			{
				Log.Debug($"Adding cluster with ID of [{cluster.PersistentID}]");
				if (!manager.m_puzzleComponentPrefabs.ContainsKey(cluster.BaseCluster))
				{
					Log.Error($"Custom cluster scan with persistent ID {cluster.PersistentID} references a non-existing base cluster of persistent ID {cluster.BaseCluster}");
					continue;
				}
				if (!manager.m_puzzleComponentPrefabs.ContainsKey(cluster.BioscanID))
				{
					Log.Error($"Custom cluster scan with persistent ID {cluster.PersistentID} references a non-existing bioscan of persistent ID {cluster.BioscanID}");
					continue;
				}
				GameObject val = manager.m_puzzleComponentPrefabs[cluster.BaseCluster];
				if ((Object)(object)val.GetComponent<CP_Cluster_Core>() == (Object)null)
				{
					Log.Error($"BaseScan id: {cluster.PersistentID} does not have {"CP_Cluster_Core"} component!");
				}
				else
				{
					GameObject val2 = Object.Instantiate<GameObject>(val);
					val2.transform.position = new Vector3(1000f, 1000f, 1000f);
					CP_Cluster_Core component = val2.GetComponent<CP_Cluster_Core>();
					GameObject childPuzzlePrefab = manager.m_puzzleComponentPrefabs[cluster.BioscanID];
					component.m_amountOfPuzzles = cluster.ClusterCount;
					component.m_childPuzzlePrefab = childPuzzlePrefab;
					component.m_distanceBetween = cluster.DistanceBetweenScans;
					component.m_revealWithHoloPath = cluster.RevealWithHoloPath;
					val2.AddComponent<ClusterPuzzleData>().PersistentID.Set(cluster.PersistentID);
					s_clusterMap.Add(cluster.PersistentID, cluster);
					manager.m_puzzleComponentPrefabs.Add(cluster.PersistentID, val2);
				}
			}
		}

		private static ColorModeColor[] ConvertToColorMode(CustomBioScan.BioScanColorByMode[] bioScanColorByModes)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			ColorModeColor[] array = (ColorModeColor[])(object)new ColorModeColor[bioScanColorByModes.Length];
			for (int i = 0; i < bioScanColorByModes.Length; i++)
			{
				CustomBioScan.BioScanColorByMode bioScanColorByMode = bioScanColorByModes[i];
				array[i] = new ColorModeColor
				{
					col = new Color(bioScanColorByMode.R, bioScanColorByMode.G, bioScanColorByMode.B, bioScanColorByMode.A),
					mode = bioScanColorByMode.Mode
				};
			}
			return array;
		}
	}
}
namespace MTFO.CustomCP.Patches
{
	[HarmonyPatch(typeof(ChainedPuzzleManager), "OnAssetsLoaded")]
	internal static class Patch_PuzzleManager
	{
		internal static void Postfix(ChainedPuzzleManager __instance)
		{
			CustomPuzzleManager.Initialize(__instance);
		}
	}
	[HarmonyPatch(typeof(CP_Bioscan_Core), "Update")]
	internal static class FixNullRefSpam
	{
		internal static bool Prefix(CP_Bioscan_Core __instance)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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)
			if (((Component)__instance).gameObject.transform.position.x > 9000f && ((Component)__instance).gameObject.transform.position.y > 9000f && ((Component)__instance).gameObject.transform.position.z > 9000f)
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class Patch_SetupPuzzleCores
	{
		[HarmonyPostfix]
		[HarmonyWrapSafe]
		[HarmonyPatch(typeof(CP_Bioscan_Core), "Setup")]
		public static void SetupSpline(CP_Bioscan_Core __instance)
		{
			CorePuzzleData component = ((Component)__instance).GetComponent<CorePuzzleData>();
			if (!((Object)(object)component == (Object)null) && CustomPuzzleManager.TryGetScanByID(component.PersistentID.Value, out var scan))
			{
				CP_Holopath_Spline val = ((Il2CppObjectBase)__instance.m_spline).TryCast<CP_Holopath_Spline>();
				if ((Object)(object)val != (Object)null)
				{
					scan.ApplySplineRevealSpeed(val);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyWrapSafe]
		[HarmonyPatch(typeof(CP_Cluster_Core), "Setup")]
		public static void SetupSpline(CP_Cluster_Core __instance)
		{
			ClusterPuzzleData component = ((Component)__instance).GetComponent<ClusterPuzzleData>();
			if (!((Object)(object)component == (Object)null) && CustomPuzzleManager.TryGetClusterByID(component.PersistentID.Value, out var cluster))
			{
				CP_Holopath_Spline val = ((Il2CppObjectBase)__instance.m_spline).TryCast<CP_Holopath_Spline>();
				if ((Object)(object)val != (Object)null)
				{
					cluster.ApplySplineRevealSpeed(val);
				}
			}
		}
	}
}
namespace MTFO.Custom
{
	public class GlowstickHolder
	{
		[JsonIgnore]
		public Dictionary<string, CustomGlowstick> GlowstickLookup;

		public List<GlowstickConfig> Glowsticks { get; set; }

		public void Setup()
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			GlowstickLookup = new Dictionary<string, CustomGlowstick>();
			foreach (GlowstickConfig glowstick in Glowsticks)
			{
				Log.Verbose(glowstick);
				if (GlowstickLookup.TryGetValue(glowstick.Name, out var value))
				{
					Log.Warn("Custom glowstick with name " + glowstick.Name + " already exists in the lookup! Skipping...");
					continue;
				}
				value = default(CustomGlowstick);
				value.Color = new Color(glowstick.r, glowstick.g, glowstick.b, (glowstick.a == 0f) ? 1f : glowstick.a);
				value.Range = ((glowstick.Range == 0f) ? 15f : glowstick.Range);
				CustomGlowstick value2 = value;
				GlowstickLookup.Add(glowstick.Name, value2);
			}
		}
	}
	public struct CustomGlowstick
	{
		public Color Color { get; set; }

		public float Range { get; set; }
	}
	public struct GlowstickConfig
	{
		public string Name { get; set; }

		public float Range { get; set; }

		public float r { get; set; }

		public float g { get; set; }

		public float b { get; set; }

		public float a { get; set; }

		public override string ToString()
		{
			return $"{Name},{Range},{r},{g},{b},{a}";
		}
	}
	public class ScanHolder
	{
		public List<CustomBioScan> Scans { get; set; }

		public List<CustomClusterScan> Clusters { get; set; }
	}
	public enum RevealMode
	{
		ScaleByDistance,
		ConstantTime,
		Instant
	}
	public struct CustomBioScan : IRevealableScanConfig
	{
		public struct BioScanGx
		{
			public bool HideScanText { get; set; }

			public string ScanText { get; set; }

			public float Radius { get; set; }

			public BioScanColorByMode[] ColorModeColor { get; set; }
		}

		public class BioScanColorByMode
		{
			public eChainedPuzzleGraphicsColorMode Mode { get; set; }

			public float R { get; set; }

			public float G { get; set; }

			public float B { get; set; }

			public float A { get; set; }
		}

		public uint BaseScan { get; set; }

		public uint PersistentID { get; set; }

		public PlayerRequirement PlayerRequirement { get; set; }

		public float ScanRadius { get; set; }

		public float[] PlayersInScanMulti { get; set; }

		public float ReduceSpeed { get; set; }

		public bool ReduceWhenNoPlayer { get; set; }

		public float RevealTime { get; set; }

		public RevealMode RevealMode { get; set; }

		public BioScanGx BioScanGraphics { get; set; }
	}
	public struct CustomClusterScan : IRevealableScanConfig
	{
		public uint BaseCluster { get; set; }

		public uint PersistentID { get; set; }

		public int ClusterCount { get; set; }

		public uint BioscanID { get; set; }

		public float DistanceBetweenScans { get; set; }

		public float RevealTime { get; set; }

		public RevealMode RevealMode { get; set; }

		public bool RevealWithHoloPath { get; set; }
	}
	internal interface IRevealableScanConfig
	{
		uint PersistentID { get; }

		float RevealTime { get; }

		RevealMode RevealMode { get; }
	}
	internal static class IRevealibleScanConfigExtensions
	{
		public static void ApplySplineRevealSpeed(this IRevealableScanConfig scan, CP_Holopath_Spline spline)
		{
			float revealTime = scan.RevealTime;
			switch (scan.RevealMode)
			{
			case RevealMode.ConstantTime:
				if (revealTime <= 0f)
				{
					Log.Warn($"Attempted to set a custom scan with persistent id '{scan.PersistentID}' to reveal in less than or equal to 0 seconds. This is not supported. Instead, use RevealMode \"{2}\" or integer value {2}");
				}
				else
				{
					spline.m_splineLength = 1f;
					spline.m_revealSpeed = revealTime;
				}
				break;
			case RevealMode.ScaleByDistance:
				if (revealTime < 0f)
				{
					Log.Warn($"Attempted to set a custom scan with persistent id '{scan.PersistentID}' to reveal in less than 0 seconds. This is not supported.");
				}
				else if (revealTime != 0f)
				{
					spline.m_revealSpeed = revealTime;
				}
				break;
			case RevealMode.Instant:
				spline.m_revealSpeed = -1f;
				break;
			}
		}
	}
	public struct TierNames
	{
		public string Tier1 { get; set; }

		public string Tier2 { get; set; }

		public string Tier3 { get; set; }

		public string Tier4 { get; set; }

		public string Tier5 { get; set; }
	}
}
namespace MTFO.Custom.CCP.Patches
{
	[HarmonyPatch(typeof(CP_Bioscan_Hud))]
	internal static class Patch_Bioscan_Hud
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		internal static void Post_Setup(CP_Bioscan_Hud __instance)
		{
			UpdateText(__instance);
		}

		[HarmonyPatch("SetVisible")]
		[HarmonyPostfix]
		internal static void Post_SetVisible(CP_Bioscan_Hud __instance)
		{
			UpdateText(__instance);
		}

		[HarmonyPatch("UpdateText")]
		[HarmonyPostfix]
		internal static void Post_UpdateText(CP_Bioscan_Hud __instance)
		{
			UpdateText(__instance);
		}

		private static void UpdateText(CP_Bioscan_Hud hud)
		{
			CorePuzzleData component = ((Component)hud).gameObject.GetComponent<CorePuzzleData>();
			if ((Object)(object)component != (Object)null)
			{
				if (!CustomPuzzleManager.TryGetScanByID(component.PersistentID.Value, out var scan))
				{
					Log.Error($"scanData missing... {component.PersistentID.Value}");
				}
				else if (scan.BioScanGraphics.HideScanText)
				{
					((TMP_Text)hud.m_bioscanWorldText).SetText(string.Empty, true);
				}
				else if (!string.IsNullOrWhiteSpace(scan.BioScanGraphics.ScanText))
				{
					((TMP_Text)hud.m_bioscanWorldText).SetText(scan.BioScanGraphics.ScanText, true);
				}
			}
		}
	}
}
namespace MTFO.Custom.CCP.Components
{
	internal sealed class ClusterPuzzleData : MonoBehaviour
	{
		public Il2CppValueField<uint> PersistentID;

		public CP_Bioscan_Hud[] ChildHuds = Array.Empty<CP_Bioscan_Hud>();
	}
	internal sealed class CorePuzzleData : MonoBehaviour
	{
		public Il2CppValueField<uint> PersistentID;
	}
}
namespace MTFO.API
{
	public delegate void GameDataContentLoadEvent(string datablockName, string jsonContent, in List<string> jsonItemsToInject);
	public delegate void GameDataContentLoadedDelegate(string datablockName, string jsonContent);
	public static class MTFOGameDataAPI
	{
		public static event GameDataContentLoadEvent OnGameDataContentLoad;

		public static event GameDataContentLoadedDelegate OnGameDataContentLoaded;

		internal static void Invoke_OnGameDataContentLoad(string datablockName, Stream jsonContentStream, in List<string> jsonItemsToInject)
		{
			if (MTFOGameDataAPI.OnGameDataContentLoad != null)
			{
				MTFOGameDataAPI.OnGameDataContentLoad(datablockName, new StreamReader(jsonContentStream).ReadToEnd(), in jsonItemsToInject);
			}
		}

		internal static void Invoke_OnGameDataContentLoaded(string datablockName, string jsonContent)
		{
			MTFOGameDataAPI.OnGameDataContentLoaded?.Invoke(datablockName, jsonContent);
		}
	}
	public static class MTFOHotReloadAPI
	{
		public static bool HotReloadEnabled => ConfigManager.IsHotReloadEnabled;

		public static event Action OnHotReload;

		internal static void HotReloaded()
		{
			MTFOHotReloadAPI.OnHotReload?.Invoke();
		}
	}
	public static class MTFOPathAPI
	{
		public static string RundownPath => ConfigManager.GameDataPath;

		public static bool HasRundownPath => ConfigManager.HasGameDataPath;

		public static string CustomPath => ConfigManager.CustomPath;

		public static bool HasCustomPath => ConfigManager.HasCustomContent;
	}
}