Decompiled source of WKLocalizationLoader v0.7.0

plugins/WKLocalizationLoader.dll

Decompiled an hour ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DG.Tweening;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using Febucci.UI;
using Febucci.UI.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Rewired.Glyphs.UnityUI;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;
using WKLocalizationLoader.Config;
using WKLocalizationLoader.FontFactory;
using WKLocalizationLoader.Modules;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("WKLocalizationLoader")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.7.0.0")]
[assembly: AssemblyInformationalVersion("0.7.0+18f62122941d9cbfccb16a040db0dc3b536ed909")]
[assembly: AssemblyProduct("WKLocalizationLoader")]
[assembly: AssemblyTitle("WKLocalizationLoader")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.7.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 WKLocalizationLoader
{
	public static class CacheManager
	{
		private static JsonSerializerSettings _jsonSerializerSettings;

		private static Dictionary<string, Font> _fontCache;

		private static Dictionary<string, TMP_FontAsset> _fontAssetCache;

		private static Dictionary<(string, RegexOptions), Regex> _regexCache;

		private static Dictionary<string, TextAsset> _textAssetCache;

		private static Plugin _plugin;

		private static ManualLogSource _logger;

		public static void Initialize(Plugin plugin)
		{
			//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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_001d: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			_jsonSerializerSettings = new JsonSerializerSettings
			{
				ContractResolver = (IContractResolver)new DefaultContractResolver
				{
					NamingStrategy = (NamingStrategy)new DefaultNamingStrategy()
				},
				Formatting = (Formatting)1,
				TypeNameHandling = (TypeNameHandling)0,
				NullValueHandling = (NullValueHandling)1,
				MissingMemberHandling = (MissingMemberHandling)0,
				ReferenceLoopHandling = (ReferenceLoopHandling)1
			};
			_fontCache = new Dictionary<string, Font>();
			_fontAssetCache = new Dictionary<string, TMP_FontAsset>();
			if (plugin != null)
			{
				_plugin = plugin;
				string text = ((BaseUnityPlugin)_plugin).Info.Metadata.Name + "/CacheManager";
				_logger = Logger.CreateLogSource(text);
			}
		}

		public static void AddFontToMemoryCache(string hash, Font font)
		{
			if (_fontCache == null)
			{
				_fontCache = new Dictionary<string, Font>();
			}
			if (!string.IsNullOrWhiteSpace(hash) && font != null)
			{
				_fontCache[hash] = font;
			}
		}

		public static Font GetFontFromMemoryCache(string hash)
		{
			if (!string.IsNullOrWhiteSpace(hash) && _fontCache != null && _fontCache.TryGetValue(hash, out var value))
			{
				return value;
			}
			return null;
		}

		public static Font CreateFontFromDiskCache(string hash)
		{
			if (string.IsNullOrWhiteSpace(hash) || _jsonSerializerSettings == null)
			{
				return null;
			}
			Font result = null;
			try
			{
				if (FileManager.TryGetFontCachePaths(hash, out var cacheDataPath, out var atlasPath))
				{
					result = FontBuilder.CreateFontFromDiskCache(cacheDataPath, atlasPath, _jsonSerializerSettings, _logger);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("An error occurred while creating Font (hash: " + hash + ") from disk cache."));
				}
				ManualLogSource logger2 = _logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)ex.Message);
				}
				result = null;
			}
			return result;
		}

		public static void WriteFontDiskCache(string hash, Font font)
		{
			if (string.IsNullOrWhiteSpace(hash) || font == null || _jsonSerializerSettings == null)
			{
				return;
			}
			try
			{
				string cacheFolder = Path.Combine(FileManager.FontCacheFolder, hash);
				FontBuilder.WriteFontDiskCache(cacheFolder, font, _jsonSerializerSettings, _logger);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("An error occurred while writing Font (hash: " + hash + ") cache to disk."));
				}
				ManualLogSource logger2 = _logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)ex.Message);
				}
			}
		}

		public static void AddFontAssetToMemoryCache(string hash, TMP_FontAsset fontAsset)
		{
			if (_fontAssetCache == null)
			{
				_fontAssetCache = new Dictionary<string, TMP_FontAsset>();
			}
			if (!string.IsNullOrWhiteSpace(hash) && fontAsset != null)
			{
				_fontAssetCache[hash] = fontAsset;
			}
		}

		public static TMP_FontAsset GetFontAssetFromMemoryCache(string hash)
		{
			if (!string.IsNullOrWhiteSpace(hash) && _fontAssetCache != null && _fontAssetCache.TryGetValue(hash, out var value))
			{
				return value;
			}
			return null;
		}

		public static TMP_FontAsset CreateFontAssetFromDiskCache(string hash)
		{
			if (string.IsNullOrWhiteSpace(hash) || _jsonSerializerSettings == null)
			{
				return null;
			}
			TMP_FontAsset result = null;
			try
			{
				if (FileManager.TryGetFontAssetCachePaths(hash, out var cacheDataPath, out var atlasPathMatches))
				{
					List<string> atlasPaths = (from p in atlasPathMatches
						orderby int.Parse(p.MatchResult.Groups[1].Value)
						select p.Path).ToList();
					result = FontAssetBuilder.CreateFontAssetFromDiskCache(cacheDataPath, atlasPaths, _jsonSerializerSettings, _logger);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("An error occurred while creating FontAsset (hash: " + hash + ") from disk cache."));
				}
				ManualLogSource logger2 = _logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)ex.Message);
				}
				result = null;
			}
			return result;
		}

		public static void WriteFontAssetDiskCache(string hash, TMP_FontAsset fontAsset)
		{
			if (string.IsNullOrWhiteSpace(hash) || fontAsset == null || _jsonSerializerSettings == null)
			{
				return;
			}
			try
			{
				string cacheFolder = Path.Combine(FileManager.FontAssetCacheFolder, hash);
				FontAssetBuilder.WriteFontAssetDiskCache(cacheFolder, fontAsset, _jsonSerializerSettings, _logger);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("An error occurred while writing FontAsset (hash: " + hash + ") cache to disk."));
				}
				ManualLogSource logger2 = _logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)ex.Message);
				}
			}
		}

		public static Regex GetOrCreateRegex(string pattern, RegexOptions regexOptions = RegexOptions.None)
		{
			if (_regexCache == null)
			{
				_regexCache = new Dictionary<(string, RegexOptions), Regex>();
			}
			(string, RegexOptions) key = (pattern, regexOptions);
			Regex value = null;
			if (_regexCache.TryGetValue(key, out value) && value != null)
			{
				return value;
			}
			value = new Regex(pattern, regexOptions);
			_regexCache[key] = value;
			return value;
		}

		public static TextAsset GetOrCreateTextAsset(string text)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			if (_textAssetCache == null)
			{
				_textAssetCache = new Dictionary<string, TextAsset>();
			}
			TextAsset value = null;
			if (_textAssetCache.TryGetValue(text, out value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = new TextAsset(text);
			_textAssetCache[text] = value;
			return value;
		}
	}
	public static class ConfigManager
	{
		private static Plugin _plugin;

		private static ConfigFile _config;

		private static ManualLogSource _logger;

		public static void Initialize(Plugin plugin)
		{
			if (plugin != null)
			{
				_plugin = plugin;
				_config = ((BaseUnityPlugin)plugin).Config;
				string text = ((BaseUnityPlugin)_plugin).Info.Metadata.Name + "/ConfigManager";
				_logger = Logger.CreateLogSource(text);
			}
		}

		public static bool IsModuleEnabled(string section, string moduleDescription = null)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			if (_config == null)
			{
				return true;
			}
			ConfigDefinition val = new ConfigDefinition(section, "IsEnabled");
			string text = "Set this field to \"false\" to disable this module.";
			ConfigEntry<bool> val2 = default(ConfigEntry<bool>);
			if (moduleDescription != null)
			{
				text = moduleDescription + "\n" + text;
			}
			else if (_config.TryGetEntry<bool>(val, ref val2))
			{
				text = ((ConfigEntryBase)val2).Description.Description;
			}
			ConfigDescription val3 = new ConfigDescription(text, (AcceptableValueBase)null, Array.Empty<object>());
			ConfigEntry<bool> val4 = _config.Bind<bool>(val, true, val3);
			return val4.Value;
		}

		public static bool IsModuleUserOverridesEnabled(string section)
		{
			if (_config == null)
			{
				return false;
			}
			ConfigEntry<bool> val = _config.Bind<bool>(section, "EnableUserOverrides", false, "Set this field to \"true\" to apply the custom values below.");
			return val.Value;
		}

		public static object GetConfigEntryValue(string section, string moduleDescription, string key, object defaultValue, string entryDescription)
		{
			if (_config == null)
			{
				return defaultValue;
			}
			bool flag = IsModuleEnabled(section, moduleDescription);
			bool flag2 = IsModuleUserOverridesEnabled(section);
			object obj = BindConfigEntryValue(section, key, defaultValue, entryDescription);
			return (flag && flag2) ? obj : defaultValue;
		}

		public static object BindConfigEntryValue(string section, string key, object defaultValue, string entryDescription)
		{
			if (1 == 0)
			{
			}
			ConfigEntryBase val;
			if (!(defaultValue is bool flag))
			{
				if (!(defaultValue is int num))
				{
					if (!(defaultValue is float num2))
					{
						if (!(defaultValue is string text))
						{
							throw new NotSupportedException("\"" + (defaultValue?.GetType().Name ?? "Null") + "\" entry type isn't currently supported.");
						}
						val = (ConfigEntryBase)(object)_config.Bind<string>(section, key, text, entryDescription);
					}
					else
					{
						val = (ConfigEntryBase)(object)_config.Bind<float>(section, key, num2, entryDescription);
					}
				}
				else
				{
					val = (ConfigEntryBase)(object)_config.Bind<int>(section, key, num, entryDescription);
				}
			}
			else
			{
				val = (ConfigEntryBase)(object)_config.Bind<bool>(section, key, flag, entryDescription);
			}
			if (1 == 0)
			{
			}
			ConfigEntryBase val2 = val;
			return val2.BoxedValue;
		}
	}
	public static class FileManager
	{
		private static Plugin _plugin;

		private static ManualLogSource _logger;

		private static string _rootFolder;

		private static string _languageFolder;

		public static string RootFolder
		{
			get
			{
				if (_rootFolder == null)
				{
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogWarning((object)"RootFolder is null.");
					}
				}
				else if (!Directory.Exists(_rootFolder))
				{
					ManualLogSource logger2 = _logger;
					if (logger2 != null)
					{
						logger2.LogWarning((object)("RootFolder \"" + _rootFolder + "\" does not exist."));
					}
				}
				return _rootFolder ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			}
		}

		public static string LanguageFolder
		{
			get
			{
				if (_languageFolder == null)
				{
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogWarning((object)"LanguageFolder is null.");
					}
				}
				else if (!Directory.Exists(_languageFolder))
				{
					ManualLogSource logger2 = _logger;
					if (logger2 != null)
					{
						logger2.LogWarning((object)("LanguageFolder \"" + _languageFolder + "\" does not exist."));
					}
				}
				return _languageFolder ?? RootFolder;
			}
		}

		public static string FontsFolder => Path.Combine(LanguageFolder, "Fonts");

		public static string CacheFolder => Path.Combine(RootFolder, "Cache");

		public static string FontCacheFolder => Path.Combine(CacheFolder, "Fonts");

		public static string FontAssetCacheFolder => Path.Combine(CacheFolder, "FontAssets");

		public static void Initialize(Plugin plugin)
		{
			_rootFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (plugin != null)
			{
				_plugin = plugin;
				string text = ((BaseUnityPlugin)_plugin).Info.Metadata.Name + "/FileManager";
				_logger = Logger.CreateLogSource(text);
				SetLanguageFolder(_plugin.LanguageFolder);
			}
		}

		public static void SetLanguageFolder(string languageFolder)
		{
			_languageFolder = Path.GetFullPath(Path.Combine(Paths.PluginPath, languageFolder));
		}

		public static bool TryGetModuleFilePath(string fileName, out string filePath)
		{
			filePath = null;
			return TryGetExistingFilePath(LanguageFolder, fileName, out filePath);
		}

		public static bool TryGetFontFilePath(string fileName, out string filePath)
		{
			filePath = null;
			return TryGetExistingFilePath(FontsFolder, fileName, out filePath);
		}

		public static bool TryGetFontCachePaths(string hash, out string cacheDataPath, out string atlasPath)
		{
			cacheDataPath = null;
			atlasPath = null;
			if (TryGetExistingFolderPath(FontCacheFolder, hash, out var folderPath))
			{
				return TryGetExistingFilePath(folderPath, "CachedFontData.json", out cacheDataPath) && TryGetExistingFilePath(folderPath, "RawAtlasTextureData", out atlasPath);
			}
			return false;
		}

		public static bool TryGetFontAssetCachePaths(string hash, out string cacheDataPath, out IEnumerable<PathMatchResult> atlasPathMatches)
		{
			cacheDataPath = null;
			atlasPathMatches = null;
			if (TryGetExistingFolderPath(FontAssetCacheFolder, hash, out var folderPath))
			{
				return TryGetExistingFilePath(folderPath, "CachedFontAssetData.json", out cacheDataPath) && TrySearchFilePaths(folderPath, "^RawAtlasTextureData_(\\d+)", out atlasPathMatches);
			}
			return false;
		}

		public static bool TryGetExistingFilePath(string folder, string fileName, out string filePath)
		{
			try
			{
				filePath = Path.Combine(folder, fileName);
				return File.Exists(filePath);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)ex.Message);
				}
				filePath = null;
				return false;
			}
		}

		public static bool TryGetExistingFolderPath(string parentFolder, string folder, out string folderPath)
		{
			try
			{
				folderPath = Path.Combine(parentFolder, folder);
				return Directory.Exists(folderPath);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)ex.Message);
				}
				folderPath = null;
				return false;
			}
		}

		public static bool TrySearchFilePaths(string folder, string fileNamePattern, out IEnumerable<PathMatchResult> matchResults)
		{
			try
			{
				Regex regex = CacheManager.GetOrCreateRegex(fileNamePattern);
				matchResults = from f in Directory.EnumerateFiles(folder)
					select new PathMatchResult(f, regex.Match(Path.GetFileName(f))) into p
					where p.MatchResult.Success
					select p;
				return matchResults.Count() > 0;
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)ex.Message);
				}
				matchResults = null;
				return false;
			}
		}
	}
	public static class HashCalculator
	{
		private static JsonSerializerSettings _jsonSerializerSettings;

		public static void Initialize()
		{
			//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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_001d: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			_jsonSerializerSettings = new JsonSerializerSettings
			{
				ContractResolver = (IContractResolver)new DefaultContractResolver
				{
					NamingStrategy = (NamingStrategy)new DefaultNamingStrategy()
				},
				Formatting = (Formatting)0,
				NullValueHandling = (NullValueHandling)0,
				DefaultValueHandling = (DefaultValueHandling)2
			};
		}

		public static string GetHashString(string characters, FontProperties fontProperties, int hashStringLength = 6)
		{
			var targetObject = new
			{
				Characters = characters,
				Properties = fontProperties
			};
			return GetHashString(targetObject, hashStringLength);
		}

		public static string GetHashString(string characters, FontAssetProperties fontAssetProperties, int hashStringLength = 6)
		{
			var targetObject = new
			{
				Characters = characters,
				Properties = fontAssetProperties
			};
			return GetHashString(targetObject, hashStringLength);
		}

		public static string GetHashString(object targetObject, int hashStringLength)
		{
			if (_jsonSerializerSettings == null)
			{
				Initialize();
			}
			string s = JsonConvert.SerializeObject(targetObject, _jsonSerializerSettings);
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(s));
			string text = BitConverter.ToString(array).Replace("-", "");
			return text.Substring(0, hashStringLength).ToLower();
		}
	}
	public static class LanguageScanner
	{
		public static List<string> Scan(int maxRecersionDepth = 5, ManualLogSource logger = null)
		{
			List<string> list = new List<string>();
			ScanLanguageFolders(Paths.PluginPath, maxRecersionDepth, 0, list, logger);
			return list;
		}

		public static void ScanLanguageFolders(string directory, int maxDepth, int currentDepth, List<string> results, ManualLogSource logger = null)
		{
			IEnumerable<string> enumerable = Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly);
			foreach (string item in enumerable)
			{
				if (!IsLanguageFolderMarkerFile(item))
				{
					continue;
				}
				string text = "." + directory.Substring(Paths.PluginPath.Length);
				text = text.Replace("\\", "/");
				if (!results.Contains(text))
				{
					if (logger != null)
					{
						logger.LogInfo((object)("Auto-detected Language Folder \"" + text + "\"."));
					}
					results.Add(text);
				}
			}
			if (currentDepth >= maxDepth)
			{
				return;
			}
			IEnumerable<string> enumerable2 = Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly);
			foreach (string item2 in enumerable2)
			{
				ScanLanguageFolders(item2, maxDepth, currentDepth + 1, results, logger);
			}
		}

		public static bool IsLanguageFolderMarkerFile(string filePath)
		{
			string fileName = Path.GetFileName(filePath);
			if (1 == 0)
			{
			}
			bool result = fileName == ".wklocalization";
			if (1 == 0)
			{
			}
			return result;
		}
	}
	public class ModuleInfo
	{
		public Type ModuleClass;

		public ModuleStatus Status;

		public string Message;

		public ModuleInfo(Type moduleClass, ModuleStatus status, string message)
		{
			ModuleClass = moduleClass;
			Status = status;
			Message = message;
		}
	}
	public static class ModuleManager
	{
		private static Plugin _plugin;

		private static ManualLogSource _logger;

		private static JsonSerializerSettings _jsonSerializerSettings;

		private static List<ModuleInfo> _moduleInfos;

		private static ValueCollection<Type, string> _conflictedModGUIDs;

		public static List<ModuleInfo> ModuleInfos
		{
			get
			{
				if (_moduleInfos == null)
				{
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogWarning((object)"ModuleInfos is null.");
					}
					return new List<ModuleInfo>();
				}
				return _moduleInfos;
			}
		}

		public static void Initialize(Plugin plugin)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			if ((Object)(object)plugin != (Object)null)
			{
				_plugin = plugin;
				string text = ((BaseUnityPlugin)_plugin).Info.Metadata.Name + "/ModuleManager";
				_logger = Logger.CreateLogSource(text);
			}
			_jsonSerializerSettings = new JsonSerializerSettings
			{
				ContractResolver = (IContractResolver)(object)new ModuleContractResolver()
			};
			_moduleInfos = new List<ModuleInfo>();
			_conflictedModGUIDs = new ValueCollection<Type, string>();
		}

		public static void LoadAllModules()
		{
			LoadModule<AchievementPatch>("Achievements.json");
			LoadModule<AnnouncementSubtitlePatch>("AnnouncementSubtitles.json");
			LoadModule<AnnouncementSubtitleTimingPatch>("AnnouncementSubtitleTimings.json");
			LoadModule<ClosedCaptionSubtitlePatch>("ClosedCaptionSubtitles.json");
			LoadModule<CosmeticPatch>("Cosmetics.json");
			LoadModule<DeathTextPatch>("DeathTexts.json");
			LoadModule<DocumentPatch>("Documents.json");
			LoadModule<FacilityUpgradePatch>("FacilityUpgrades.json");
			LoadModule<FontPatch>("Fonts.json");
			LoadModule<FontAssetPatch>("FontAssets.json");
			LoadModule<GamemodePatch>("Gamemodes.json");
			LoadModule<GameplayTextPatch>("GameplayTexts.json");
			LoadModule<ItemDescriptionPatch>("ItemDescriptions.json");
			LoadModule<LocationNamePatch>("LocationNames.json");
			LoadModule<MainMenuPatch>("MainMenu.json");
			LoadModule<MotherSubtitlePatch>("MotherSubtitles.json");
			LoadModule<NotePatch>("Notes.json");
			LoadModule<ObjectivePatch>("Objectives.json");
			LoadModule<PerkPatch>("Perks.json");
			LoadModule<ProgressionUnlockPatch>("ProgressionUnlocks.json");
			LoadModule<QuietOSPatch>("QuietOS.json");
			LoadModule<RecordingSubtitlePatch>("RecordingSubtitles.json");
			LoadModule<RecordingSubtitleTimingPatch>("RecordingSubtitleTimings.json");
			LoadModule<RoachTraderSubtitlePatch>("RoachTraderSubtitles.json");
			LoadModule<ScoreScreenPatch>("ScoreScreen.json");
			LoadModule<StaticTextPatch>("StaticTexts.json");
			LoadModule<TextScrawlPatch>("TextScrawls.json");
			LoadModule<TrinketPatch>("Trinkets.json");
		}

		public static void LoadModule<TModule>(string fileName) where TModule : ModuleBase<TModule>
		{
			Type typeFromHandle = typeof(TModule);
			if (!FileManager.TryGetModuleFilePath(fileName, out var filePath))
			{
				RegisterFileMissingModule(typeFromHandle);
				return;
			}
			try
			{
				string text = File.ReadAllText(filePath);
				if (string.IsNullOrWhiteSpace(text))
				{
					throw new InvalidDataException("File content is empty or whitespace.");
				}
				JsonConvert.DeserializeObject<TModule>(text, _jsonSerializerSettings);
			}
			catch (Exception ex)
			{
				ModuleBase<TModule>.IsEnabled = false;
				RegisterDeserializationFailedModule(typeFromHandle, fileName, ex);
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)ex);
				}
				return;
			}
			List<string> conflictedModGUIDs;
			if (!CheckSupportedGameVersion(typeFromHandle))
			{
				ModuleBase<TModule>.IsEnabled = false;
				RegisterGameVersionUnsupportedModule(typeFromHandle);
			}
			else if (CheckConflictedMods(typeFromHandle, out conflictedModGUIDs))
			{
				ModuleBase<TModule>.IsEnabled = false;
				RegisterModConflictedModule(typeFromHandle, conflictedModGUIDs);
			}
			else if (!ModuleBase<TModule>.IsEnabled)
			{
				RegisterDisabledModule(typeFromHandle);
			}
			else
			{
				RegisterOKModule(typeFromHandle);
			}
		}

		public static void RegisterOKModule(Type moduleClass)
		{
			string message = "Loaded \"" + moduleClass.Name + "\" successfully.";
			RegisterModule(moduleClass, ModuleStatus.OK, message);
		}

		public static void RegisterDisabledModule(Type moduleClass)
		{
			string message = "\"" + moduleClass.Name + "\" is loaded but manually disabled in config.";
			RegisterModule(moduleClass, ModuleStatus.Disabled, message);
		}

		public static void RegisterFileMissingModule(Type moduleClass)
		{
			string message = "\"" + moduleClass.Name + "\" is missing its associated .json file and disabled by default.";
			RegisterModule(moduleClass, ModuleStatus.Disabled, message);
		}

		public static void RegisterModConflictedModule(Type moduleClass, List<string> conflictedModGUIDs)
		{
			string text = null;
			text = ((conflictedModGUIDs != null && conflictedModGUIDs.Count != 0) ? ("\"" + moduleClass.Name + "\" is disabled to avoid conflicts with the following mod(s):\n" + string.Join("\n", conflictedModGUIDs)) : ("\"" + moduleClass.Name + "\" is disabled to avoid conflicts."));
			RegisterModule(moduleClass, ModuleStatus.Conflicted, text);
		}

		public static void RegisterDeserializationFailedModule(Type moduleClass, string filePath, Exception e)
		{
			string message = "An error occurred while deserializing \"" + moduleClass.Name + "\" from \"" + filePath + "\".\n" + e.Message;
			RegisterModule(moduleClass, ModuleStatus.Failed, message);
		}

		public static void RegisterGameVersionUnsupportedModule(Type moduleClass)
		{
			string message = "\"" + moduleClass.Name + "\" is disabled on current version of the game that the mod does not yet fully support.";
			RegisterModule(moduleClass, ModuleStatus.Unsupported, message);
		}

		public static void RegisterModule(Type moduleClass, ModuleStatus status, string message)
		{
			if (_moduleInfos == null)
			{
				_moduleInfos = new List<ModuleInfo>();
			}
			ModuleInfo moduleInfo = _moduleInfos.FirstOrDefault((ModuleInfo m) => m.ModuleClass == moduleClass);
			if (moduleInfo == null)
			{
				moduleInfo = new ModuleInfo(moduleClass, status, message);
				_moduleInfos.Add(moduleInfo);
			}
			else
			{
				moduleInfo.Status = status;
				moduleInfo.Message = message;
			}
		}

		public static bool CheckSupportedGameVersion(Type moduleClass)
		{
			CrossGameVersionCompatibleAttribute customAttribute = moduleClass.GetCustomAttribute<CrossGameVersionCompatibleAttribute>();
			if (customAttribute != null)
			{
				return true;
			}
			if (_plugin == null)
			{
				return false;
			}
			return _plugin.CheckSupportedGameVersion() || _plugin.AllowAllModulesOnUnsupportedGameVersion;
		}

		public static bool CheckConflictedMods(Type moduleClass, out List<string> conflictedModGUIDs)
		{
			conflictedModGUIDs = null;
			if (_conflictedModGUIDs != null && _conflictedModGUIDs.TryGetValues(moduleClass, out conflictedModGUIDs))
			{
				return conflictedModGUIDs.Any((string g) => Chainloader.PluginInfos.ContainsKey(g) || Harmony.HasAnyPatches(g));
			}
			return false;
		}
	}
	public enum ModuleStatus
	{
		OK,
		Disabled,
		Conflicted,
		Failed,
		Unsupported
	}
	public class PathMatchResult
	{
		public string Path;

		public Match MatchResult;

		public PathMatchResult(string path, Match matchResult)
		{
			Path = path;
			MatchResult = matchResult;
		}
	}
	[BepInPlugin("mimimi-turret.wk-localization-loader", "WKLocalizationLoader", "0.7.0")]
	[BepInProcess("White Knuckle.exe")]
	public class Plugin : BaseUnityPlugin
	{
		private ConfigEntry<string> _languageFolder;

		private ConfigEntry<int> _maxScanDepth;

		private ConfigEntry<bool> _skipTranslationPatching;

		private ConfigEntry<string> _allowAllModulesOnUnsupportedGameVersion;

		public static ManualLogSource Logger;

		public string SupportedGameVersion => "0.62";

		public string LanguageFolder => _languageFolder?.Value;

		public int MaxScanDepth => _maxScanDepth?.Value ?? 5;

		public bool SkipTranslationPatching => _skipTranslationPatching?.Value ?? false;

		public bool AllowAllModulesOnUnsupportedGameVersion => Application.version == _allowAllModulesOnUnsupportedGameVersion?.Value;

		private string _unsupportedGameVersionWarning => "Incompatibility Warning:\nYou are using the mod on a version of the game that the mod does not yet fully support.\n\nMost modules will be disabled except the following ones:\n* StaticTextPatch\n* FontPatch\n* FontAssetPatch\n\nIf you wish to allow for all modules to load, set \"AllowAllModulesOnUnsupportedGameVersion\" in mod config to \"" + Application.version + "\" and restart the game.\n(VERY NOT RECOMMENDED / HIGHLY RISKY!!!)";

		private string _allowAllModulesOnUnsupportedGameVersionWarning => "Incompatibility Warning:\nYou are forcing all modules to load on a version of the game that the mod does not yet fully support.\n\nTo disable this experimental feature, set \"AllowAllModulesOnUnsupportedGameVersion\" in mod config to an empty string and restart your game.\n\nIt is VERY RECOMMENDED to keep backups of your save.\nProceed at your own risk. You have been warned!";

		private string _languageFolderDescription => "Specifies the path to a Language Folder.\nA relative path is resolved from \"BepInEx\\plugins\\\".\n\nA Language Folder may contain any of the following files:\n* StaticTexts.json\n* FontAssets.json\n* Fonts\\\n* Licenses\\ (licenses of the fonts, etc.)\n\nLeave this field empty to auto-detect a \nLanguage Folder installed in \"BepInEx\\plugins\\\".\nBy default, the plugin will load the first valid Language Folder it detects.\nFor further info, see \"MaxScanDepth\" below or plugin wiki.";

		private string _maxScanDepthDescription => "Limits the directory depth when auto-detecting Language Folders.\n\nA Language Folder is detected when it contains the following file:\n* .wklocalization\nNote: This file is for auto-detection purpose only.\nIt does not store any actual information or data.\n\nScanning will start from \"BepInEx\\plugins\\\" where the directory depth is 0.";

		private string _skipTranslationPatchingDescription => "Set this to \"true\" to disable translation patching during loading.\nTranslation .json files will be loaded as usual,\nbut no translation patches will be applied.";

		private string _allowAllModulesOnUnsupportedGameVersionDescription => "This mod is not tested on current version of the game.\nMost modules are likely incompatible with newer or older\nversions of the game than the mod supports.\n\nTo prevent game from being unstable or data from being corrupted,\nmost modules will be disabled except the following ones:\n* StaticTextPatch\n* FontPatch\n* FontAssetPatch\n\nIt is VERY RECOMMENDED to:\n* Wait for newer updates of this mod.\n* Keep backups of your save.\n* Keep away from this setting if you don't know what it means.\n\nThis is a HIGHLY EXPERIMENTAL / RISKY feature.\nKeep backups of your save before proceeding.\n\nSet this field to \"" + Application.version + "\" if you still wish to allow for all modules to load,\ndespite incompatibility issues may occur.\nEnable this at your own risk. You have been warned!";

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			if (!CheckSupportedGameVersion())
			{
				string text = (AllowAllModulesOnUnsupportedGameVersion ? _allowAllModulesOnUnsupportedGameVersionWarning : _unsupportedGameVersionWarning);
				Logger.LogWarning((object)text);
			}
			Initialize();
			if (string.IsNullOrWhiteSpace(LanguageFolder))
			{
				Logger.LogFatal((object)"Failed to auto-detect Language Folders.");
				return;
			}
			FileManager.Initialize(this);
			ConfigManager.Initialize(this);
			ModuleManager.Initialize(this);
			ResourceLoader.Initialize(this);
			CacheManager.Initialize(this);
			List<Type> moduleClasses = LoadAllModules();
			if (!SkipTranslationPatching)
			{
				ApplyHarmonyPatches(moduleClasses);
				ApplyScriptableObjectPatches(moduleClasses);
			}
		}

		public void Initialize()
		{
			_languageFolder = ((BaseUnityPlugin)this).Config.Bind<string>("General", "LanguageFolder", "", _languageFolderDescription);
			_maxScanDepth = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxScanDepth", 5, _maxScanDepthDescription);
			_skipTranslationPatching = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "SkipTranslationPatching", false, _skipTranslationPatchingDescription);
			if (!CheckSupportedGameVersion())
			{
				_allowAllModulesOnUnsupportedGameVersion = ((BaseUnityPlugin)this).Config.Bind<string>("Experimental", "AllowAllModulesOnUnsupportedGameVersion", "", _allowAllModulesOnUnsupportedGameVersionDescription);
			}
			if (string.IsNullOrWhiteSpace(LanguageFolder))
			{
				List<string> list = LanguageScanner.Scan(MaxScanDepth, Logger);
				Logger.LogInfo((object)"Loading the first valid Language Folder detected by default.");
				_languageFolder.Value = list?.FirstOrDefault();
			}
		}

		public bool CheckSupportedGameVersion()
		{
			return Application.version.Contains(SupportedGameVersion);
		}

		public List<Type> LoadAllModules()
		{
			ModuleManager.LoadAllModules();
			return (from m in ModuleManager.ModuleInfos
				where m.Status == ModuleStatus.OK
				select m.ModuleClass).ToList();
		}

		public void ApplyHarmonyPatches(List<Type> moduleClasses)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
			foreach (Type moduleClass in moduleClasses)
			{
				if (((MemberInfo)moduleClass).GetCustomAttribute<HarmonyPatch>() != null)
				{
					val.CreateClassProcessor(moduleClass).Patch();
					AccessTools.Field(moduleClass, "IsPatched")?.SetValue(null, true);
				}
			}
		}

		public void ApplyScriptableObjectPatches(List<Type> moduleClasses)
		{
			ScriptableObjectPatcher.Initialize(moduleClasses);
		}
	}
	public static class ResourceLoader
	{
		private static Plugin _plugin;

		private static ManualLogSource _logger;

		public static void Initialize(Plugin plugin)
		{
			if (plugin != null)
			{
				_plugin = plugin;
				string text = ((BaseUnityPlugin)_plugin).Info.Metadata.Name + "/ResourceLoader";
				_logger = Logger.CreateLogSource(text);
			}
		}

		public static bool TryGetOrCreateFont(string characters, FontProperties fontProperties, out Font font, bool isDiskCacheEnabled = false)
		{
			string hashString = HashCalculator.GetHashString(characters, fontProperties);
			font = CacheManager.GetFontFromMemoryCache(hashString);
			if ((Object)(object)font != (Object)null)
			{
				return true;
			}
			bool flag = isDiskCacheEnabled;
			if (isDiskCacheEnabled)
			{
				font = CacheManager.CreateFontFromDiskCache(hashString);
				flag = font == null;
			}
			if (font == null)
			{
				font = CreateFont(hashString, characters, fontProperties);
			}
			flag = flag && (Object)(object)font != (Object)null;
			if (font == null)
			{
				font = FontBuilder.CreateFontFromOSFont(fontProperties);
			}
			if (font == null)
			{
				return false;
			}
			if (flag)
			{
				CacheManager.WriteFontDiskCache(hashString, font);
			}
			CacheManager.AddFontToMemoryCache(hashString, font);
			return true;
		}

		public static Font CreateFont(string hash, string characters, FontProperties fontProperties)
		{
			try
			{
				if (FileManager.TryGetFontFilePath(fontProperties.FileName, out var filePath))
				{
					if (string.IsNullOrEmpty(fontProperties.FontName))
					{
						fontProperties.FontName = "SubstituteFont - " + Path.GetFileNameWithoutExtension(filePath);
					}
					return FontBuilder.CreateFont(filePath, characters, fontProperties, _logger);
				}
				return null;
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)("An error occurred while creating Font (hash: " + hash + ")."));
				}
				ManualLogSource logger2 = _logger;
				if (logger2 != null)
				{
					logger2.LogError((object)ex.Message);
				}
				return null;
			}
		}

		public static bool TryGetOrCreateFontAsset(string characters, FontAssetProperties fontAssetProperties, out TMP_FontAsset fontAsset, bool isDiskCacheEnabled = false)
		{
			string hashString = HashCalculator.GetHashString(characters, fontAssetProperties);
			fontAsset = CacheManager.GetFontAssetFromMemoryCache(hashString);
			if ((Object)(object)fontAsset != (Object)null)
			{
				return true;
			}
			bool flag = isDiskCacheEnabled;
			if (isDiskCacheEnabled)
			{
				fontAsset = CacheManager.CreateFontAssetFromDiskCache(hashString);
				flag = fontAsset == null;
			}
			if (fontAsset == null)
			{
				fontAsset = CreateFontAsset(hashString, characters, fontAssetProperties);
			}
			if (fontAsset == null)
			{
				return false;
			}
			if (flag)
			{
				CacheManager.WriteFontAssetDiskCache(hashString, fontAsset);
			}
			CacheManager.AddFontAssetToMemoryCache(hashString, fontAsset);
			return true;
		}

		public static TMP_FontAsset CreateFontAsset(string hash, string characters, FontAssetProperties fontAssetProperties)
		{
			try
			{
				if (FileManager.TryGetFontFilePath(fontAssetProperties.FileName, out var filePath))
				{
					if (string.IsNullOrEmpty(fontAssetProperties.FontName))
					{
						fontAssetProperties.FontName = "FallbackFontAsset - " + Path.GetFileNameWithoutExtension(filePath);
					}
					return FontAssetBuilder.CreateFontAsset(filePath, characters, fontAssetProperties, _logger);
				}
				return null;
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)("An error occurred while creating FontAsset (hash: " + hash + ")."));
				}
				_logger.LogError((object)ex.Message);
				return null;
			}
		}
	}
	public class ScriptableObjectPatcher
	{
		public static List<Type> ModuleClasses;

		public static void Initialize(List<Type> moduleClasses)
		{
			ModuleClasses = moduleClasses?.Where((Type m) => IsScriptableObjectPatchClass(m)).ToList();
			if (ModuleClasses != null && ModuleClasses.Count != 0)
			{
				SceneManager.sceneLoaded += OnSceneLoaded;
			}
		}

		public static void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
		{
			if (((Scene)(ref scene)).name == "Main-Menu")
			{
				ApplyScriptableObjectPatches();
				SceneManager.sceneLoaded -= OnSceneLoaded;
			}
		}

		public static void ApplyScriptableObjectPatches()
		{
			if (ModuleClasses == null || ModuleClasses.Count == 0)
			{
				return;
			}
			foreach (Type moduleClass in ModuleClasses)
			{
				AccessTools.Method(moduleClass, "PatchScriptableObjects", (Type[])null, (Type[])null)?.Invoke(null, null);
				AccessTools.Field(moduleClass, "IsPatched")?.SetValue(null, true);
			}
		}

		public static bool IsScriptableObjectPatchClass(Type moduleClass)
		{
			return moduleClass != null && typeof(IScriptableObjectPatch).IsAssignableFrom(moduleClass) && AccessTools.Method(moduleClass, "PatchScriptableObjects", (Type[])null, (Type[])null) != null;
		}
	}
	public class TemplateTranslations
	{
		private Dictionary<string, string> _textTranslations;

		private Dictionary<Regex, string> _templateMappings;

		private readonly Regex _templateGroupRegex;

		private readonly Regex _escapedTemplateGroupRegex;

		public TemplateTranslations(Dictionary<string, string> textTranslations)
		{
			_templateGroupRegex = CacheManager.GetOrCreateRegex("\\{(\\d+)\\}", RegexOptions.Compiled);
			_escapedTemplateGroupRegex = CacheManager.GetOrCreateRegex("\\\\\\{\\d+\\}", RegexOptions.Compiled);
			AddTemplateTranslations(textTranslations);
		}

		public void AddTemplateTranslations(Dictionary<string, string> textTranslations)
		{
			if (textTranslations == null)
			{
				return;
			}
			foreach (KeyValuePair<string, string> textTranslation in textTranslations)
			{
				AddTemplateTranslation(textTranslation.Key, textTranslation.Value);
			}
		}

		public void AddTemplateTranslation(string originalTemplateString, string translatedTemplateString)
		{
			if (_textTranslations == null)
			{
				_textTranslations = new Dictionary<string, string>();
			}
			if (_templateMappings == null)
			{
				_templateMappings = new Dictionary<Regex, string>();
			}
			if (originalTemplateString != null && translatedTemplateString != null)
			{
				_textTranslations[originalTemplateString] = translatedTemplateString;
				if (_templateGroupRegex.IsMatch(originalTemplateString))
				{
					Regex key = CreateTemplateRegex(originalTemplateString);
					_templateMappings[key] = translatedTemplateString;
				}
			}
		}

		public string GetTemplateTranslation(string originalText)
		{
			if (string.IsNullOrWhiteSpace(originalText))
			{
				return originalText;
			}
			if (_textTranslations != null && _textTranslations.TryGetValue(originalText, out var value) && value != null)
			{
				return value;
			}
			if (_templateMappings != null)
			{
				foreach (KeyValuePair<Regex, string> templateMapping in _templateMappings)
				{
					Regex key = templateMapping.Key;
					Match match = key.Match(originalText);
					if (match.Success)
					{
						string value2 = templateMapping.Value;
						return (value2 == null) ? originalText : BuildStringFromTemplate(value2, match);
					}
				}
			}
			return originalText;
		}

		public string BuildStringFromTemplate(string templateString, Match templateMatch)
		{
			return _templateGroupRegex.Replace(templateString, delegate(Match m)
			{
				int num = Convert.ToInt32(m.Groups[1].Value) + 1;
				return (num > templateMatch.Groups.Count) ? "" : templateMatch.Groups[num].Value;
			});
		}

		public Regex CreateTemplateRegex(string templateString)
		{
			string input = Regex.Escape(templateString);
			string text = _escapedTemplateGroupRegex.Replace(input, "(.*)");
			text = "^" + text + "$";
			return CacheManager.GetOrCreateRegex(text, RegexOptions.Singleline);
		}
	}
	public class ValueCollection<TKey, TValue>
	{
		private Dictionary<TKey, List<TValue>> _dictionary;

		public void Add(TKey key, TValue value)
		{
			if (_dictionary == null)
			{
				_dictionary = new Dictionary<TKey, List<TValue>>();
			}
			if (key == null || value == null)
			{
				return;
			}
			if (TryGetValues(key, out var values))
			{
				if (!values.Contains(value))
				{
					values.Add(value);
				}
			}
			else
			{
				_dictionary[key] = new List<TValue> { value };
			}
		}

		public bool TryGetValues(TKey key, out List<TValue> values)
		{
			if (key == null || _dictionary == null || !_dictionary.TryGetValue(key, out values) || values == null)
			{
				values = null;
				return false;
			}
			return true;
		}

		public void Clear()
		{
			_dictionary?.Clear();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "WKLocalizationLoader";

		public const string PLUGIN_NAME = "WKLocalizationLoader";

		public const string PLUGIN_VERSION = "0.7.0";
	}
}
namespace WKLocalizationLoader.Modules
{
	[HarmonyPatch]
	public class AchievementPatch : TextTranslator<AchievementPatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> AchievementTitles;

