Decompiled source of WKLocalizationLoader v0.6.3

plugins/WKLocalizationLoader.dll

Decompiled 3 hours 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 Steamworks;
using Steamworks.Data;
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(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("WKLocalizationLoader")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.6.3.0")]
[assembly: AssemblyInformationalVersion("0.6.3+69a5d4aa1d94e44ad1972915f95ed94bcad92993")]
[assembly: AssemblyProduct("WKLocalizationLoader")]
[assembly: AssemblyTitle("WKLocalizationLoader")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.6.3.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 ValueCollection<Type, Object> _scriptableObjectCache;

		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 void ScanScriptableObjects()
		{
			if (_scriptableObjectCache == null)
			{
				_scriptableObjectCache = new ValueCollection<Type, Object>();
			}
			Object[] array = Resources.FindObjectsOfTypeAll(typeof(ScriptableObject));
			foreach (Object val in array)
			{
				_scriptableObjectCache.Add(((object)val).GetType(), val);
			}
		}

		public static IEnumerable<TScriptableObject> EnumerateScriptableObjects<TScriptableObject>() where TScriptableObject : ScriptableObject
		{
			if (_scriptableObjectCache == null || !_scriptableObjectCache.TryGetValues(typeof(TScriptableObject), out var scriptableObjects))
			{
				yield break;
			}
			foreach (Object scriptableObject in scriptableObjects)
			{
				TScriptableObject so = (TScriptableObject)(object)((scriptableObject is TScriptableObject) ? scriptableObject : null);
				if (so != null)
				{
					yield return so;
				}
			}
		}

		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 class ModuleLoadResult
	{
		private ManualLogSource _logger;

		public List<ModuleInfo> ModuleInfos;

		public ModuleLoadResult(ManualLogSource logger)
		{
			_logger = logger;
			ModuleInfos = new List<ModuleInfo>();
		}

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

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

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

		public void AddConflictedModule(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."));
			AddModuleInfo(moduleClass, ModuleStatus.Conflicted, text);
		}

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

		public void AddModuleInfo(Type moduleClass, ModuleStatus status, string message)
		{
			ModuleInfo item = new ModuleInfo(moduleClass, status, message);
			ModuleInfos.Add(item);
		}

		public List<Type> FilterModuleClassesByModuleStatus(ModuleStatus status)
		{
			if (ModuleInfos == null)
			{
				return null;
			}
			return (from m in ModuleInfos
				where m.Status == status
				select m.ModuleClass).ToList();
		}

		public void PrintModuleInfoMessageBySeverity(ModuleStatus minSeverity)
		{
			if (ModuleInfos == null || _logger == null)
			{
				return;
			}
			List<ModuleInfo> list = ModuleInfos.Where((ModuleInfo m) => m.Status >= minSeverity).ToList();
			foreach (ModuleInfo item in list)
			{
				PrintModuleInfoMessage(item);
			}
		}

		public void PrintModuleInfoMessage(ModuleInfo moduleInfo)
		{
			if (_logger != null)
			{
				switch (moduleInfo.Status)
				{
				case ModuleStatus.OK:
					_logger.LogInfo((object)moduleInfo.Message);
					break;
				case ModuleStatus.Disabled:
					_logger.LogInfo((object)moduleInfo.Message);
					break;
				case ModuleStatus.Conflicted:
					_logger.LogInfo((object)moduleInfo.Message);
					break;
				case ModuleStatus.Failed:
					_logger.LogError((object)moduleInfo.Message);
					break;
				default:
					throw new ArgumentOutOfRangeException("How?");
				}
			}
		}
	}
	public static class ModuleManager
	{
		private static Plugin _plugin;

		private static ManualLogSource _logger;

		private static JsonSerializerSettings _jsonSerializerSettings;

		private static ModuleLoadResult _moduleLoadResult;

		private static ValueCollection<Type, string> _conflictedModsInfo = new ValueCollection<Type, string>();

		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_0013: Expected O, but got Unknown
			_jsonSerializerSettings = new JsonSerializerSettings
			{
				NullValueHandling = (NullValueHandling)1
			};
			if (plugin != null)
			{
				_plugin = plugin;
				string text = ((BaseUnityPlugin)_plugin).Info.Metadata.Name + "/ModuleManager";
				_logger = Logger.CreateLogSource(text);
				_moduleLoadResult = new ModuleLoadResult(_logger);
			}
		}

		public static void LoadAllModules()
		{
			LoadModule<AchievementPatch>("Achievements.json");
			LoadModule<AnnouncementSubtitlePatch>("AnnouncementSubtitles.json");
			LoadModule<AnnouncementSubtitleTimingPatch>("AnnouncementSubtitleTimings.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 (DetectConflictedMods(typeFromHandle, out var conflictedModGUIDs))
			{
				_moduleLoadResult.AddConflictedModule(typeFromHandle, conflictedModGUIDs);
				return;
			}
			if (!FileManager.TryGetModuleFilePath(fileName, out var filePath))
			{
				_moduleLoadResult.AddFileMissingModule(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 e)
			{
				_moduleLoadResult.AddDeserializationFailedModule(typeFromHandle, fileName, e);
				return;
			}
			if (ModuleBase<TModule>.IsEnabled)
			{
				_moduleLoadResult.AddOKModule(typeFromHandle);
			}
			else
			{
				_moduleLoadResult.AddDisabledModule(typeFromHandle);
			}
		}

		public static bool DetectConflictedMods(Type moduleClass, out List<string> conflictedModGUIDs)
		{
			conflictedModGUIDs = null;
			if (_conflictedModsInfo != null && _conflictedModsInfo.TryGetValues(moduleClass, out conflictedModGUIDs))
			{
				return conflictedModGUIDs.Any((string g) => Chainloader.PluginInfos.ContainsKey(g));
			}
			return false;
		}

		public static List<Type> FilterModuleClassesByModuleStatus(ModuleStatus status)
		{
			return _moduleLoadResult?.FilterModuleClassesByModuleStatus(status);
		}

		public static void PrintModuleInfoMessageBySeverity(ModuleStatus minSeverity)
		{
			_moduleLoadResult?.PrintModuleInfoMessageBySeverity(minSeverity);
		}
	}
	public enum ModuleStatus
	{
		OK,
		Disabled,
		Conflicted,
		Failed
	}
	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.6.3")]
	[BepInProcess("White Knuckle.exe")]
	public class Plugin : BaseUnityPlugin
	{
		private ConfigEntry<string> _languageFolder;

		private ConfigEntry<int> _maxScanDepth;

		public static ManualLogSource Logger;

		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* Texts.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.";

		public string LanguageFolder => _languageFolder?.Value;

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

		private void Awake()
		{
			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();
			ApplyHarmonyPatches(moduleClasses);
			ApplyScriptableObjectPatches(moduleClasses);
		}

		private void Initialize()
		{
			_languageFolder = ((BaseUnityPlugin)this).Config.Bind<string>("General", "LanguageFolder", "", _languageFolderDescription);
			_maxScanDepth = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxScanDepth", 5, _maxScanDepthDescription);
			Logger = ((BaseUnityPlugin)this).Logger;
			if (string.IsNullOrWhiteSpace(LanguageFolder))
			{
				List<string> list = LanguageScanner.Scan(MaxScanDepth, Logger);
				if (list.Count != 0)
				{
					Logger.LogInfo((object)"Loading the first valid Language Folder detected by default.");
					_languageFolder.Value = list.FirstOrDefault();
				}
			}
		}

		private List<Type> LoadAllModules()
		{
			ModuleManager.LoadAllModules();
			List<Type> result = ModuleManager.FilterModuleClassesByModuleStatus(ModuleStatus.OK);
			ModuleManager.PrintModuleInfoMessageBySeverity(ModuleStatus.OK);
			return result;
		}

		private 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)
				{
					PatchClassProcessor val2 = val.CreateClassProcessor(moduleClass);
					val2.Patch();
				}
			}
		}

		private 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;
			FilterScriptableObjectPatchClasses();
			if (ModuleClasses != null && ModuleClasses.Count != 0)
			{
				SceneManager.sceneLoaded += OnSceneLoaded;
			}
		}

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

		public static void ApplyScriptableObjectPatches()
		{
			foreach (Type moduleClass in ModuleClasses)
			{
				moduleClass.GetMethod("PatchScriptableObjects", BindingFlags.Static | BindingFlags.Public)?.Invoke(null, null);
			}
		}

		public static void FilterScriptableObjectPatchClasses()
		{
			if (ModuleClasses != null && ModuleClasses.Count != 0)
			{
				ModuleClasses = ModuleClasses.Where((Type m) => typeof(IScriptableObjectPatch).IsAssignableFrom(m) && m.GetMethod("PatchScriptableObjects", BindingFlags.Static | BindingFlags.Public) != null).ToList();
			}
		}
	}
	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)
		{
			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;
					if (key != null)
					{
						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 static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "WKLocalizationLoader";

		public const string PLUGIN_NAME = "WKLocalizationLoader";

		public const string PLUGIN_VERSION = "0.6.3";
	}
}
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", 2.2f, "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 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 ?? "{name} ({current}/{count})";
					infoPanel.debugText.text = text.Replace("{name}", textTranslation).Replace("{current}", num.ToString()).Replace("{count}", count.ToString());
				}
			}
		}
	}
	[ConfigSection("Modules.CosmeticPatch", "This module replaces texts for cosmetics.")]
	public class CosmeticPatchSettings : ModuleSettingsBase
	{
	}
	[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)
			{
				string text = "";
				OS_Window component = ((Component)__instance).GetComponent<OS_Window>();
				FileInfo fileInfo = component.file.fileInfo;
				if (fileInfo.textAssetData == null)
				{
					string data = fileInfo.data;
					string text2 = DarkMachineFunctions.ProcessText(data, true);
					text = TextTranslator<DocumentPatch>.GetTextTranslation(DocumentTexts, text2);
					fileInfo.data = data.Replace(text2, text);
				}
				else
				{
					text = TextTranslator<DocumentPatch>.GetTextTranslation(DocumentTexts, 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 UpgradeLockedHoverTextTemplate;

		[JsonProperty]
		public static string UpgradeCantAffordHoverTextTemplate;

		[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 (UpgradeLockedHoverTextTemplate != null)
				{
					__instance.tooltip.tip = UpgradeLockedHoverTextTemplate.Replace("{unlockDescription}", upgrade.unlockDesc).Replace("{description}", upgrade.description);
				}
				return;
			}
			int value = StatManager.saveData.GetRoachBankByID("campaign").value;
			if (!upgrade.IsOwned(facility.id) && upgrade.cost >= value && UpgradeCantAffordHoverTextTemplate != null)
			{
				__instance.tooltip.tip = UpgradeCantAffordHoverTextTemplate.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 (!string.IsNullOrEmpty(upgrade.unlockFlag) && !CL_GameManager.HasActiveFlag(upgrade.unlockFlag, true) && upgrade.prerequisiteUpgrade != null && !__instance.facility.HasUpgrade(upgrade.prerequisiteUpgrade.id) && UpgradeLockedHoverTextTemplate != null)
				{
					__instance.tooltip.tip = UpgradeLockedHoverTextTemplate.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
	{
	}
	[HarmonyPatch]
	public class FontAssetPatch : ModuleBase<FontAssetPatch>
	{
		[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);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TMP_FontAsset), "Awake")]
		public static void Postfix_FontAsset_Awake(TMP_FontAsset __instance)
		{
			if (ModuleBase<FontAssetPatch>.IsEnabled)
			{
				AddFallbackFontAssets(__instance);
			}
		}

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

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

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

		public static bool TryGetFallbackFontAssets(string targetFontName, out List<TMP_FontAsset> fallbackFontAssets)
		{
			if (FallbackFontAssets != null && FallbackFontAssets.TryGetValues(targetFontName, 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;
	}
	[HarmonyPriority(0)]
	[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 __instance)
		{
			Font font = __instance.font;
			string targetFontName = ((font != null) ? ((Object)font).name : null);
			if (TryGetSubstituteFont(targetFontName, out var substituteFont))
			{
				__instance.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> NewGameTexts;

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

		[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 KeepWhiteSpaceInGamemodeName;

		[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)
			{
				PatchGamemodes();
				PatchGamemodeSettings();
			}
		}

		public static void PatchGamemodes()
		{
			IEnumerable<M_Gamemode> enumerable = CacheManager.EnumerateScriptableObjects<M_Gamemode>();
			foreach (M_Gamemode item in enumerable)
			{
				item.unlockHint = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeUnlockHints, item.unlockHint);
				item.modeDescription = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeDescriptions, item.modeDescription);
				item.newGameText = TextTranslator<GamemodePatch>.GetTextTranslation(NewGameTexts, item.newGameText);
				item.introText = TextTranslator<GamemodePatch>.GetTextTranslation(GamemodeIntroTexts, item.introText);
			}
		}

		public static void PatchGamemodeSettings()
		{
			IEnumerable<GamemodeSetting> enumerable = CacheManager.EnumerateScriptableObjects<GamemodeSetting>();
			foreach (GamemodeSetting item in enumerable)
			{
				item.title = TextTranslator<GamemodePatch>.GetTextTranslation(ModifierTitles, item.title);
				item.description = TextTranslator<GamemodePatch>.GetTextTranslation(ModifierDescriptions, item.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_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, KeepWhiteSpaceInGamemodeName ? " " : "");
			}
			if (text2 != null && ModifierAppends != null)
			{
				foreach (KeyValuePair<string, string> modifierAppend in ModifierAppends)
				{
					string key = modifierAppend.Key;
					string value2 = modifierAppend.Value;
					if (key != null && value2 != null)
					{
						text2 = text2.Replace(key, value2);
					}
				}
			}
			string text3 = GamemodeTextTemplate ?? "{prefix}{gamemodeName}{modifierAppends}";
			return text3.Replace("{prefix}", originalText).Replace("{gamemodeName}", text).Replace("{modifierAppends}", text2);
		}

		public static string GetTranslatedProgress(ProgressionUnlock unlock)
		{
			string text = unlock.GetProgressString();
			if (text != "N/A" && 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 string ScoreTrackerTemplate;

		[JsonProperty]
		public static string DistanceTrackerTemplate;

		[JsonProperty]
		public static string SpeedTrackerTemplate;

		[JsonProperty]
		public static string HighScoreTrackerTemplate;

		[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.ascentTracker.text;
				if (text2.StartsWith("Climb Distance: ") && DistanceTrackerTemplate != null)
				{
					string newValue2 = text2.Substring(16);
					uiMan.ascentTracker.text = DistanceTrackerTemplate.Replace("{distance}", newValue2);
				}
				string text3 = uiMan.ascentRateTracker.text;
				if (text3.StartsWith("Climb Speed: ") && SpeedTrackerTemplate != null)
				{
					string newValue3 = text3.Substring(13);
					uiMan.ascentRateTracker.text = SpeedTrackerTemplate.Replace("{speed}", newValue3);
				}
				string text4 = uiMan.highScoreTracker.text;
				if (text4.StartsWith("High Score: ") && HighScoreTrackerTemplate != null)
				{
					string newValue4 = text4.Substring(12);
					uiMan.highScoreTracker.text = HighScoreTrackerTemplate.Replace("{highScore}", 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(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)
			{
				PatchRegions();
				PatchSubregions();
			}
		}

		public static void PatchRegions()
		{
			IEnumerable<M_Region> enumerable = CacheManager.EnumerateScriptableObjects<M_Region>();
			foreach (M_Region item in enumerable)
			{
				item.introText = TextTranslator<LocationNamePatch>.GetTextTranslation(RegionIntroTexts, item.introText);
			}
		}

		public static void PatchSubregions()
		{
			IEnumerable<M_Subregion> enumerable = CacheManager.EnumerateScriptableObjects<M_Subregion>();
			foreach (M_Subregion item in enumerable)
			{
				item.introText = TextTranslator<LocationNamePatch>.GetTextTranslation(SubregionIntroTexts, item.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 string LoadingProgressTemplate;

		[JsonProperty]
		public static string PageCounterTemplate;

		[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);
			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());
				}
			}
		}

		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;

		public ModuleBase()
		{
			FieldInfo field = GetType().GetField("ModuleSettings", BindingFlags.Static | BindingFlags.Public);
			if ((object)field != null)
			{
				ConfigSectionAttribute customAttribute = field.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 && field.GetValue(this) == null)
				{
					object value = Activator.CreateInstance(field.FieldType);
					field.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 (field.GetValue(moduleSettings) == null || 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 string RandomCharacters;

		[JsonProperty]
		public static string NonRandomCharacters;

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

		[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_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			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) => !Enumerable.Contains(NonRandomCharacters, 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;

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

		[JsonIgnore]
		public static NotePatchSettings ModuleSettings;

		[OnDeserialized]
		private void OnDeserialized(StreamingContext _)
		{
			TrimmedNoteTexts = NoteTexts.ToDictionary((KeyValuePair<string, string> t) => t.Key.TrimStart(Array.Empty<char>()), (KeyValuePair<string, string> t) => t.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);
			}
		}
	}
	[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_RoachCollector), "Start")]
		public static void Postfix_RoachCollector_Start(CH_RoachCollector __instance)
		{
			if (ModuleBase<ObjectivePatch>.IsEnabled)
			{
				__instance.successText = TextTranslator<ObjectivePatch>.GetTextTranslation(ObjectiveSuccessHeaders, __instance.successText);
			}
		}
	}
	[ConfigSection("Modules.ObjectivePatch", "This module replaces text information of objectives.")]
	public class ObjectivePatchSettings : ModuleSettingsBase
	{
	}
	[HarmonyPatch]
	public class PerkPatch : TextTranslator<PerkPatch>, IScriptableObjectPatch
	{
		[JsonProperty]
		public static Dictionary<string, string> PerkTitles;

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

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

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

		[JsonProperty]
		public static string DurationSecondTemplate;

		[JsonProperty]
		public static string DurationSecondsTemplate;

		[JsonProperty]
		public static string AppPerkHoverTextTemplate;

		[JsonProperty]
		public static string AppPerkAmountTemplate;

		[JsonProperty]
		public static string AppRefreshPurchasedText;

		[JsonIgnore]
		public static readonly Regex SecondsFormatRegex = CacheManager.GetOrCreateRegex("\\{.*?\\^s.*?\\}", RegexOptions.Compiled);

		[JsonIgnore]
		public static readonly Regex SecondsRegex = CacheManager.GetOrCreateRegex("([+-]?\\d+(?:\\.\\d+)?) Seconds?", RegexOptions.Compiled);

		[JsonIgnore]
		public static PerkPatchSettings ModuleSettings;

		public static void PatchScriptableObjects()
		{
			if (ModuleBase<PerkPatch>.IsEnabled)
			{
				PatchPerks();
			}
		}

		public static void PatchPerks()
		{
			IEnumerable<Perk> enumerable = CacheManager.EnumerateScriptableObjects<Perk>();
			foreach (Perk item in enumerable)
			{
				item.title = TextTranslator<PerkPatch>.GetTextTranslation(PerkTitles, item.title);
				item.description = TextTranslator<PerkPatch>.GetTextTranslation(PerkDescriptions, item.description);
				item.flavorText = TextTranslator<PerkPatch>.GetTextTranslation(PerkFlavorTexts, item.flavorText);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Perk), "GetTitle")]
		public static string Postfix_Perk_GetTitle(string __result, Perk __instance)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			if (!ModuleBase<PerkPatch>.IsEnabled || ((int)__instance.perkType != 8 && (int)__instance.perkType != 9))
			{
				return __result;
			}
			string translatedTrinketType = GetTranslatedTrinketType(__instance);
			string trinketPerkTitle = GetTrinketPerkTitle(__instance);
			return translatedTrinketType + "\n<shimmer s=0.1>" + trinketPerkTitle + "</shimmer></color>";
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Perk), "GetDescription")]
		public static string Postfix_Perk_GetDescription(string __result, Perk __instance)
		{
			if (!ModuleBase<PerkPatch>.IsEnabled || !SecondsFormatRegex.IsMatch(__instance.description))
			{
				return __result;
			}
			return SecondsRegex.Replace(__result, delegate(Match m)
			{
				string value = m.Groups[1].Value;
				string text = ((value == "1") ? (DurationSecondTemplate ?? "{time} Second") : (DurationSecondsTemplate ?? "{time} Seconds"));
				return text.Replace("{time}", value);
			});
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PerkModule_RemovalTimer), "GetCounterString")]
		public static string Postfix_RemovalTimerModule_GetCounterString(string __result, PerkModule_RemovalTimer __instance)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if (!ModuleBase<PerkPatch>.IsEnabled || (int)__instance.removalTimerDisplayType != 3)
			{
				return __result;
			}
			string removalTimerPrefix = __instance.removalTimerPrefix;
			string originalText = __result.Substring(removalTimerPrefix.Length);
			originalText = TextTranslator<PerkPatch>.GetTextTranslation(DurationRoughTexts, originalText);
			return removalTimerPrefix + originalText;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(App_PerkPage), "GenerateIcons")]
		public static void Postfix_PerkPage_GenerateIcons(App_PerkPage __instance)
		{
			if (!ModuleBase<PerkPatch>.IsEnabled)
			{
				return;
			}
			Image[] componentsInChildren = ((Component)__instance.iconParent).GetComponentsInChildren<Image>();
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			List<Perk> perks = CL_GameManager.gMan.localPlayer.perks;
			if (perks == null || perks.Count == 0)
			{
				return;
			}
			Dictionary<Sprite, Perk> dictionary = perks.ToDictionary((Perk p) => p.icon, (Perk p) => p);
			foreach (Image val in componentsInChildren)
			{
				OS_Tooltip component = ((Component)val).GetComponent<OS_Tooltip>();
				if ((Object)(object)component != (Object)null && dictionary.TryGetValue(val.sprite, out var value))
				{
					string title = value.GetTitle(true);
					string newValue = "<color=#C7C7C7>" + GetTranslatedAppPerkAmount(value, isPreview: false);
					string newValue2 = "<color=\"grey\">" + value.GetDescription(true, true, true, true) + "<color>";
					string text = AppPerkHoverTextTemplate ?? "{title}{amount}\n{description}";
					component.tip = text.Replace("{title}", title).Replace("{amount}", newValue).Replace("{description}", newValue2);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(App_PerkPage_Card), "Initialize")]
		public static void Postfix_PerkPageCard_Initialize(App_PerkPage_Card __instance, App_PerkPage page, Perk p)
		{
			if (ModuleBase<PerkPatch>.IsEnabled)
			{
				string title = p.GetTitle(true);
				string newValue = "<color=\"grey\">" + GetTranslatedAppPerkAmount(p, isPreview: true);
				string newValue2 = "<color=#C7C7C7>" + p.GetDescription(true, false, true, true) + "</color>";
				string text = AppPerkHoverTextTemplate ?? "{title}{amount}\n{description}";
				__instance.tooltip.tip = text.Replace("{title}", title).Replace("{amount}", newValue).Replace("{description}", newValue2);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(App_PerkPage), "PurchaseRefresh")]
		public static void Postfix_PerkPage_PurchaseRefresh(App_PerkPage __instance)
		{
			if (!ModuleBase<PerkPatch>.IsEnabled || AppRefreshPurchasedText == null)
			{
				return;
			}
			GameObject reloadSettingsRoot = __instance.reloadSettingsRoot;
			TMP_Text[] componentsInChildren = reloadSettingsRoot.GetComponentsInChildren<TMP_Text>();
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			foreach (TMP_Text val in componentsInChildren)
			{
				if (val.text == "<color=\"red>PURCHASED</color>")
				{
					val.text = AppRefreshPurchasedText;
					break;
				}
			}
		}

		public static string GetTranslatedTrinketType(Perk perk)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: I