		[JsonProperty]
		public static Dictionary<string, string> AchievementDescriptions;

		[JsonIgnore]
		public static AchievementPatchSettings ModuleSettings;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(CL_AchievementManager), "Awake")]
		public static void Postfix_AchievementManager_Awake(CL_AchievementManager __instance)
		{
			if (!ModuleBase<AchievementPatch>.IsEnabled)
			{
				return;
			}
			foreach (GameAchievement achievement in __instance.achievements)
			{
				if (achievement.announce)
				{
					achievement.name = TextTranslator<AchievementPatch>.GetTextTranslation(AchievementTitles, achievement.name);
					achievement.announceText = TextTranslator<AchievementPatch>.GetTextTranslation(AchievementDescriptions, achievement.announceText);
				}
			}
		}
	}
	[ConfigSection("Modules.AchievementPatch", "This module replaces texts for achievements.")]
	public class AchievementPatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class AnnouncementSubtitlePatch : ModuleBase<AnnouncementSubtitlePatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> AnnouncementSubtitles;

		[JsonIgnore]
		public static AnnouncementSubtitlePatchSettings ModuleSettings;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Localization), "GetLine")]
		public static string Postfix_Localization_GetLine(string __result, string group, string key)
		{
			if (!ModuleBase<AnnouncementSubtitlePatch>.IsEnabled || group != "announcements" || AnnouncementSubtitles == null || !AnnouncementSubtitles.ContainsKey(key))
			{
				return __result;
			}
			return AnnouncementSubtitles[key] ?? __result;
		}
	}
	[ConfigSection("Modules.AnnouncementSubtitlePatch", "This module replaces announcer subtitle texts.")]
	public class AnnouncementSubtitlePatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPriority(0)]
	[HarmonyPatch]
	public class AnnouncementSubtitleTimingPatch : ModuleBase<AnnouncementSubtitleTimingPatch>
	{
		[JsonProperty]
		public static AnnouncementSubtitleTimingPatchSettings ModuleSettings;

		[JsonProperty]
		public static Dictionary<string, List<float>> AnnouncementSubtitleTimings;

		[JsonIgnore]
		public static readonly string[] LinebreakPattern = new string[1] { "<br>" };

		[JsonIgnore]
		public static readonly Regex DelayRegex = CacheManager.GetOrCreateRegex("<delay\\s*=\\s*([+-]?\\d*\\.?\\d+)>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Localization), "GetLine")]
		public static string Postfix_Localization_GetLine(string __result, string group, string key)
		{
			if (!ModuleBase<AnnouncementSubtitleTimingPatch>.IsEnabled || group != "announcements" || AnnouncementSubtitleTimings == null || !AnnouncementSubtitleTimings.ContainsKey(key) || (ModuleSettings.UseOriginalDelay && DelayRegex.IsMatch(__result)))
			{
				return __result;
			}
			return RebuildSubtitleTextWithTimings(__result, AnnouncementSubtitleTimings[key]);
		}

		public static string RebuildSubtitleTextWithTimings(string subtitleText, List<float> subtitleTimings)
		{
			if (subtitleTimings == null || subtitleTimings.Count == 0)
			{
				return subtitleText;
			}
			string[] array = subtitleText.Split(LinebreakPattern, StringSplitOptions.None);
			int num = Math.Min(array.Length, subtitleTimings.Count);
			for (int i = 0; i < num; i++)
			{
				string subtitleLine = array[i];
				subtitleLine = RemoveDelayTag(subtitleLine);
				float num2 = (float)subtitleLine.Length * ModuleSettings.CharacterInterval + ModuleSettings.BaseDuration;
				float num3 = subtitleTimings[i];
				if (i > 0)
				{
					num3 -= subtitleTimings[i - 1];
				}
				if (i == num - 1)
				{
					num3 += ModuleSettings.EndDelay;
				}
				float num4 = num3 - num2;
				string delayTag = ((num4 < 0f) ? $"<delay={num4:F3}>" : $"<delay={num4:F4}>");
				subtitleLine = InsertDelayTag(subtitleLine, delayTag);
				array[i] = subtitleLine;
			}
			return string.Join(LinebreakPattern[0], array);
		}

		public static string RemoveDelayTag(string subtitleLine)
		{
			Match match = DelayRegex.Match(subtitleLine);
			return match.Success ? subtitleLine.Remove(match.Index, match.Length) : subtitleLine;
		}

		public static string InsertDelayTag(string subtitleLine, string delayTag)
		{
			if (string.IsNullOrEmpty(delayTag))
			{
				return subtitleLine;
			}
			Match match = DelayRegex.Match(subtitleLine);
			return match.Success ? subtitleLine.Insert(match.Index, delayTag) : (subtitleLine + delayTag);
		}
	}
	[ConfigSection("Modules.AnnouncementSubtitleTimingPatch", "This module adjusts display timings of announcer subtitles.")]
	public class AnnouncementSubtitleTimingPatchSettings : ModuleSettingsBase
	{
		[ConfigEntry("BaseDuration", 0.8f, "Base duration (in seconds) for displaying a subtitle.")]
		public float BaseDuration;

		[ConfigEntry("CharacterInterval", 0.1f, "Additional duration (in seconds) added per character in the subtitle text.")]
		public float CharacterInterval;

		[ConfigEntry("EndDelay", 0.5f, "Extra duration (in seconds) added at the end of a subtitle.")]
		public float EndDelay;

		[ConfigEntry("UseOriginalDelay", false, "Set this field to \"true\" to retain original timings of\nsubtitles that contain \"<delay>\" tag(s).")]
		public bool UseOriginalDelay;
	}
	[HarmonyPatch]
	public class ClosedCaptionSubtitlePatch : ModuleBase<ClosedCaptionSubtitlePatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> ClosedCaptionSubtitles;

		[JsonIgnore]
		public static ClosedCaptionSubtitlePatchSettings ModuleSettings;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Localization), "GetLine")]
		public static string Postfix_Localization_GetLine(string __result, string group, string key)
		{
			if (!ModuleBase<ClosedCaptionSubtitlePatch>.IsEnabled || group != "sound" || ClosedCaptionSubtitles == null || !ClosedCaptionSubtitles.ContainsKey(key))
			{
				return __result;
			}
			return ClosedCaptionSubtitles[key] ?? __result;
		}
	}
	[ConfigSection("Modules.ClosedCaptionSubtitlePatch", "This module replaces closed caption subtitle texts.")]
	public class ClosedCaptionSubtitlePatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class CosmeticPatch : TextTranslator<CosmeticPatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> CosmeticDescriptions;

		[JsonProperty]
		public static Dictionary<string, string> PaletteNames;

		[JsonProperty]
		public static string PaletteTextTemplate;

		[JsonIgnore]
		public static CosmeticPatchSettings ModuleSettings;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_CosmeticInfoPanel), "Open")]
		public static void Postfix_CosmeticInfoPanel_Open(UI_CosmeticInfoPanel __instance)
		{
			if (ModuleBase<CosmeticPatch>.IsEnabled)
			{
				__instance.descText.text = TextTranslator<CosmeticPatch>.GetTextTranslation(CosmeticDescriptions, __instance.descText.text);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_CosmeticInfoPanel), "UpdateSprite")]
		public static void Postfix_CosmeticInfoPanel_UpdateSprite(UI_CosmeticInfoPanel __instance)
		{
			if (ModuleBase<CosmeticPatch>.IsEnabled)
			{
				TranslatePaletteText(__instance);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_CosmeticInfoPanel), "ChangePalette")]
		public static void Postfix_CosmeticInfoPanel_ChangePalette(UI_CosmeticInfoPanel __instance)
		{
			if (ModuleBase<CosmeticPatch>.IsEnabled)
			{
				TranslatePaletteText(__instance);
			}
		}

		public static void TranslatePaletteText(UI_CosmeticInfoPanel infoPanel)
		{
			Cosmetic_Base selectedCosmetic = infoPanel.selectedCosmetic;
			if (!(selectedCosmetic.cosmeticInfo.tag != "hand"))
			{
				Cosmetic_HandItem val = (Cosmetic_HandItem)(object)((selectedCosmetic is Cosmetic_HandItem) ? selectedCosmetic : null);
				List<ColorPalette> palettes = val.cosmeticData.palettes;
				if (palettes != null && palettes.Count != 0)
				{
					ColorPalette val2 = palettes[val.currentPaletteId];
					string textTranslation = TextTranslator<CosmeticPatch>.GetTextTranslation(PaletteNames, val2.title);
					int num = val.currentPaletteId + 1;
					int count = palettes.Count;
					string text = PaletteTextTemplate ?? "{paletteName} ({current}/{total})";
					infoPanel.debugText.text = text.Replace("{paletteName}", textTranslation).Replace("{current}", num.ToString()).Replace("{total}", count.ToString());
				}
			}
		}
	}
	[ConfigSection("Modules.CosmeticPatch", "This module replaces texts for cosmetics.")]
	public class CosmeticPatchSettings : ModuleSettingsBase
	{
	}
	[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
	public class CrossGameVersionCompatibleAttribute : Attribute
	{
	}
	[HarmonyPatch]
	public class DeathTextPatch : TextTranslator<DeathTextPatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> DeathMessages;

		[JsonProperty]
		public static Dictionary<string, string> DeathTips;

		[JsonIgnore]
		public static DeathTextPatchSettings ModuleSettings;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Localization), "GetLine")]
		public static string Postfix_Localization_GetLine(string __result, string group, string key)
		{
			if (!ModuleBase<DeathTextPatch>.IsEnabled || group != "deathmessages" || DeathMessages == null || !DeathMessages.ContainsKey(key))
			{
				return __result;
			}
			return DeathMessages[key] ?? __result;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_ScoreScreen), "SetTip")]
		public static void Postfix_ScoreScreen_SetTip(UI_ScoreScreen __instance)
		{
			if (ModuleBase<DeathTextPatch>.IsEnabled && __instance.useDeathText && __instance.tipText != null)
			{
				__instance.tipText.text = TextTranslator<DeathTextPatch>.GetTextTranslation(DeathTips, __instance.tipText.text);
			}
		}
	}
	[ConfigSection("Modules.DeathTextPatch", "This module replaces death messages and death tips.")]
	public class DeathTextPatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class DocumentPatch : TextTranslator<DocumentPatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> DocumentTexts;

		[JsonIgnore]
		public static DocumentPatchSettings ModuleSettings;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(App_DocumentReader), "Start")]
		public static void Prefix_DocumentReader_Start(App_DocumentReader __instance)
		{
			if (!ModuleBase<DocumentPatch>.IsEnabled)
			{
				return;
			}
			string text = "";
			OS_Window component = ((Component)__instance).GetComponent<OS_Window>();
			FileInfo fileInfo = component.file.fileInfo;
			if (fileInfo.textAssetData == null)
			{
				fileInfo.data = TextTranslator<DocumentPatch>.GetTextTranslation(DocumentTexts, fileInfo.data);
				return;
			}
			text = TextTranslator<DocumentPatch>.GetTextTranslation(DocumentTexts, fileInfo.textAssetData.text);
			if (!(text == fileInfo.textAssetData.text))
			{
				fileInfo.textAssetData = CacheManager.GetOrCreateTextAsset(text);
			}
		}
	}
	[ConfigSection("Modules.DocumentPatch", "This module replaces texts for QuietOS document files.")]
	public class DocumentPatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class FacilityUpgradePatch : TextTranslator<FacilityUpgradePatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> UpgradePageTitles;

		[JsonProperty]
		public static Dictionary<string, string> UpgradeTitles;

		[JsonProperty]
		public static Dictionary<string, string> UpgradeDescriptions;

		[JsonProperty]
		public static Dictionary<string, string> UpgradeUnlockDescriptions;

		[JsonProperty]
		public static string UpgradePageCounterTemplate;

		[JsonProperty]
		public static string UpgradeLockedTooltipTemplate;

		[JsonProperty]
		public static string UpgradeCantAffordTooltipTemplate;

		[JsonIgnore]
		public static FacilityUpgradePatchSettings ModuleSettings;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UI_FacilityMenu_Button), "Initialize")]
		public static void Prefix_FacilityMenuButton_Initialize(ref FacilityUpgrade upg)
		{
			if (ModuleBase<FacilityUpgradePatch>.IsEnabled)
			{
				PatchFacilityUpgrade(upg);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_FacilityMenu_Button), "Refresh")]
		public static void Postfix_FacilityMenuButton_Refresh(UI_FacilityMenu_Button __instance)
		{
			if (!ModuleBase<FacilityUpgradePatch>.IsEnabled)
			{
				return;
			}
			FacilityUpgrade upgrade = __instance.upgrade;
			Facility facility = __instance.facility;
			if (upgrade == null || facility == null)
			{
				return;
			}
			if (upgrade.IsLocked(facility.id))
			{
				if (UpgradeLockedTooltipTemplate != null)
				{
					__instance.tooltip.tip = UpgradeLockedTooltipTemplate.Replace("{unlockDescription}", upgrade.unlockDesc).Replace("{description}", upgrade.description);
				}
				return;
			}
			int value = StatManager.saveData.GetRoachBankByID("campaign").value;
			if (!upgrade.IsOwned(facility.id) && upgrade.cost >= value && UpgradeCantAffordTooltipTemplate != null)
			{
				__instance.tooltip.tip = UpgradeCantAffordTooltipTemplate.Replace("{description}", upgrade.description);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(App_Facility_Card), "Initialize")]
		public static void Prefix_FacilityApp_Initialize(ref FacilityUpgrade up)
		{
			if (ModuleBase<FacilityUpgradePatch>.IsEnabled)
			{
				PatchFacilityUpgrade(up);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(App_Facility_Card), "CheckLock")]
		public static void Postfix_FacilityAppCard_CheckLock(App_Facility_Card __instance)
		{
			if (ModuleBase<FacilityUpgradePatch>.IsEnabled)
			{
				FacilityUpgrade upgrade = __instance.upgrade;
				if (__instance.locked && UpgradeLockedTooltipTemplate != null)
				{
					__instance.tooltip.tip = UpgradeLockedTooltipTemplate.Replace("{unlockDescription}", upgrade.unlockDesc).Replace("{description}", upgrade.description);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(App_FacilitySlotHolder), "SetPage")]
		public static void Postfix_FacilitySlotHolder_SetPage(List<UpgradePage> pageList, ref int pageNumber, TMP_Text titleObject, string title)
		{
			if (ModuleBase<FacilityUpgradePatch>.IsEnabled)
			{
				string textTranslation = TextTranslator<FacilityUpgradePatch>.GetTextTranslation(UpgradePageTitles, title);
				int count = pageList.Count;
				if (count < 2)
				{
					titleObject.text = textTranslation;
					return;
				}
				int num = pageNumber + 1;
				string text = UpgradePageCounterTemplate ?? "{title} ({current}/{total})";
				titleObject.text = text.Replace("{title}", textTranslation).Replace("{current}", num.ToString()).Replace("{total}", count.ToString());
			}
		}

		public static void PatchFacilityUpgrade(FacilityUpgrade upgrade)
		{
			upgrade.cardName = TextTranslator<FacilityUpgradePatch>.GetTextTranslation(UpgradeTitles, upgrade.cardName);
			upgrade.description = TextTranslator<FacilityUpgradePatch>.GetTextTranslation(UpgradeDescriptions, upgrade.description);
			upgrade.unlockDesc = TextTranslator<FacilityUpgradePatch>.GetTextTranslation(UpgradeUnlockDescriptions, upgrade.unlockDesc);
		}
	}
	[ConfigSection("Modules.FacilityUpgradePatch", "This module replaces texts of facility upgrades.")]
	public class FacilityUpgradePatchSettings : ModuleSettingsBase
	{
	}
	[CrossGameVersionCompatible]
	public class FontAssetPatch : ModuleBase<FontAssetPatch>, IScriptableObjectPatch
	{
		[JsonProperty]
		public static FontAssetPatchSettings ModuleSettings;

		[JsonProperty]
		public static Dictionary<string, List<FontAssetProperties>> CustomFontAssets;

		[JsonProperty]
		public static string CharactersToRender;

		[JsonIgnore]
		public static ValueCollection<string, TMP_FontAsset> FallbackFontAssets = new ValueCollection<string, TMP_FontAsset>();

		[OnDeserialized]
		private void OnDeserialized(StreamingContext _)
		{
			if (!ModuleBase<FontAssetPatch>.IsEnabled)
			{
				return;
			}
			foreach (KeyValuePair<string, List<FontAssetProperties>> customFontAsset in CustomFontAssets)
			{
				string key = customFontAsset.Key;
				List<FontAssetProperties> value = customFontAsset.Value;
				foreach (FontAssetProperties item in value)
				{
					CreateAndRegisterFallbackFontAsset(key, item);
				}
			}
		}

		public static void PatchScriptableObjects()
		{
			if (!ModuleBase<FontAssetPatch>.IsEnabled)
			{
				return;
			}
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			if (array != null && array.Length != 0)
			{
				Array.ForEach(array, delegate(TMP_FontAsset i)
				{
					PatchFontAsset(i);
				});
			}
		}

		public static void PatchFontAsset(TMP_FontAsset fontAsset)
		{
			if (fontAsset != null)
			{
				AddFallbackFontAssets(fontAsset);
			}
		}

		public static void AddFallbackFontAssets(TMP_FontAsset fontAsset)
		{
			if (TryGetFallbackFontAssets((fontAsset != null) ? ((Object)fontAsset).name : null, out var fallbackFontAssets))
			{
				if (ModuleSettings.HighFallbackPriority)
				{
					fontAsset.fallbackFontAssetTable = fallbackFontAssets.Union(fontAsset.fallbackFontAssetTable).ToList();
				}
				else
				{
					fontAsset.fallbackFontAssetTable = fontAsset.fallbackFontAssetTable.Union(fallbackFontAssets).ToList();
				}
			}
		}

		public static void CreateAndRegisterFallbackFontAsset(string targetFontAssetName, FontAssetProperties fallbackFontAssetProperties)
		{
			if (ResourceLoader.TryGetOrCreateFontAsset(CharactersToRender, fallbackFontAssetProperties, out var fontAsset, ModuleSettings.SaveFontAssetCacheOnDisk))
			{
				RegisterFallbackFontAsset(targetFontAssetName, fontAsset);
			}
		}

		public static void RegisterFallbackFontAsset(string targetFontAssetName, TMP_FontAsset fallbackFontAsset)
		{
			FallbackFontAssets?.Add(targetFontAssetName, fallbackFontAsset);
		}

		public static bool TryGetFallbackFontAssets(string targetFontAssetName, out List<TMP_FontAsset> fallbackFontAssets)
		{
			if (FallbackFontAssets != null && FallbackFontAssets.TryGetValues(targetFontAssetName, out fallbackFontAssets))
			{
				return true;
			}
			fallbackFontAssets = null;
			return false;
		}
	}
	[ConfigSection("Modules.FontAssetPatch", "This module adds fallback font assets.")]
	public class FontAssetPatchSettings : ModuleSettingsBase
	{
		[ConfigEntry("HighFallbackPriority", true, "Set this field to \"false\" to\nlower fallback priority of custom fallback font assets.")]
		public bool HighFallbackPriority;

		[ConfigEntry("SaveFontAssetCacheOnDisk", false, "Set this field to \"true\" to cache generated TMP_FontAsset\non disk to reduce load times on subsequent game launches.\nWarning: Cache size may grow significantly --\nA 4096×4096 atlas alone is about 16MB.\nEnable this only if you have spare disk space.\nCache files are stored in the same directory as plugin .dll.")]
		public bool SaveFontAssetCacheOnDisk;
	}
	[CrossGameVersionCompatible]
	[HarmonyPriority(500)]
	[HarmonyPatch]
	public class FontPatch : ModuleBase<FontPatch>
	{
		[JsonProperty]
		public static FontPatchSettings ModuleSettings;

		[JsonProperty]
		public static Dictionary<string, FontProperties> CustomFonts;

		[JsonProperty]
		public static string CharactersToRender;

		[JsonIgnore]
		public static Dictionary<string, Font> SubstituteFonts = new Dictionary<string, Font>();

		[OnDeserialized]
		private void OnDeserialized(StreamingContext _)
		{
			if (!ModuleBase<FontPatch>.IsEnabled)
			{
				return;
			}
			foreach (KeyValuePair<string, FontProperties> customFont in CustomFonts)
			{
				string key = customFont.Key;
				FontProperties value = customFont.Value;
				CreateAndRegisterSubstituteFont(key, value);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Text), "OnEnable")]
		public static void Postfix_Text_OnEnable(Text __instance)
		{
			if (ModuleBase<FontPatch>.IsEnabled)
			{
				ReplaceFont(__instance);
			}
		}

		public static void ReplaceFont(Text text)
		{
			Font font = text.font;
			string targetFontName = ((font != null) ? ((Object)font).name : null);
			if (TryGetSubstituteFont(targetFontName, out var substituteFont))
			{
				text.font = substituteFont;
			}
		}

		public static void CreateAndRegisterSubstituteFont(string targetFontName, FontProperties substituteFontProperties)
		{
			if (ResourceLoader.TryGetOrCreateFont(CharactersToRender, substituteFontProperties, out var font, ModuleSettings.SaveFontCacheOnDisk))
			{
				RegisterSubstituteFont(targetFontName, font);
			}
		}

		public static void RegisterSubstituteFont(string targetFontName, Font substituteFont)
		{
			if (SubstituteFonts == null)
			{
				SubstituteFonts = new Dictionary<string, Font>();
			}
			if (targetFontName != null && substituteFont != null)
			{
				SubstituteFonts[targetFontName] = substituteFont;
			}
		}

		public static bool TryGetSubstituteFont(string targetFontName, out Font substituteFont)
		{
			if (targetFontName == null || SubstituteFonts == null || !SubstituteFonts.TryGetValue(targetFontName, out substituteFont) || substituteFont == null)
			{
				substituteFont = null;
				return false;
			}
			return true;
		}
	}
	[ConfigSection("Modules.FontPatch", "This module replaces font of Text class instances.")]
	public class FontPatchSettings : ModuleSettingsBase
	{
		[ConfigEntry("SaveFontCacheOnDisk", false, "Set this field to \"true\" to cache generated Font\non disk to reduce load times on subsequent game launches.\nWarning: Cache size may grow significantly --\nA 4096×4096 atlas alone is about 16MB.\nEnable this only if you have spare disk space.\nCache files are stored in the same directory as plugin .dll.")]
		public bool SaveFontCacheOnDisk;
	}
	[HarmonyPatch]
	public class GamemodePatch : TextTranslator<GamemodePatch>, IScriptableObjectPatch
	{
		[JsonProperty]
		public static Dictionary<string, string> CapsuleNames;

		[JsonProperty]
		public static Dictionary<string, string> GamemodeUnlockHints;

		[JsonProperty]
		public static Dictionary<string, string> GamemodeDescriptions;

		[JsonProperty]
		public static Dictionary<string, string> GamemodeNewGameTexts;

		[JsonProperty]
		public static Dictionary<string, string> GamemodeIntroTexts;

		[JsonProperty]
		public static Dictionary<string, string> GamemodeMedalTitles;

		[JsonProperty]
		public static Dictionary<string, string> GamemodeTextPrefixes;

		[JsonProperty]
		public static Dictionary<string, string> ModifierTitles;

		[JsonProperty]
		public static Dictionary<string, string> ModifierDescriptions;

		[JsonProperty]
		public static Dictionary<string, string> ModifierAppends;

		[JsonProperty]
		public static bool KeepWhiteSpaceInCapsuleName;

		[JsonProperty]
		public static string GamemodeTextTemplate;

		[JsonProperty]
		public static string ModifierConflictedDescription;

		[JsonProperty]
		public static string ModifierLockedDescriptionTemplate;

		[JsonProperty]
		public static string ModifierUnlockProgressTemplate;

		[JsonIgnore]
		public static Regex WhiteSpaceRegex = CacheManager.GetOrCreateRegex("\\s+|<br\\s*>", RegexOptions.IgnoreCase);

		[JsonIgnore]
		public static GamemodePatchSettings ModuleSettings;

		public static void PatchScriptableObjects()
		{
			if (ModuleBase<GamemodePatch>.IsEnabled)
			{
				CL_AssetManager.GetFullCombinedAssetDatabase()?.gamemodeAssets?.ForEach(delegate(M_Gamemode i)
				{
					PatchGamemode(i);
				});
			}
		}

		public static void PatchGamemode(M_Gamemode gamemode)
		{
			if (gamemode != null)
			{
				gamemode.unlockHint = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeUnlockHints, gamemode.unlockHint);
				gamemode.modeDescription = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeDescriptions, gamemode.modeDescription);
				gamemode.newGameText = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeNewGameTexts, gamemode.newGameText);
				gamemode.introText = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeIntroTexts, gamemode.introText);
				gamemode.gamemodeSettings?.ForEach(delegate(GamemodeSetting i)
				{
					PatchGamemodeSetting(i);
				});
			}
		}

		public static void PatchGamemodeSetting(GamemodeSetting gamemodeSetting)
		{
			if (gamemodeSetting != null)
			{
				gamemodeSetting.title = TextTranslator<GamemodePatch>.GetTextTranslation(ModifierTitles, gamemodeSetting.title);
				gamemodeSetting.description = TextTranslator<GamemodePatch>.GetTextTranslation(ModifierDescriptions, gamemodeSetting.description);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_Gamemode_Button), "Initialize")]
		public static void Postfix_GamemodeButton_Initialize(UI_Gamemode_Button __instance)
		{
			if (ModuleBase<GamemodePatch>.IsEnabled && __instance.gamemode != null && CapsuleNames != null && CapsuleNames.TryGetValue(__instance.gamemode.gamemodeName, out var value) && value != null)
			{
				__instance.title.text = value;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_GamemodeText), "Refresh")]
		public static void Postfix_GamemodeText_Refresh(UI_GamemodeText __instance)
		{
			if (ModuleBase<GamemodePatch>.IsEnabled && CL_GameManager.gamemode != null)
			{
				__instance.text.text = GetTranslatedGamemodeText(CL_GameManager.gamemode, __instance.text.text);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_LeaderboardEntryDetailWindow), "ShowDetails")]
		public static void Postfix_LeaderboardEntryWindow_ShowDetails(UI_LeaderboardEntryDetailWindow __instance)
		{
			if (ModuleBase<GamemodePatch>.IsEnabled)
			{
				__instance.gamemodeText.text = GetTranslatedGamemodeText(CL_GameManager.GetBaseGamemode(), __instance.gamemodeText.text);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_LeaderboardEntryDetailWindow), "ShowNoEntryDetails")]
		public static void Postfix_LeaderboardEntryWindow_ShowNoEntryDetails(UI_LeaderboardEntryDetailWindow __instance)
		{
			if (ModuleBase<GamemodePatch>.IsEnabled)
			{
				__instance.gamemodeText.text = GetTranslatedGamemodeText(CL_GameManager.GetBaseGamemode(), __instance.gamemodeText.text);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_ScoreScreen), "SetMedalInfo")]
		public static void Postfix_ScoreScreen_SetMedalInfo(UI_ScoreScreen __instance)
		{
			if (ModuleBase<GamemodePatch>.IsEnabled)
			{
				__instance.scoreRankText.text = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeMedalTitles, __instance.scoreRankText.text);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_GamemodeSetting), "UpdateColor")]
		public static void Postfix_GamemodeSettingUI_UpdateColor(UI_GamemodeSetting __instance)
		{
			if (!ModuleBase<GamemodePatch>.IsEnabled)
			{
				return;
			}
			string text = __instance.descriptionText.text;
			if (text == "LOCKED")
			{
				__instance.descriptionText.text = ModifierConflictedDescription ?? "LOCKED";
				return;
			}
			ProgressionUnlock unlock = __instance.gamemodeSetting.unlock;
			if (!unlock.CheckUnlock())
			{
				string newValue = (unlock.showProgression ? GetTranslatedProgress(unlock) : "");
				string text2 = ModifierLockedDescriptionTemplate ?? "{unlockHint}{progress}";
				__instance.descriptionText.text = text2.Replace("{unlockHint}", unlock.unlockHint).Replace("{progress}", newValue);
			}
		}

		public static string GetTranslatedGamemodeText(M_Gamemode gamemode, string gamemodeText)
		{
			string text = gamemode.gamemodeName;
			string[] array = gamemodeText.Split(new string[1] { text }, 2, StringSplitOptions.None);
			if (array.Length != 2)
			{
				return gamemodeText;
			}
			string originalText = array[0];
			string text2 = array[1];
			originalText = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeTextPrefixes, originalText);
			if (CapsuleNames != null && CapsuleNames.TryGetValue(text, out var value) && value != null)
			{
				text = WhiteSpaceRegex.Replace(value, KeepWhiteSpaceInCapsuleName ? " " : "");
			}
			if (ModifierAppends != null)
			{
				foreach (KeyValuePair<string, string> modifierAppend in ModifierAppends)
				{
					if (modifierAppend.Value != null)
					{
						text2 = text2.Replace(modifierAppend.Key, modifierAppend.Value);
					}
				}
			}
			string text3 = GamemodeTextTemplate ?? "{textPrefix}{capsuleName}{modifierAppends}";
			return text3.Replace("{textPrefix}", originalText).Replace("{capsuleName}", text).Replace("{modifierAppends}", text2);
		}

		public static string GetTranslatedProgress(ProgressionUnlock unlock)
		{
			string text = unlock.GetProgressString();
			if (ModifierUnlockProgressTemplate != null)
			{
				string[] array = text.Split(new char[1] { '/' }, 2);
				if (array.Length == 2)
				{
					string newValue = array[0];
					string newValue2 = array[1];
					text = ModifierUnlockProgressTemplate.Replace("{current}", newValue).Replace("{required}", newValue2);
				}
			}
			return text;
		}
	}
	[ConfigSection("Modules.GamemodePatch", "This module replaces texts for gamemodes and gamemode settings.")]
	public class GamemodePatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class GameplayTextPatch : TextTranslator<GameplayTextPatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> RoachCounterTemplates;

		[JsonProperty]
		public static Dictionary<string, string> BadgeTitles;

		[JsonProperty]
		public static Dictionary<string, string> InteractionHints;

		[JsonProperty]
		public static string ScoreTrackerTemplate;

		[JsonProperty]
		public static string HighScoreTrackerTemplate;

		[JsonProperty]
		public static string DistanceTrackerTemplate;

		[JsonProperty]
		public static string SpeedTrackerTemplate;

		[JsonProperty]
		public static string ForlornGatewayDoorPoweredText;

		[JsonProperty]
		public static string VendorUnavailableText;

		[JsonProperty]
		public static string VendorCostTemplate;

		[JsonProperty]
		public static string VendorPurchasedText;

		[JsonIgnore]
		public static GameplayTextPatchSettings ModuleSettings;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(CL_GameManager), "Update")]
		public static void Postfix_GameManager_Update(CL_GameManager __instance)
		{
			if (!ModuleBase<GameplayTextPatch>.IsEnabled || CL_GameManager.runHasEnded)
			{
				return;
			}
			CL_UIManager uiMan = __instance.uiMan;
			if (uiMan != null && uiMan.scoreTracker != null)
			{
				string text = uiMan.scoreTracker.text;
				if (text.StartsWith("Score: ") && ScoreTrackerTemplate != null)
				{
					string newValue = text.Substring(7);
					uiMan.scoreTracker.text = ScoreTrackerTemplate.Replace("{score}", newValue);
				}
				string text2 = uiMan.highScoreTracker.text;
				if (text2.StartsWith("High Score: ") && HighScoreTrackerTemplate != null)
				{
					string newValue2 = text2.Substring(12);
					uiMan.highScoreTracker.text = HighScoreTrackerTemplate.Replace("{highScore}", newValue2);
				}
				string text3 = uiMan.ascentTracker.text;
				if (text3.StartsWith("Climb Distance: ") && DistanceTrackerTemplate != null)
				{
					string newValue3 = text3.Substring(16);
					uiMan.ascentTracker.text = DistanceTrackerTemplate.Replace("{distance}", newValue3);
				}
				string text4 = uiMan.ascentRateTracker.text;
				if (text4.StartsWith("Climb Speed: ") && SpeedTrackerTemplate != null)
				{
					string newValue4 = text4.Substring(13);
					uiMan.ascentRateTracker.text = SpeedTrackerTemplate.Replace("{speed}", newValue4);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UT_CheckFlag), "CheckFlag")]
		public static void Postfix_CheckFlag_CheckFlag(UT_CheckFlag __instance)
		{
			if (!ModuleBase<GameplayTextPatch>.IsEnabled || ForlornGatewayDoorPoweredText == null)
			{
				return;
			}
			string flagName = __instance.flagName;
			if (flagName != "habentrywaypowered" && flagName != "habentryunlocked")
			{
				return;
			}
			SessionFlag gameFlag = CL_GameManager.GetGameFlag(flagName);
			if (gameFlag == null || !gameFlag.state)
			{
				return;
			}
			TMP_Text[] componentsInChildren = ((Component)((Component)__instance).transform.parent).GetComponentsInChildren<TMP_Text>();
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			foreach (TMP_Text val in componentsInChildren)
			{
				if (val.text == "POWERED")
				{
					val.text = ForlornGatewayDoorPoweredText;
					break;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UT_RoachTextCounter), "UpdateText")]
		public static void Prefix_RoachTextCounter_UpdateText(UT_RoachTextCounter __instance)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled)
			{
				__instance.textFormat = TextTranslator<GameplayTextPatch>.GetTextTranslation(RoachCounterTemplates, __instance.textFormat);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UI_Badge), "ShowBadge")]
		public static void Prefix_Badge_ShowBadge(Sprite sprite, ref string title)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled)
			{
				title = TextTranslator<GameplayTextPatch>.GetTextTranslation(BadgeTitles, title);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UT_HoverInfo), "OnHover")]
		public static bool Prefix_HoverInfo_OnHover(UT_HoverInfo __instance)
		{
			if (!ModuleBase<GameplayTextPatch>.IsEnabled)
			{
				return true;
			}
			string textTranslation = TextTranslator<GameplayTextPatch>.GetTextTranslation(InteractionHints, __instance.infoText);
			CL_UIManager.instance.SetDescription(textTranslation);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ENV_Vendor_Disk), "CheckBlock")]
		public static void Prefix_DiskVendor_CheckBlock(ENV_Vendor_Disk __instance)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled && VendorUnavailableText != null && !__instance.isBlocked && !__instance.hasBeenBought && CL_GameManager.HasActiveFlag("blockshops", false))
			{
				__instance.isBlocked = true;
				__instance.purchaseButton.SetInteractable(false);
				__instance.costText.text = VendorUnavailableText;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ENV_Vendor_Disk), "CheckRoaches")]
		public static void Prefix_DiskVendor_CheckRoaches(ENV_Vendor_Disk __instance)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled && VendorPurchasedText != null && __instance.allowPurchases && !__instance.isBlocked && !__instance.hasBeenBought && !(__instance.id == ""))
			{
				SessionFlag gameFlag = CL_GameManager.GetGameFlag("boughtdisk-" + __instance.id + "-station");
				if (gameFlag != null && gameFlag.state)
				{
					__instance.hasBeenBought = true;
					__instance.costText.text = VendorPurchasedText;
					((Component)__instance.purchaseSprite).gameObject.SetActive(false);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ENV_Vendor_Disk), "CheckRoaches")]
		public static void Postfix_DiskVendor_CheckRoaches(ENV_Vendor_Disk __instance)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled && VendorCostTemplate != null && __instance.allowPurchases && !__instance.isBlocked && !__instance.hasBeenBought)
			{
				int cost = __instance.cost;
				int roaches = CL_GameManager.GetRoaches(false);
				__instance.costText.text = VendorCostTemplate.Replace("{cost}", cost.ToString()).Replace("{balance}", roaches.ToString());
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ENV_Vendor_Disk), "Purchase")]
		public static void Postfix_DiskVendor_Purchase(ENV_Vendor_Disk __instance)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled && !__instance.isBlocked && !((Component)__instance.purchaseSprite).gameObject.activeInHierarchy && VendorPurchasedText != null)
			{
				__instance.costText.text = VendorPurchasedText;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ENV_Vendor_Event), "CheckRoaches")]
		public static void Prefix_EventVendor_CheckRoaches(ENV_Vendor_Event __instance)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled && VendorPurchasedText != null && __instance.allowPurchases && !__instance.hasBeenBought && !(__instance.id == ""))
			{
				SessionFlag gameFlag = CL_GameManager.GetGameFlag("boughtdisk-" + __instance.id + "-station");
				if (gameFlag != null && gameFlag.state)
				{
					__instance.hasBeenBought = true;
					__instance.costText.text = VendorPurchasedText;
					((Component)__instance.purchaseSprite).gameObject.SetActive(false);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ENV_Vendor_Event), "CheckRoaches")]
		public static void Postfix_EventVendor_CheckRoaches(ENV_Vendor_Event __instance)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled && VendorCostTemplate != null && __instance.allowPurchases && !__instance.hasBeenBought)
			{
				int cost = __instance.cost;
				int roaches = CL_GameManager.GetRoaches(false);
				__instance.costText.text = VendorCostTemplate.Replace("{cost}", cost.ToString()).Replace("{balance}", roaches.ToString());
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ENV_Vendor_Event), "Purchase")]
		public static void Postfix_EventVendor_Purchase(ENV_Vendor_Event __instance)
		{
			if (ModuleBase<GameplayTextPatch>.IsEnabled && !((Component)__instance.purchaseSprite).gameObject.activeInHierarchy && VendorPurchasedText != null)
			{
				__instance.costText.text = VendorPurchasedText;
			}
		}
	}
	[ConfigSection("Modules.GameplayTextPatch", "This module replaces texts of\nroach counters, vendors and stat trackers.")]
	public class GameplayTextPatchSettings : ModuleSettingsBase
	{
	}
	public interface IScriptableObjectPatch
	{
	}
	[HarmonyPatch]
	public class ItemDescriptionPatch : ModuleBase<ItemDescriptionPatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> ItemDescriptions;

		[JsonIgnore]
		public static ItemDescriptionPatchSettings ModuleSettings;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Localization), "GetLine")]
		public static string Postfix_Localization_GetLine(string __result, string group, string key)
		{
			if (!ModuleBase<ItemDescriptionPatch>.IsEnabled || group != "items" || ItemDescriptions == null || !ItemDescriptions.ContainsKey(key))
			{
				return __result;
			}
			return ItemDescriptions[key] ?? __result;
		}
	}
	[ConfigSection("Modules.ItemDescriptionPatch", "This module replaces item description texts.")]
	public class ItemDescriptionPatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class LocationNamePatch : TextTranslator<LocationNamePatch>, IScriptableObjectPatch
	{
		[JsonProperty]
		public static Dictionary<string, string> RegionIntroTexts;

		[JsonProperty]
		public static Dictionary<string, string> SubregionIntroTexts;

		[JsonProperty]
		public static Dictionary<string, string> LevelIntroTexts;

		[JsonProperty]
		public static Dictionary<string, string> LevelSaveNames;

		[JsonProperty]
		public static string ContinueTextTemplate;

		[JsonIgnore]
		public static LocationNamePatchSettings ModuleSettings;

		public static void PatchScriptableObjects()
		{
			if (ModuleBase<LocationNamePatch>.IsEnabled)
			{
				WKAssetDatabase fullCombinedAssetDatabase = CL_AssetManager.GetFullCombinedAssetDatabase();
				fullCombinedAssetDatabase?.regionAssets?.ForEach(delegate(M_Region i)
				{
					PatchRegion(i);
				});
				fullCombinedAssetDatabase?.subRegionAssets?.ForEach(delegate(M_Subregion i)
				{
					PatchSubregion(i);
				});
			}
		}

		public static void PatchRegion(M_Region region)
		{
			if (region != null)
			{
				region.introText = TextTranslator<LocationNamePatch>.GetTextTranslation(RegionIntroTexts, region.introText);
			}
		}

		public static void PatchSubregion(M_Subregion subregion)
		{
			if (subregion != null)
			{
				subregion.introText = TextTranslator<LocationNamePatch>.GetTextTranslation(SubregionIntroTexts, subregion.introText);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UT_ZoneTitler), "Start")]
		public static void Postfix_ZoneTitler_Start(UT_ZoneTitler __instance)
		{
			if (ModuleBase<LocationNamePatch>.IsEnabled)
			{
				__instance.region = TextTranslator<LocationNamePatch>.GetTextTranslation(RegionIntroTexts, __instance.region);
				__instance.subRegion = TextTranslator<LocationNamePatch>.GetTextTranslation(SubregionIntroTexts, __instance.subRegion);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(M_Level), "Awake")]
		public static void Postfix_Level_Awake(M_Level __instance)
		{
			if (ModuleBase<LocationNamePatch>.IsEnabled)
			{
				__instance.introText = TextTranslator<LocationNamePatch>.GetTextTranslation(LevelIntroTexts, __instance.introText);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_GamemodeScreen), "RefreshCurrentGamemode")]
		public static void Postfix_GamemodeScreen_RefreshCurrentGamemode(UI_GamemodeScreen __instance)
		{
			if (ModuleBase<LocationNamePatch>.IsEnabled)
			{
				string text = __instance.currentPanel.continueButtonText.text;
				if (CL_SaveManager.SessionFileExists(__instance.baseGamemode.gamemodeName, CL_GameManager.IsHardmode()) && !CL_GameManager.gamemode.IsCompetitive() && !CL_GameManager.GetBaseGamemode().IsCompetitive() && text.StartsWith("Continue: "))
				{
					string originalText = text.Substring(10);
					originalText = TextTranslator<LocationNamePatch>.GetTextTranslation(LevelSaveNames, originalText);
					string text2 = ContinueTextTemplate ?? "Continue: {saveName}";
					__instance.currentPanel.continueButtonText.text = text2.Replace("{saveName}", originalText);
				}
			}
		}
	}
	[ConfigSection("Modules.LocationNamePatch", "This module replaces location intro texts and level save names.")]
	public class LocationNamePatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class MainMenuPatch : TextTranslator<MainMenuPatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> PageTitles;

		[JsonProperty]
		public static Dictionary<string, string> ShimmerColorTitles;

		[JsonProperty]
		public static string LoadingProgressTemplate;

		[JsonProperty]
		public static string PageCounterTemplate;

		[JsonProperty]
		public static string ShimmerColorCounterTemplate;

		[JsonIgnore]
		public static MainMenuPatchSettings ModuleSettings;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UT_Intro), "EndIntro")]
		public static bool Prefix_Intro_EndIntro(UT_Intro __instance)
		{
			if (!ModuleBase<MainMenuPatch>.IsEnabled || LoadingProgressTemplate == null)
			{
				return true;
			}
			__instance.video.Stop();
			__instance.hasSkipped = true;
			TMP_Text loadPercentageText = __instance.loadPercentageText;
			((Component)loadPercentageText.transform.parent).gameObject.SetActive(true);
			if (ModuleBase<FontAssetPatch>.IsEnabled)
			{
				FontAssetPatch.AddFallbackFontAssets(loadPercentageText.font);
			}
			loadPercentageText.text = LoadingProgressTemplate.Replace("{progress}", "0");
			((MonoBehaviour)__instance).StartCoroutine(CustomLoadingIntro(__instance));
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_PageHolder), "UpdatePage")]
		public static void Postfix_PageHolder_UpdatePage(UI_PageHolder __instance)
		{
			if (!ModuleBase<MainMenuPatch>.IsEnabled)
			{
				return;
			}
			TMP_Text pageTitle = __instance.pageTitle;
			if (pageTitle != null)
			{
				List<UI_Page> pages = __instance.pages;
				int count = pages.Count;
				if (count != 0)
				{
					string textTranslation = TextTranslator<MainMenuPatch>.GetTextTranslation(PageTitles, pages[__instance.currentPage].title);
					int num = __instance.currentPage + 1;
					string text = PageCounterTemplate ?? "{title} ({current}/{total})";
					pageTitle.text = text.Replace("{title}", textTranslation).Replace("{current}", num.ToString()).Replace("{total}", count.ToString());
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ColorSetSettingBinder), "UpdateText")]
		public static void Postfix_ColorSetSettingBinder_UpdateText(ColorSetSettingBinder __instance)
		{
			if (ModuleBase<MainMenuPatch>.IsEnabled && __instance.showColorSetInfo && __instance.text != null)
			{
				List<ColorSetting> colorSettings = SettingsManager.GetColorSettingsSet().colorSettings;
				string currentShimmerColorID = SettingsManager.GetSetting(__instance.settingName);
				int num = colorSettings.FindIndex((ColorSetting i) => ((Object)i).name == currentShimmerColorID);
				if (num >= 0)
				{
					string textTranslation = TextTranslator<MainMenuPatch>.GetTextTranslation(ShimmerColorTitles, colorSettings[num].title);
					int num2 = num + 1;
					int count = colorSettings.Count;
					string text = ShimmerColorCounterTemplate ?? "Shimmer Color\n<color=grey>{title} ({current}/{total})";
					__instance.text.text = text.Replace("{title}", textTranslation).Replace("{current}", num2.ToString()).Replace("{total}", count.ToString());
				}
			}
		}

		public static IEnumerator CustomLoadingIntro(UT_Intro intro)
		{
			yield return (object)new WaitForSeconds(0.1f);
			UnityEvent onEnd = intro.onEnd;
			if (onEnd != null)
			{
				onEnd.Invoke();
			}
			yield return (object)new WaitForSeconds(0.1f);
			while (true)
			{
				UpdateLoadingProgressText();
				yield return null;
				yield return null;
				if (intro.loadMenuOperation != null && !((double)intro.loadMenuOperation.progress < 0.89))
				{
					intro.loadMenuOperation.allowSceneActivation = true;
					if (intro.loadMenuOperation.isDone)
					{
						break;
					}
				}
			}
			UpdateLoadingProgressText();
			intro.loadMenuOperation.allowSceneActivation = true;
			void UpdateLoadingProgressText()
			{
				if (intro.loadMenuOperation != null)
				{
					string newValue = Mathf.RoundToInt(intro.loadMenuOperation.progress * 100f).ToString();
					intro.loadPercentageText.text = LoadingProgressTemplate.Replace("{progress}", newValue);
				}
			}
		}
	}
	[ConfigSection("Modules.MainMenuPatch", "This module replaces texts of loading intro and menu pages.")]
	public class MainMenuPatchSettings : ModuleSettingsBase
	{
	}
	public abstract class ModuleBase<TModule>
	{
		[JsonIgnore]
		public static bool IsEnabled;

		[JsonIgnore]
		public static bool IsPatched;

		public ModuleBase()
		{
			FieldInfo fieldInfo = AccessTools.Field(GetType(), "ModuleSettings");
			if ((object)fieldInfo != null)
			{
				ConfigSectionAttribute customAttribute = fieldInfo.FieldType.GetCustomAttribute<ConfigSectionAttribute>();
				if (customAttribute != null)
				{
					customAttribute.Deconstruct(out var section, out var moduleDescription);
					string section2 = section;
					string moduleDescription2 = moduleDescription;
					IsEnabled = ConfigManager.IsModuleEnabled(section2, moduleDescription2);
				}
				if (IsEnabled && fieldInfo.GetValue(this) == null)
				{
					object value = Activator.CreateInstance(fieldInfo.FieldType);
					fieldInfo.SetValue(this, value);
				}
			}
		}
	}
	public class ModuleSettingsBase
	{
		public ModuleSettingsBase()
		{
			Type type = GetType();
			ConfigSectionAttribute customAttribute = type.GetCustomAttribute<ConfigSectionAttribute>();
			FieldInfo[] fields = type.GetFields();
			foreach (FieldInfo fieldInfo in fields)
			{
				ConfigEntryAttribute customAttribute2 = fieldInfo.GetCustomAttribute<ConfigEntryAttribute>();
				PopulateModuleSettingsField(this, fieldInfo, customAttribute, customAttribute2);
			}
		}

		private void PopulateModuleSettingsField(ModuleSettingsBase moduleSettings, FieldInfo field, ConfigSectionAttribute configSectionAttribute, ConfigEntryAttribute configEntryAttribute)
		{
			if (configEntryAttribute != null)
			{
				field.SetValue(moduleSettings, configEntryAttribute.DefaultValue);
				if (configSectionAttribute != null)
				{
					configSectionAttribute.Deconstruct(out var section, out var moduleDescription);
					string section2 = section;
					string moduleDescription2 = moduleDescription;
					configEntryAttribute.Deconstruct(out moduleDescription, out var defaultValue, out section);
					string key = moduleDescription;
					object defaultValue2 = defaultValue;
					string entryDescription = section;
					object configEntryValue = ConfigManager.GetConfigEntryValue(section2, moduleDescription2, key, defaultValue2, entryDescription);
					field.SetValue(moduleSettings, configEntryValue);
				}
			}
		}
	}
	[HarmonyPatch]
	public class MotherSubtitlePatch : ModuleBase<MotherSubtitlePatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> MotherSubtitles;

		[JsonProperty]
		public static string RandomCharacters;

		[JsonProperty]
		public static string NonRandomCharacters;

		[JsonIgnore]
		public static MotherSubtitlePatchSettings ModuleSettings;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Localization), "GetLine")]
		public static string Postfix_GetLine(string __result, string group, string key)
		{
			if (!ModuleBase<MotherSubtitlePatch>.IsEnabled || group != "mother" || MotherSubtitles == null || !MotherSubtitles.ContainsKey(key))
			{
				return __result;
			}
			return MotherSubtitles[key] ?? __result;
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(HUD_CustomElement_PsychicCommunication), "PlaySubtitle")]
		public static IEnumerable<CodeInstruction> Transpiler_PlaySubtitle(IEnumerable<CodeInstruction> codeInstructions)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			if (!ModuleBase<MotherSubtitlePatch>.IsEnabled)
			{
				return codeInstructions;
			}
			CodeMatcher val = new CodeMatcher(codeInstructions, (ILGenerator)null);
			val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction i) => i.opcode == OpCodes.Stfld && i.operand is FieldInfo fieldInfo2 && fieldInfo2.Name == "rand"), (string)null)
			});
			if (!val.IsValid)
			{
				return codeInstructions;
			}
			FieldInfo fieldInfo = (FieldInfo)val.Instruction.operand;
			FieldInfo field = fieldInfo.DeclaringType.GetField("startString");
			val.MatchBack(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldstr, (object)"abcdefghijklmnopqrstuvwxyz", (string)null)
			});
			if (!val.IsValid)
			{
				return codeInstructions;
			}
			MethodInfo method = typeof(MotherSubtitlePatch).GetMethod("GetRandomCharacters");
			val.RemoveInstruction();
			val.Insert((CodeInstruction[])(object)new CodeInstruction[3]
			{
				new CodeInstruction(OpCodes.Dup, (object)null),
				new CodeInstruction(OpCodes.Ldfld, (object)field),
				new CodeInstruction(OpCodes.Call, (object)method)
			});
			return val.InstructionEnumeration();
		}

		public static string GetRandomCharacters(string startString)
		{
			if (!ModuleBase<MotherSubtitlePatch>.IsEnabled || string.IsNullOrWhiteSpace(startString))
			{
				return "abcdefghijklmnopqrstuvwxyz";
			}
			string text = (string.IsNullOrEmpty(RandomCharacters) ? new string(startString.Distinct().ToArray()) : RandomCharacters);
			text = text.ToLower();
			if (!string.IsNullOrEmpty(NonRandomCharacters))
			{
				NonRandomCharacters = NonRandomCharacters.ToLower();
				text = new string(text.Where((char c) => !NonRandomCharacters.Contains(c)).ToArray());
			}
			return text;
		}
	}
	[ConfigSection("Modules.MotherSubtitlePatch", "This module replaces Mother subtitle texts.")]
	public class MotherSubtitlePatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class NotePatch : TextTranslator<NotePatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> NoteTexts;

		[JsonProperty]
		public static Dictionary<string, string> RoachTexts;

		[JsonProperty]
		public static string RoachTextTemplate;

		[JsonIgnore]
		public static Dictionary<string, string> TrimmedNoteTexts;

		[JsonIgnore]
		public static NotePatchSettings ModuleSettings;

		[OnDeserialized]
		private void OnDeserialized(StreamingContext _)
		{
			if (NoteTexts == null || NoteTexts.Count <= 0)
			{
				return;
			}
			TrimmedNoteTexts = new Dictionary<string, string>();
			foreach (KeyValuePair<string, string> noteText in NoteTexts)
			{
				string value = noteText.Value;
				if (value != null)
				{
					string key = noteText.Key.TrimStart();
					TrimmedNoteTexts[key] = value;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(HandItem_Note), "Initialize")]
		public static void Postfix_HandItemNote_Initialize(HandItem_Note __instance)
		{
			if (ModuleBase<NotePatch>.IsEnabled)
			{
				__instance.text.text = TextTranslator<NotePatch>.GetTextTranslation(TrimmedNoteTexts, __instance.text.text);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Localization), "GetLine")]
		public static string Postfix_Localization_GetLine(string __result, string group, string key)
		{
			if (!ModuleBase<NotePatch>.IsEnabled || group != "roachtext" || RoachTexts == null || !RoachTexts.ContainsKey(key))
			{
				return __result;
			}
			return RoachTexts[key] ?? __result;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(CL_UIManager), "SetDescription")]
		public static void Prefix_UIManager_SetDescription(ref string t)
		{
			if (ModuleBase<NotePatch>.IsEnabled && t.StartsWith("<color=#89501A>[ROACH MODE] ") && t.EndsWith("</color>") && RoachTextTemplate != null)
			{
				string newValue = t.Substring(28, t.Length - 36);
				t = RoachTextTemplate.Replace("{text}", newValue);
			}
		}
	}
	[ConfigSection("Modules.NotePatch", "This module replaces texts for paper notes.")]
	public class NotePatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class ObjectivePatch : TemplateTranslator<ObjectivePatch>
	{
		[JsonProperty]
		public static Dictionary<string, string> ObjectiveViewerTitleTemplates;

		[JsonProperty]
		public static Dictionary<string, string> ObjectiveTitleTemplates;

		[JsonProperty]
		public static Dictionary<string, string> ObjectiveDescriptionTemplates;

		[JsonProperty]
		public static Dictionary<string, string> ObjectiveProgressHeaderTemplates;

		[JsonProperty]
		public static Dictionary<string, string> ObjectiveSuccessHeaders;

		[JsonIgnore]
		public static TemplateTranslations ViewerTitleTemplates;

		[JsonIgnore]
		public static TemplateTranslations TitleTemplates;

		[JsonIgnore]
		public static TemplateTranslations DescriptionTemplates;

		[JsonIgnore]
		public static ObjectivePatchSettings ModuleSettings;

		[OnDeserialized]
		private void OnDeserialized(StreamingContext _)
		{
			if (ModuleBase<ObjectivePatch>.IsEnabled)
			{
				ViewerTitleTemplates = new TemplateTranslations(ObjectiveViewerTitleTemplates);
				TitleTemplates = new TemplateTranslations(ObjectiveTitleTemplates);
				DescriptionTemplates = new TemplateTranslations(ObjectiveDescriptionTemplates);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UI_ObjectiveViewer), "Awake")]
		public static void Postfix_ObjectiveViewer_Awake(UI_ObjectiveViewer __instance)
		{
			if (ModuleBase<ObjectivePatch>.IsEnabled)
			{
				__instance.objectiveViewerTitle.text = TemplateTranslator<ObjectivePatch>.GetTemplateTranslation(ViewerTitleTemplates, __instance.objectiveViewerTitle.text);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UI_ObjectiveViewer), "SetTitle")]
		public static void Prefix_ObjectiveViewer_SetTitle(ref string s)
		{
			if (ModuleBase<ObjectivePatch>.IsEnabled)
			{
				s = TemplateTranslator<ObjectivePatch>.GetTemplateTranslation(ViewerTitleTemplates, s);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UI_ObjectiveViewer), "CreateOrUpdateObjective")]
		public static void Prefix_ObjectiveViewer_CreateOrUpdateObjective(string id, ref string title, ref string desc)
		{
			if (ModuleBase<ObjectivePatch>.IsEnabled)
			{
				title = TemplateTranslator<ObjectivePatch>.GetTemplateTranslation(TitleTemplates, title);
				desc = TemplateTranslator<ObjectivePatch>.GetTemplateTranslation(DescriptionTemplates, desc);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(CH_ChallengeCounter), "Start")]
		public static void Prefix_ChallengeCounter_Start(CH_ChallengeCounter __instance)
		{
			if (!ModuleBase<ObjectivePatch>.IsEnabled)
			{
				return;
			}
			foreach (ObjectiveCounter objective in __instance.objectives)
			{
				objective.objectiveTitle = TemplateTranslator<ObjectivePatch>.GetTemplateTranslation(TitleTemplates, objective.objectiveTitle);
				objective.objectiveDesc = TemplateTranslator<ObjectivePatch>.GetTemplateTranslation(DescriptionTemplates, objective.objectiveDesc);
				objective.progressHeaderDesc = TextTranslator<ObjectivePatch>.GetTextTranslation(ObjectiveProgressHeaderTemplates, objective.progressHeaderDesc);
				objective.finishedHeaderDesc = TextTranslator<ObjectivePatch>.GetTextTranslation(ObjectiveSuccessHeaders, objective.finishedHeaderDesc);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(CH_Ro