Decompiled source of Crawl Localization Chinese v0.13.0

CrawlLocalization/CrawlLocalization.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Crawl Localization")]
[assembly: AssemblyDescription("Menu localization and per-language font replacement for Crawl")]
[assembly: AssemblyFileVersion("0.13.0.0")]
[assembly: AssemblyVersion("0.13.0.0")]
namespace CrawlLocalization;

[HarmonyPatch(typeof(LevelTitle), "Start")]
internal static class ChapterTitlePatch
{
	private static void Postfix(TextMesh ___m_textLevel, TextMesh ___m_textFloor)
	{
		if (!((Object)(object)Plugin.Instance == (Object)null))
		{
			Bind(___m_textLevel);
			Bind(___m_textFloor);
		}
	}

	private static void Bind(TextMesh mesh)
	{
		if (!((Object)(object)mesh == (Object)null))
		{
			LocalizedMenuLabel orCreate = LocalizedMenuLabel.GetOrCreate(((Component)mesh).gameObject);
			orCreate.Standalone = true;
			orCreate.ResetSource(mesh.text);
			orCreate.Refresh();
		}
	}
}
internal static class ChineseTextWrapper
{
	private static readonly Regex Units = new Regex("</?(?:b|i|size|color|material)(?:=[^>]*)?>|<quad\\s[^>]*>|\\r\\n|[\\r\\n]|[A-Za-z0-9]+(?:[-'][A-Za-z0-9]+)*|.", RegexOptions.IgnoreCase);

	private const string Closing = ",。!?;:、)】》」』”’%%.,!?;:)";

	private const string Opening = "(【《「『“‘(";

	private static bool IsTag(string unit)
	{
		if (unit.StartsWith("<", StringComparison.Ordinal))
		{
			return unit.EndsWith(">", StringComparison.Ordinal);
		}
		return false;
	}

	internal static bool ContainsChinese(string text)
	{
		if (text == null)
		{
			return false;
		}
		foreach (char c in text)
		{
			if (c >= '㐀' && c <= '鿿')
			{
				return true;
			}
		}
		return false;
	}

	internal static string Wrap(string text, float width, Func<char, float> advance, Func<string, float> tagWidth = null)
	{
		if (string.IsNullOrEmpty(text) || width <= 0f)
		{
			return text;
		}
		List<string> list = new List<string>();
		foreach (Match item in Units.Matches(text))
		{
			string value = item.Value;
			if (list.Count > 0 && !IsTag(list[list.Count - 1]) && value.Length == 1 && ",。!?;:、)】》」』”’%%.,!?;:)".IndexOf(value[0]) >= 0 && list[list.Count - 1] != "\n" && list[list.Count - 1] != "\r" && list[list.Count - 1] != "\r\n")
			{
				list[list.Count - 1] += value;
			}
			else if (list.Count > 0 && list[list.Count - 1].Length == 1 && "(【《「『“‘(".IndexOf(list[list.Count - 1][0]) >= 0 && !IsTag(value) && value != "\n" && value != "\r" && value != "\r\n")
			{
				list[list.Count - 1] += value;
			}
			else
			{
				list.Add(value);
			}
		}
		StringBuilder stringBuilder = new StringBuilder();
		float num = 0f;
		foreach (string item2 in list)
		{
			switch (item2)
			{
			case "\r\n":
			case "\r":
			case "\n":
				stringBuilder.Append('\n');
				num = 0f;
				continue;
			}
			if (IsTag(item2))
			{
				float num2 = tagWidth?.Invoke(item2) ?? 0f;
				if (num2 > 0f && num > 0f && num + num2 > width)
				{
					stringBuilder.Append('\n');
					num = 0f;
				}
				stringBuilder.Append(item2);
				num += num2;
				continue;
			}
			float num3 = 0f;
			string text2 = item2;
			foreach (char arg in text2)
			{
				num3 += advance(arg);
			}
			if (num > 0f && num + num3 > width)
			{
				stringBuilder.Append('\n');
				num = 0f;
			}
			if (num == 0f && item2 == " ")
			{
				continue;
			}
			text2 = item2;
			foreach (char c in text2)
			{
				float num4 = advance(c);
				if (num > 0f && num + num4 > width)
				{
					stringBuilder.Append('\n');
					num = 0f;
				}
				stringBuilder.Append(c);
				num += num4;
			}
		}
		return stringBuilder.ToString();
	}
}
internal sealed class FontChoice
{
	internal Font Font;

	internal int Size;

	internal FontStyle Style;

	internal float Scale = 1f;
}
internal sealed class FontService : IDisposable
{
	private readonly Action<string> warn;

	private readonly Action<string> info;

	private readonly Dictionary<string, Font> fonts = new Dictionary<string, Font>(StringComparer.Ordinal);

	private readonly Dictionary<string, AssetBundle> bundles = new Dictionary<string, AssetBundle>(StringComparer.OrdinalIgnoreCase);

	private readonly HashSet<string> privateFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private readonly List<Font> ownedFonts = new List<Font>();

	private static readonly Regex RichTags = new Regex("</?(?:b|i|size|color|material)(?:=[^>]*)?>|<quad\\s[^>]*>", RegexOptions.IgnoreCase);

	private const uint PrivateFont = 16u;

	[DllImport("gdi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
	private static extern int AddFontResourceEx(string file, uint flags, IntPtr reserved);

	[DllImport("gdi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
	private static extern bool RemoveFontResourceEx(string file, uint flags, IntPtr reserved);

	internal FontService(Action<string> warn, Action<string> info)
	{
		this.warn = warn;
		this.info = info;
	}

	internal bool TryChoose(LanguagePack language, Font original, int size, FontStyle style, string text, out FontChoice choice)
	{
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0185: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		choice = null;
		if (language.Fonts.TryGetValue(((Object)(object)original == (Object)null) ? "" : ((Object)original).name, out var value) || language.Fonts.TryGetValue("*", out value))
		{
			Font val = Load(value);
			int num = ((value.Kind == "system") ? 32 : SamplingSize(language, size, text));
			if (Covers(val, text, num, style))
			{
				choice = new FontChoice
				{
					Font = val,
					Size = num,
					Style = style,
					Scale = value.Scale * ((value.Kind == "system") ? HeightRatio(original, size, style, val, num, text) : ((float)size / (float)num))
				};
				return true;
			}
			warn("Mapped font unavailable or missing glyphs: " + value.Name + "; trying original/system fonts.");
		}
		int num2 = SamplingSize(language, size, text);
		if (Covers(original, text, num2, style))
		{
			choice = new FontChoice
			{
				Font = original,
				Size = num2,
				Style = style,
				Scale = (float)size / (float)num2
			};
			return true;
		}
		string[] fallbackFonts = language.FallbackFonts;
		foreach (string text2 in fallbackFonts)
		{
			Font val2 = Load(new FontRule
			{
				Kind = "system",
				File = "",
				Name = text2.Trim(),
				Scale = 1f
			});
			if (Covers(val2, text, 32, style))
			{
				choice = new FontChoice
				{
					Font = val2,
					Size = 32,
					Style = style,
					Scale = HeightRatio(original, size, style, val2, 32, text)
				};
				return true;
			}
		}
		warn("No font covers translation: " + text + "; retaining original menu text.");
		return false;
	}

	internal bool TryChooseGui(LanguagePack language, Font original, int size, FontStyle style, string text, out Font font)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		font = original;
		if (language.Fonts.TryGetValue("CrawlOnlineOverlay", out var value))
		{
			Font val = Load(value);
			if (Covers(val, text, size, style))
			{
				font = val;
				return true;
			}
		}
		if (Covers(original, text, size, style))
		{
			return true;
		}
		string[] fallbackFonts = language.FallbackFonts;
		foreach (string text2 in fallbackFonts)
		{
			Font val2 = Load(new FontRule
			{
				Kind = "system",
				File = "",
				Name = text2.Trim(),
				Scale = 1f
			});
			if (Covers(val2, text, size, style))
			{
				font = val2;
				return true;
			}
		}
		return false;
	}

	internal static int SamplingSize(LanguagePack language, int size, string text)
	{
		if (!language.UsesChineseTextRules || size <= 0 || size >= 64 || text == null)
		{
			return size;
		}
		foreach (char c in text)
		{
			if (c >= '㐀' && c <= '鿿')
			{
				return size * ((160 + size - 1) / size);
			}
		}
		return size;
	}

	private Font Load(FontRule rule)
	{
		string text = rule.Kind + "|" + rule.File + "|" + rule.Name;
		if (fonts.TryGetValue(text, out var value))
		{
			return value;
		}
		value = null;
		try
		{
			if (rule.Kind == "bundle")
			{
				if (!bundles.TryGetValue(rule.File, out var value2))
				{
					if (!File.Exists(rule.File))
					{
						throw new FileNotFoundException("Font bundle not found.", rule.File);
					}
					value2 = AssetBundle.LoadFromFile(rule.File);
					bundles.Add(rule.File, value2);
				}
				if ((Object)(object)value2 == (Object)null)
				{
					throw new InvalidOperationException("Cannot load bundle; use a Windows bundle compatible with Unity 5.4.2.");
				}
				value = value2.LoadAsset<Font>(rule.Name);
				if ((Object)(object)value == (Object)null)
				{
					throw new InvalidOperationException("Font asset not found: " + rule.Name);
				}
			}
			else
			{
				if (rule.Kind == "ttf" && !privateFiles.Contains(rule.File))
				{
					if (Environment.OSVersion.Platform != PlatformID.Win32NT)
					{
						throw new PlatformNotSupportedException("Private TTF loading requires Windows. Use a compatible bundle on other platforms.");
					}
					if (!File.Exists(rule.File))
					{
						throw new FileNotFoundException("Font file not found.", rule.File);
					}
					if (AddFontResourceEx(rule.File, 16u, IntPtr.Zero) == 0)
					{
						throw new InvalidOperationException("Windows rejected font file (error " + Marshal.GetLastWin32Error() + ").");
					}
					privateFiles.Add(rule.File);
				}
				if (rule.Kind == "system" && !Array.Exists(Font.GetOSInstalledFontNames(), (string name) => string.Equals(name, rule.Name, StringComparison.OrdinalIgnoreCase)))
				{
					throw new InvalidOperationException("System font is not installed: " + rule.Name);
				}
				value = Font.CreateDynamicFontFromOSFont(rule.Name, 32);
				if ((Object)(object)value == (Object)null)
				{
					throw new InvalidOperationException("Unity could not create font: " + rule.Name);
				}
				Object.DontDestroyOnLoad((Object)(object)value);
				ownedFonts.Add(value);
			}
		}
		catch (Exception ex)
		{
			warn("Font load failed [" + text + "]: " + ex.Message);
		}
		fonts[text] = value;
		if ((Object)(object)value != (Object)null)
		{
			info(((rule.Kind == "bundle") ? "Loaded embedded font: " : "Created font by family (file identity unverified): ") + text);
		}
		return value;
	}

	private bool Covers(Font font, string text, int size, FontStyle style)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)font == (Object)null || (Object)(object)font.material == (Object)null)
			{
				return false;
			}
			text = RichTags.Replace(text, "");
			if (font.dynamic)
			{
				font.RequestCharactersInTexture(text, size, style);
			}
			string text2 = text;
			CharacterInfo val = default(CharacterInfo);
			foreach (char c in text2)
			{
				if (!char.IsWhiteSpace(c) && !char.IsControl(c) && (!font.GetCharacterInfo(c, ref val, size, style) || ((CharacterInfo)(ref val)).glyphWidth <= 0 || ((CharacterInfo)(ref val)).glyphHeight <= 0))
				{
					return false;
				}
			}
			return true;
		}
		catch (Exception ex)
		{
			warn("Font glyph check failed; trying next font: " + ex.Message);
			return false;
		}
	}

	private static float HeightRatio(Font original, int originalSize, FontStyle style, Font replacement, int size, string text)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		float num = (float)originalSize * 0.7f;
		CharacterInfo val = default(CharacterInfo);
		if ((Object)(object)original != (Object)null)
		{
			if (original.dynamic)
			{
				original.RequestCharactersInTexture("A", originalSize, style);
			}
			if (original.GetCharacterInfo('A', ref val, originalSize, style))
			{
				num = Mathf.Max(1, ((CharacterInfo)(ref val)).maxY - ((CharacterInfo)(ref val)).minY);
			}
		}
		string text2 = RichTags.Replace(text, "");
		foreach (char c in text2)
		{
			if (c > '\u007f' && !char.IsWhiteSpace(c) && replacement.GetCharacterInfo(c, ref val, size, style))
			{
				return num / (float)Mathf.Max(1, ((CharacterInfo)(ref val)).maxY - ((CharacterInfo)(ref val)).minY);
			}
		}
		return num / Mathf.Max(1f, (float)size * 0.7f);
	}

	public void Dispose()
	{
		foreach (Font ownedFont in ownedFonts)
		{
			if ((Object)(object)ownedFont != (Object)null)
			{
				Object.Destroy((Object)(object)ownedFont);
			}
		}
		foreach (AssetBundle value in bundles.Values)
		{
			if ((Object)(object)value != (Object)null)
			{
				value.Unload(true);
			}
		}
		foreach (string privateFile in privateFiles)
		{
			RemoveFontResourceEx(privateFile, 16u, IntPtr.Zero);
		}
		fonts.Clear();
		bundles.Clear();
		ownedFonts.Clear();
		privateFiles.Clear();
	}
}
[HarmonyPatch(typeof(GUIActionIndicator), "SetText")]
internal static class GameplayActionPatch
{
	private static void Postfix(string text, TextMesh ___m_textMesh)
	{
		if ((Object)(object)___m_textMesh == (Object)null || (Object)(object)Plugin.Instance == (Object)null)
		{
			return;
		}
		LocalizedMenuLabel localizedMenuLabel = ((Component)___m_textMesh).GetComponent<LocalizedMenuLabel>();
		if ((Object)(object)localizedMenuLabel == (Object)null)
		{
			if (!Plugin.Instance.Language.TryTranslate(text, out var _))
			{
				return;
			}
			localizedMenuLabel = LocalizedMenuLabel.GetOrCreate(((Component)___m_textMesh).gameObject);
		}
		localizedMenuLabel.Skip = false;
		localizedMenuLabel.RefreshDirect(text, null);
	}
}
[HarmonyPatch]
internal static class GameplayStaticTextPatch
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		Type[] array = new Type[5]
		{
			typeof(PropPortal),
			typeof(PropSign),
			typeof(PropStoreTeleporter),
			typeof(PropActionable),
			typeof(GUIGame)
		};
		foreach (Type type in array)
		{
			yield return AccessTools.Method(type, "Start", (Type[])null, (Type[])null);
		}
	}

	private static void Postfix(MonoBehaviour __instance)
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			MenuTextMeshes.Scan(((Component)__instance).gameObject);
		}
	}
}
[HarmonyPatch(typeof(GUIPlayer), "Start")]
internal static class GameplayRewardPatch
{
	private static void Postfix(GUIPlayer __instance, MenuFlashEffect ___m_favourTallyContainer)
	{
		if ((Object)(object)Plugin.Instance == (Object)null)
		{
			return;
		}
		if ((Object)(object)___m_favourTallyContainer != (Object)null)
		{
			TextMesh[] componentsInChildren = ((Component)___m_favourTallyContainer).GetComponentsInChildren<TextMesh>(true);
			foreach (TextMesh val in componentsInChildren)
			{
				string text = null;
				if (string.Equals(val.text, "gold", StringComparison.OrdinalIgnoreCase))
				{
					text = "@gameplay.reward-unit:gold";
				}
				else if (string.Equals(val.text, "REWARD", StringComparison.OrdinalIgnoreCase))
				{
					text = "REWARD";
				}
				if (text != null)
				{
					LocalizedMenuLabel.GetOrCreate(((Component)val).gameObject).RefreshDirect(text, null);
				}
			}
		}
		MenuTextMeshes.Scan(((Component)__instance).gameObject);
	}
}
[HarmonyPatch(typeof(GUIGame), "ShowKillStreak")]
internal static class GameplayStreakPatch
{
	private static void Postfix(GameObject ___m_killStreak)
	{
		if (!((Object)(object)Plugin.Instance == (Object)null) && !((Object)(object)___m_killStreak == (Object)null))
		{
			TextMesh component = ___m_killStreak.GetComponent<TextMesh>();
			if ((Object)(object)component != (Object)null)
			{
				LocalizedMenuLabel.GetOrCreate(((Component)component).gameObject).RefreshDirect(component.text, null);
			}
		}
	}
}
internal static class HeroStatsDisplay
{
	internal static void Refresh(TextMesh mesh, string lookup, string fallback)
	{
		if (!((Object)(object)mesh == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null))
		{
			LocalizedMenuLabel.GetOrCreate(((Component)mesh).gameObject).RefreshDirect(lookup, fallback);
		}
	}
}
[HarmonyPatch(typeof(GUIHeroStats), "UpdateText")]
internal static class HeroStatsNamesPatch
{
	private static void Postfix(PlayerGameData ___m_player, TextMesh ___m_weaponNoSplit, TextMesh ___m_weaponSplit1, TextMesh ___m_weaponSplit2, TextMesh ___m_spell)
	{
		if ((Object)(object)Plugin.Instance == (Object)null || ___m_player == null || ___m_player.GetWeaponData() == null)
		{
			return;
		}
		if ((Object)(object)___m_weaponNoSplit != (Object)null && ((Component)___m_weaponNoSplit).gameObject.activeSelf)
		{
			HeroStatsDisplay.Refresh(___m_weaponNoSplit, ___m_weaponNoSplit.text, null);
		}
		else
		{
			if ((Object)(object)___m_weaponSplit1 != (Object)null)
			{
				HeroStatsDisplay.Refresh(___m_weaponSplit1, "@shop.prefix:" + ___m_weaponSplit1.text, null);
			}
			if ((Object)(object)___m_weaponSplit2 != (Object)null)
			{
				HeroStatsDisplay.Refresh(___m_weaponSplit2, ___m_weaponSplit2.text, null);
			}
		}
		if ((Object)(object)___m_spell != (Object)null)
		{
			HeroStatsDisplay.Refresh(___m_spell, ___m_spell.text, null);
		}
	}
}
[HarmonyPatch(typeof(GUIHeroStats), "UpdateArtifactIcons")]
internal static class HeroStatsArtifactsPatch
{
	private static void Prefix(int ___m_numArtifacts, out int __state)
	{
		__state = ___m_numArtifacts;
	}

	private static void Postfix(bool onlyIfCountChanged, int __state, int ___m_numArtifacts, PlayerGameData ___m_player, List<GUIArtifactIcon> ___m_artifactIcons)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Invalid comparison between Unknown and I4
		if ((Object)(object)Plugin.Instance == (Object)null || ___m_player == null || ___m_artifactIcons == null || (onlyIfCountChanged && __state == ___m_numArtifacts))
		{
			return;
		}
		int num = 0;
		foreach (Powerup powerup in ___m_player.GetPowerups())
		{
			if ((int)powerup.GetPurchasableType() == 2)
			{
				if (num >= ___m_numArtifacts || num >= ___m_artifactIcons.Count)
				{
					break;
				}
				GUIArtifactIcon val = ___m_artifactIcons[num++];
				if (!((Object)(object)val == (Object)null))
				{
					string lookup = (string.IsNullOrEmpty(powerup.m_displayNameHud) ? powerup.m_displayName : powerup.m_displayNameHud);
					HeroStatsDisplay.Refresh(val.m_text, lookup, powerup.m_displayName);
				}
			}
		}
	}
}
internal sealed class FontRule
{
	internal string Kind;

	internal string File;

	internal string Name;

	internal float Scale;
}
internal sealed class LanguagePack
{
	internal string Id;

	internal string[] FallbackFonts = new string[0];

	internal readonly Dictionary<string, FontRule> Fonts = new Dictionary<string, FontRule>(StringComparer.Ordinal);

	private readonly Dictionary<string, string> exact = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

	private readonly List<NumericTranslation> numeric = new List<NumericTranslation>();

	internal readonly HashSet<string> TextMeshPaths = new HashSet<string>(StringComparer.Ordinal);

	internal bool UsesChineseTextRules
	{
		get
		{
			if (Id != null)
			{
				if (!string.Equals(Id, "zh", StringComparison.OrdinalIgnoreCase) && !Id.StartsWith("zh-", StringComparison.OrdinalIgnoreCase))
				{
					return Id.StartsWith("zh_", StringComparison.OrdinalIgnoreCase);
				}
				return true;
			}
			return false;
		}
	}

	internal int Count => exact.Count + numeric.Count;

	internal static string NormalizeId(string id)
	{
		id = (id ?? "").Trim();
		if (string.Equals(id, "zh-CN", StringComparison.OrdinalIgnoreCase) || string.Equals(id, "zh-Hans", StringComparison.OrdinalIgnoreCase))
		{
			return "zh-Hans";
		}
		if (string.Equals(id, "zh-TW", StringComparison.OrdinalIgnoreCase) || string.Equals(id, "zh-Hant", StringComparison.OrdinalIgnoreCase))
		{
			return "zh-Hant";
		}
		if (string.Equals(id, "en", StringComparison.OrdinalIgnoreCase))
		{
			return "en";
		}
		if (!Regex.IsMatch(id, "\\A[A-Za-z0-9][A-Za-z0-9_-]*\\z"))
		{
			return "en";
		}
		return id;
	}

	internal static string ResolveFile(string root, string relative)
	{
		if (string.IsNullOrEmpty(relative) || Path.IsPathRooted(relative))
		{
			throw new FormatException("Expected a relative file path.");
		}
		string text = Path.GetFullPath(root).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
		char directorySeparatorChar = Path.DirectorySeparatorChar;
		string text2 = text + directorySeparatorChar;
		string fullPath = Path.GetFullPath(Path.Combine(text2, relative));
		if (!fullPath.StartsWith(text2, StringComparison.OrdinalIgnoreCase))
		{
			throw new FormatException("File must stay inside the plugin folder.");
		}
		return fullPath;
	}

	internal static LanguagePack Load(string root, string id, Action<string> warn)
	{
		LanguagePack pack = new LanguagePack
		{
			Id = NormalizeId(id)
		};
		if (pack.Id == "en")
		{
			return pack;
		}
		string path = Path.Combine(Path.Combine(root, "Languages"), pack.Id);
		string[] array = new string[10] { "translations.tsv", "vault-translations.tsv", "deity-translations.tsv", "chapter-translations.tsv", "shop-translations.tsv", "gameplay-translations.tsv", "progression-translations.tsv", "shop-il-translations.tsv", "crawlonline-translations.tsv", "tutorial-translations.tsv" };
		foreach (string text in array)
		{
			string path2 = Path.Combine(path, text);
			if (text != "translations.tsv" && !File.Exists(path2))
			{
				continue;
			}
			ReadLines(path2, warn, delegate(string line)
			{
				string[] array2 = line.Split(new char[1] { '\t' });
				if ((array2.Length != 2 && array2.Length != 3) || array2[0].Length == 0 || (array2[1].Length == 0 && !array2[0].StartsWith("@shop.il:", StringComparison.Ordinal)))
				{
					throw new FormatException("Expected source<TAB>translation[<TAB>comment].");
				}
				if (!(array2[1] == "@TODO"))
				{
					string text2 = NormalizeText(Unescape(array2[0]));
					string text3 = Unescape(array2[1]);
					NumericTranslation numericTranslation = NumericTranslation.Create(text2, text3);
					if (text2.StartsWith("@shop.", StringComparison.Ordinal) || text2.StartsWith("@progression.", StringComparison.Ordinal) || text2.StartsWith("@online:", StringComparison.Ordinal))
					{
						numericTranslation = null;
					}
					if (numericTranslation != null)
					{
						foreach (NumericTranslation item in pack.numeric)
						{
							if (string.Equals(item.Source, text2, StringComparison.OrdinalIgnoreCase))
							{
								throw new FormatException("Duplicate source: " + text2);
							}
						}
						pack.numeric.Add(numericTranslation);
					}
					else
					{
						if (pack.exact.ContainsKey(text2))
						{
							throw new FormatException("Duplicate source: " + text2);
						}
						pack.exact.Add(text2, text3);
					}
				}
			});
		}
		ReadLines(Path.Combine(path, "fonts.tsv"), warn, delegate(string line)
		{
			string[] array2 = line.Split(new char[1] { '\t' });
			if (array2.Length != 5 || array2[0].Length == 0 || array2[3].Length == 0 || !float.TryParse(array2[4], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || float.IsNaN(result) || float.IsInfinity(result) || result <= 0f || result > 10f)
			{
				throw new FormatException("Expected original<TAB>kind<TAB>file<TAB>family/asset<TAB>scale (0..10).");
			}
			if (array2[1] != "ttf" && array2[1] != "bundle" && array2[1] != "system")
			{
				throw new FormatException("Font kind must be ttf, bundle or system.");
			}
			string file = ((array2[1] == "system") ? "" : ResolveFile(root, array2[2]));
			if (pack.Fonts.ContainsKey(array2[0]))
			{
				throw new FormatException("Duplicate font mapping: " + array2[0]);
			}
			pack.Fonts.Add(array2[0], new FontRule
			{
				Kind = array2[1],
				File = file,
				Name = array2[3],
				Scale = result
			});
		});
		ReadLines(Path.Combine(path, "language.ini"), warn, delegate(string line)
		{
			int num = line.IndexOf('=');
			if (num <= 0)
			{
				throw new FormatException("Expected key=value.");
			}
			string text2 = line.Substring(0, num).Trim();
			if (text2 == "FallbackFonts")
			{
				pack.FallbackFonts = line.Substring(num + 1).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			}
			else if (text2 != "Name")
			{
				throw new FormatException("Unknown setting: " + text2);
			}
		});
		string path3 = Path.Combine(path, "textmesh-paths.txt");
		if (File.Exists(path3))
		{
			ReadLines(path3, warn, delegate(string line)
			{
				pack.TextMeshPaths.Add(line.Trim());
			});
		}
		return pack;
	}

	internal static string[] ReadTextLines(string path)
	{
		using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
		using StreamReader streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true));
		List<string> list = new List<string>();
		string item;
		while ((item = streamReader.ReadLine()) != null)
		{
			list.Add(item);
		}
		return list.ToArray();
	}

	private static void ReadLines(string path, Action<string> warn, Action<string> read)
	{
		try
		{
			string[] array = ReadTextLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i].Trim().Length != 0 && !array[i].StartsWith("#", StringComparison.Ordinal))
				{
					try
					{
						read(array[i]);
					}
					catch (Exception ex)
					{
						warn(path + ":" + (i + 1) + ": " + ex.Message);
					}
				}
			}
		}
		catch (Exception ex2)
		{
			warn("Cannot read " + path + ": " + ex2.Message + "; keeping original text/font where needed.");
		}
	}

	internal bool TryTranslate(string source, out string translated)
	{
		translated = null;
		if (source == null)
		{
			return false;
		}
		source = NormalizeText(source);
		if (exact.TryGetValue(source, out translated))
		{
			return true;
		}
		foreach (NumericTranslation item in numeric)
		{
			if (item.TryTranslate(source, out translated))
			{
				return true;
			}
		}
		return false;
	}

	internal static string NormalizeText(string text)
	{
		return text.Replace("\r\n", "\n").Replace('\r', '\n');
	}

	private static string Unescape(string value)
	{
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < value.Length; i++)
		{
			if (value[i] != '\\')
			{
				stringBuilder.Append(value[i]);
				continue;
			}
			if (++i == value.Length)
			{
				throw new FormatException("Trailing backslash; escape literal backslashes.");
			}
			switch (value[i])
			{
			case 'n':
				stringBuilder.Append('\n');
				break;
			case 'r':
				stringBuilder.Append('\r');
				break;
			case 't':
				stringBuilder.Append('\t');
				break;
			case '\\':
				stringBuilder.Append('\\');
				break;
			default:
				throw new FormatException("Unsupported escape sequence.");
			}
		}
		return stringBuilder.ToString();
	}
}
[HarmonyPatch(typeof(MenuTextMenuItem), "Initialise")]
internal static class MenuInitialisePatch
{
	private static void Prefix(MenuTextMenuItem __instance, MenuTextMenuItemData data)
	{
		if (!((Object)(object)Plugin.Instance == (Object)null) && data != null)
		{
			LocalizedMenuLabel orCreate = LocalizedMenuLabel.GetOrCreate(((Component)__instance).gameObject);
			if (!((Object)(object)orCreate == (Object)null))
			{
				orCreate.Skip = data.m_message == "MsgCharSelect" || data.m_message == "MsgNameSelect";
				orCreate.ResetSource(data.m_text);
			}
		}
	}

	private static void Postfix(MenuTextMenuItem __instance)
	{
		MenuTextMeshes.Scan(((Component)__instance).gameObject);
	}
}
[HarmonyPatch(typeof(MenuItemTextButton), "Initialise")]
internal static class TextButtonInitialisePatch
{
	private static void Postfix(MenuItemTextButton __instance)
	{
		LocalizedMenuLabel component = ((Component)__instance).GetComponent<LocalizedMenuLabel>();
		if ((Object)(object)component != (Object)null)
		{
			component.Refresh();
		}
	}
}
[HarmonyPatch(typeof(GUIText), "SetText")]
internal static class MenuDisplayPatch
{
	private static void Prefix(GUIText __instance, ref string text)
	{
		if (!((Object)(object)Plugin.Instance == (Object)null))
		{
			LocalizedMenuLabel localizedMenuLabel = ((Component)__instance).GetComponent<LocalizedMenuLabel>();
			if ((Object)(object)localizedMenuLabel == (Object)null && (Object)(object)((Component)__instance).GetComponent<MenuTextMenuItem>() != (Object)null)
			{
				localizedMenuLabel = LocalizedMenuLabel.GetOrCreate(((Component)__instance).gameObject);
			}
			if ((Object)(object)localizedMenuLabel == (Object)null)
			{
				localizedMenuLabel = MenuTextMeshes.Bind(((Component)__instance).gameObject, refresh: false);
			}
			if ((Object)(object)localizedMenuLabel != (Object)null)
			{
				text = localizedMenuLabel.Translate(text);
			}
		}
	}

	private static void Postfix(GUIText __instance)
	{
		LocalizedMenuLabel component = ((Component)__instance).GetComponent<LocalizedMenuLabel>();
		if ((Object)(object)component != (Object)null)
		{
			component.CaptureRendered();
		}
	}
}
[HarmonyPatch(typeof(MenuFlashEffect), "StartFlash")]
internal static class MenuFlashPatch
{
	private static void Prefix(MenuFlashEffect __instance)
	{
		LocalizedMenuLabel component = ((Component)__instance).GetComponent<LocalizedMenuLabel>();
		if ((Object)(object)component != (Object)null)
		{
			component.Refresh();
		}
	}
}
[HarmonyPatch(typeof(GUIText), "SetFontScale")]
internal static class MenuFontScalePatch
{
	private static void Prefix(GUIText __instance, ref float scale)
	{
		LocalizedMenuLabel component = ((Component)__instance).GetComponent<LocalizedMenuLabel>();
		if (!((Object)(object)component == (Object)null))
		{
			component.BaseScale = scale;
			scale *= component.ScaleFactor;
		}
	}
}
public sealed class LocalizedMenuLabel : MonoBehaviour
{
	internal static Func<string, Func<LanguagePack, string>> ExternalProvider;

	private Func<LanguagePack, string> boundExternalProvider;

	internal bool Skip;

	internal bool Standalone;

	internal Func<LanguagePack, string> TextProvider;

	internal float BaseScale;

	internal float ScaleFactor = 1f;

	private TextMesh mesh;

	private Renderer textRenderer;

	private Font originalFont;

	private Material originalMaterial;

	private Material localMaterial;

	private int originalSize;

	private FontStyle originalStyle;

	private string source;

	private string display;

	private string rendered;

	private string directLookup;

	private string directFallback;

	private bool usesDirectLookup;

	internal static LocalizedMenuLabel GetOrCreate(GameObject obj)
	{
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		LocalizedMenuLabel component = obj.GetComponent<LocalizedMenuLabel>();
		if ((Object)(object)component != (Object)null)
		{
			return component;
		}
		TextMesh component2 = obj.GetComponent<TextMesh>();
		if ((Object)(object)component2 == (Object)null)
		{
			return null;
		}
		component = obj.AddComponent<LocalizedMenuLabel>();
		component.mesh = component2;
		component.textRenderer = ((Component)component2).GetComponent<Renderer>();
		component.originalFont = component2.font;
		component.originalMaterial = (((Object)(object)component.textRenderer == (Object)null) ? null : component.textRenderer.sharedMaterial);
		component.originalSize = component2.fontSize;
		component.originalStyle = component2.fontStyle;
		component.BaseScale = component2.characterSize;
		Font.textureRebuilt += component.OnTextureRebuilt;
		Plugin.Instance.Labels.Add(component);
		return component;
	}

	private void BindExternal(string text)
	{
		if (ExternalProvider == null)
		{
			return;
		}
		Func<LanguagePack, string> func = ExternalProvider(text);
		if (func != null)
		{
			TextProvider = func;
			boundExternalProvider = func;
		}
		else if (boundExternalProvider != null && text != source && text != display)
		{
			if ((object)TextProvider == boundExternalProvider)
			{
				TextProvider = null;
			}
			boundExternalProvider = null;
		}
	}

	internal void ResetSource(string text)
	{
		BindExternal(text);
		source = text;
		display = null;
	}

	internal void RefreshDirect(string lookup, string fallback)
	{
		string text = mesh.text;
		if (usesDirectLookup && source == text && directLookup == lookup && directFallback == fallback && rendered != null)
		{
			mesh.text = rendered;
			return;
		}
		directLookup = lookup;
		directFallback = fallback;
		if (!usesDirectLookup)
		{
			TextProvider = TranslateDirect;
			usesDirectLookup = true;
		}
		ResetSource(text);
		Refresh();
	}

	private string TranslateDirect(LanguagePack pack)
	{
		if (pack.TryTranslate(directLookup, out var translated))
		{
			return translated;
		}
		if (directFallback == null || !pack.TryTranslate(directFallback, out translated))
		{
			return null;
		}
		return translated;
	}

	internal string Translate(string text)
	{
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		BindExternal(text);
		if (display == null || text != display)
		{
			source = text;
		}
		try
		{
			string translated = null;
			Plugin instance = Plugin.Instance;
			int size = ((originalSize > 0) ? originalSize : (((Object)(object)originalFont != (Object)null && originalFont.fontSize > 0) ? originalFont.fontSize : 16));
			bool flag = false;
			if (!Skip)
			{
				translated = ((TextProvider == null) ? null : TextProvider(instance.Language));
				flag = translated != null || instance.Language.TryTranslate(source, out translated);
			}
			if (flag && instance.Fonts.TryChoose(instance.Language, originalFont, size, originalStyle, translated, out var choice))
			{
				Apply(choice);
				display = translated;
				return display;
			}
		}
		catch (Exception ex)
		{
			Plugin.Instance.Warn("Menu localization failed for " + source + ": " + ex.Message);
		}
		Restore(restoreText: false);
		display = source;
		return source;
	}

	private void Apply(FontChoice choice)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Expected O, but got Unknown
		ScaleFactor = choice.Scale;
		mesh.font = choice.Font;
		mesh.fontSize = choice.Size;
		mesh.fontStyle = choice.Style;
		mesh.characterSize = BaseScale * ScaleFactor;
		if ((Object)(object)textRenderer == (Object)null)
		{
			return;
		}
		if ((Object)(object)choice.Font == (Object)(object)originalFont)
		{
			textRenderer.sharedMaterial = originalMaterial;
			return;
		}
		if ((Object)(object)localMaterial == (Object)null)
		{
			localMaterial = new Material(((Object)(object)originalMaterial != (Object)null) ? originalMaterial : choice.Font.material);
		}
		textRenderer.sharedMaterial = localMaterial;
		UpdateAtlas();
	}

	private void OnTextureRebuilt(Font font)
	{
		if ((Object)(object)mesh != (Object)null && (Object)(object)font == (Object)(object)mesh.font && (Object)(object)font != (Object)(object)originalFont)
		{
			UpdateAtlas();
		}
	}

	private void UpdateAtlas()
	{
		if (!((Object)(object)mesh == (Object)null) && !((Object)(object)mesh.font == (Object)null) && !((Object)(object)mesh.font.material == (Object)null) && !((Object)(object)localMaterial == (Object)null))
		{
			Texture mainTexture = mesh.font.material.mainTexture;
			if ((Object)(object)mainTexture != (Object)null)
			{
				mainTexture.filterMode = (FilterMode)0;
			}
			localMaterial.mainTexture = mainTexture;
			if ((Object)(object)textRenderer != (Object)null && (Object)(object)textRenderer.sharedMaterial != (Object)null && (Object)(object)textRenderer.sharedMaterial != (Object)(object)originalMaterial)
			{
				textRenderer.sharedMaterial.mainTexture = mainTexture;
			}
		}
	}

	internal void Refresh()
	{
		if (source != null && !((Object)(object)mesh == (Object)null))
		{
			GUIText component = ((Component)this).GetComponent<GUIText>();
			if ((Object)(object)component != (Object)null)
			{
				component.SetText(source);
			}
			else
			{
				mesh.text = Translate(source);
			}
			if (!((Component)this).gameObject.activeInHierarchy)
			{
				mesh.text = display ?? source;
			}
			CaptureRendered();
		}
	}

	internal void CaptureRendered()
	{
		if ((Object)(object)mesh != (Object)null)
		{
			rendered = mesh.text;
		}
	}

	private void LateUpdate()
	{
		RefreshChangedText();
	}

	internal void RefreshChangedText()
	{
		if (Standalone && !((Object)(object)mesh == (Object)null) && !(mesh.text == rendered))
		{
			mesh.text = Translate(mesh.text);
			CaptureRendered();
		}
	}

	internal void Restore(bool restoreText)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)mesh == (Object)null)
		{
			return;
		}
		mesh.font = originalFont;
		mesh.fontSize = originalSize;
		mesh.fontStyle = originalStyle;
		mesh.characterSize = BaseScale;
		ScaleFactor = 1f;
		if ((Object)(object)textRenderer != (Object)null)
		{
			textRenderer.sharedMaterial = originalMaterial;
		}
		if (restoreText && source != null)
		{
			GUIText component = ((Component)this).GetComponent<GUIText>();
			if ((Object)(object)component != (Object)null)
			{
				component.SetText(source);
			}
			mesh.text = source;
		}
	}

	private void OnDestroy()
	{
		Font.textureRebuilt -= OnTextureRebuilt;
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.Labels.Remove(this);
		}
		if ((Object)(object)localMaterial != (Object)null)
		{
			Object.Destroy((Object)(object)localMaterial);
		}
	}
}
internal static class MenuTextMeshes
{
	internal static LocalizedMenuLabel Bind(GameObject obj, bool refresh)
	{
		Plugin instance = Plugin.Instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)obj.GetComponent<MenuTextMenuItem>() != (Object)null)
		{
			return null;
		}
		LocalizedMenuLabel component = obj.GetComponent<LocalizedMenuLabel>();
		if ((Object)(object)component != (Object)null)
		{
			return component;
		}
		List<string> list = new List<string>();
		Transform val = obj.transform;
		while ((Object)(object)val != (Object)null)
		{
			list.Insert(0, ((Object)val).name);
			val = val.parent;
		}
		if (!MenuTextPolicy.MatchesPath(instance.Language.TextMeshPaths, string.Join("/", list.ToArray())))
		{
			return null;
		}
		TextMesh component2 = obj.GetComponent<TextMesh>();
		if ((Object)(object)component2 == (Object)null)
		{
			return null;
		}
		LocalizedMenuLabel orCreate = LocalizedMenuLabel.GetOrCreate(obj);
		orCreate.Standalone = true;
		orCreate.ResetSource(component2.text);
		if (refresh)
		{
			orCreate.Refresh();
		}
		return orCreate;
	}

	internal static void Scan(GameObject root)
	{
		TextMesh[] componentsInChildren = root.GetComponentsInChildren<TextMesh>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			Bind(((Component)componentsInChildren[i]).gameObject, refresh: true);
		}
	}

	internal static void ScanLoadedScenes()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < SceneManager.sceneCount; i++)
		{
			Scene sceneAt = SceneManager.GetSceneAt(i);
			if (((Scene)(ref sceneAt)).isLoaded)
			{
				GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).GetRootGameObjects();
				for (int j = 0; j < rootGameObjects.Length; j++)
				{
					Scan(rootGameObjects[j]);
				}
			}
		}
	}
}
[HarmonyPatch(typeof(MenuStateMachine), "SetState", new Type[] { typeof(int) })]
internal static class MenuStateTextPatch
{
	private static void Postfix(MenuStateMachine __instance)
	{
		MenuTextMeshes.Scan(((Component)__instance).gameObject);
	}
}
[HarmonyPatch(typeof(GUIText), "OnEnable")]
internal static class MenuStaticTextEnablePatch
{
	private static void Postfix(GUIText __instance)
	{
		MenuTextMeshes.Bind(((Component)__instance).gameObject, refresh: true);
	}
}
[HarmonyPatch(typeof(MenuOptions), "SetItemTextReplayTutorial")]
internal static class ReplayTutorialDisplayPatch
{
	private static bool Prefix(MenuTextMenu ___m_menuOptions)
	{
		if ((Object)(object)___m_menuOptions == (Object)null)
		{
			return true;
		}
		bool startTutorialHACK = Singleton<SystemUnlocks>.Get.GetStartTutorialHACK();
		___m_menuOptions.SetItemString(4, MenuTextPolicy.ReplayTutorialText(startTutorialHACK, SystemTutorial.GetShouldStartTutorial()));
		return false;
	}
}
internal static class MenuTextPolicy
{
	internal static string ReplayTutorialText(bool requested, bool allowed)
	{
		if (requested)
		{
			if (!allowed)
			{
				return "Replay Tutorial: ON (unavailable)";
			}
			return "Replay Tutorial: ON";
		}
		return "Replay Tutorial: OFF";
	}

	internal static bool MatchesPath(IEnumerable<string> paths, string hierarchy)
	{
		hierarchy = hierarchy.Replace("(Clone)", "");
		hierarchy = Regex.Replace(hierarchy, "(?<=MenuJoinPlayer)[1-4](?=/)", "*");
		foreach (string path in paths)
		{
			if (hierarchy == path || hierarchy.EndsWith("/" + path, StringComparison.Ordinal))
			{
				return true;
			}
		}
		return false;
	}
}
[HarmonyPatch(typeof(TextWrapper), "WrapText")]
internal static class MenuChineseWrapPatch
{
	private static bool Prefix(string input, float width, TextMesh ___textMesh, ref string __result)
	{
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Plugin.Instance == (Object)null || !Plugin.Instance.Language.UsesChineseTextRules || (Object)(object)___textMesh == (Object)null || !ChineseTextWrapper.ContainsChinese(input) || (Object)(object)((Component)___textMesh).GetComponent<LocalizedMenuLabel>() == (Object)null || ((Object)(object)((Component)___textMesh).GetComponentInParent<MenuLibrary>() == (Object)null && (Object)(object)((Component)___textMesh).GetComponentInParent<MenuDeitySelectPlayer>() == (Object)null && (Object)(object)((Component)___textMesh).GetComponentInParent<GUIStore>() == (Object)null && (Object)(object)((Component)___textMesh).GetComponentInParent<MenuPrompt>() == (Object)null))
		{
			return true;
		}
		TextMesh mesh = ___textMesh;
		Font font = mesh.font;
		if ((Object)(object)font == (Object)null)
		{
			return true;
		}
		int size = ((mesh.fontSize > 0) ? mesh.fontSize : font.fontSize);
		if (font.dynamic)
		{
			font.RequestCharactersInTexture(input, size, mesh.fontStyle);
		}
		float scale = mesh.characterSize * 0.1f * Mathf.Abs(((Component)mesh).transform.lossyScale.x);
		CharacterInfo val = default(CharacterInfo);
		__result = ChineseTextWrapper.Wrap(input, width, (char ch) => (!font.GetCharacterInfo(ch, ref val, size, mesh.fontStyle)) ? ((float)size * scale) : ((float)((CharacterInfo)(ref val)).advance * scale));
		return false;
	}
}
internal sealed class NumericTranslation
{
	private static readonly Regex Tokens = new Regex("\\{([0-9])\\}");

	internal readonly string Source;

	private readonly string target;

	private readonly Regex pattern;

	private NumericTranslation(string source, string target, string pattern)
	{
		Source = source;
		this.target = target;
		this.pattern = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
	}

	internal static NumericTranslation Create(string source, string target)
	{
		MatchCollection matchCollection = Tokens.Matches(source);
		if (matchCollection.Count == 0)
		{
			return null;
		}
		HashSet<string> hashSet = new HashSet<string>();
		StringBuilder stringBuilder = new StringBuilder("\\A");
		int num = 0;
		foreach (Match item in matchCollection)
		{
			stringBuilder.Append(Regex.Escape(source.Substring(num, item.Index - num)));
			string value = item.Groups[1].Value;
			stringBuilder.Append(hashSet.Add(value) ? ("(?<n" + value + ">[0-9]+)") : ("\\k<n" + value + ">"));
			num = item.Index + item.Length;
		}
		stringBuilder.Append(Regex.Escape(source.Substring(num))).Append("\\z");
		HashSet<string> hashSet2 = new HashSet<string>();
		foreach (Match item2 in Tokens.Matches(target))
		{
			hashSet2.Add(item2.Groups[1].Value);
		}
		if (!hashSet.SetEquals(hashSet2))
		{
			throw new FormatException("Numeric translation must preserve all source placeholders without adding new ones.");
		}
		return new NumericTranslation(source, target, stringBuilder.ToString());
	}

	internal bool TryTranslate(string source, out string translated)
	{
		Match match = pattern.Match(source);
		translated = null;
		if (match.Success)
		{
			translated = Tokens.Replace(target, (Match token) => match.Groups["n" + token.Groups[1].Value].Value);
		}
		return match.Success;
	}
}
internal static class OnlineIntegration
{
	internal static readonly string[] MenuMethods = new string[11]
	{
		"ItemText", "ShowPlayOnline", "ShowHostType", "ShowServerList", "ShowServerListResults", "JoinEntry", "ShowLobby", "ConfirmCloseLobby", "ShowError", "LobbyText",
		"UpdatePanel"
	};

	internal static readonly string[] OverlayMethods = new string[6] { "Draw", "StatusLine", "DrawWindow", "DrawIdle", "DrawHost", "DrawClient" };

	internal static void Install(Harmony harmony, Action<string> warn)
	{
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Expected O, but got Unknown
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("CrawlOnline.UI.OnlineMenu");
		if ((object)type == null)
		{
			return;
		}
		LocalizedMenuLabel.ExternalProvider = Provider;
		Type type2 = AccessTools.TypeByName("CrawlOnline.UI.Overlay");
		Type[] array = new Type[2] { type, type2 };
		foreach (Type type3 in array)
		{
			if ((object)type3 == null)
			{
				continue;
			}
			string[] array2 = (((object)type3 == type) ? MenuMethods : OverlayMethods);
			foreach (string text in array2)
			{
				MethodInfo methodInfo = AccessTools.Method(type3, text, (Type[])null, (Type[])null);
				if ((object)methodInfo == null)
				{
					warn("CrawlOnline UI method unavailable: " + type3.Name + "." + text);
					continue;
				}
				try
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(OnlineIntegration), "Transpiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				}
				catch (Exception ex)
				{
					warn("CrawlOnline localization unavailable for " + text + ": " + ex.Message);
				}
			}
		}
		MethodInfo methodInfo2 = AccessTools.Method(type, "ShowError", (Type[])null, (Type[])null);
		if ((object)methodInfo2 == null)
		{
			return;
		}
		try
		{
			harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(OnlineIntegration), "ErrorPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		catch (Exception ex2)
		{
			warn("CrawlOnline error-message localization unavailable: " + ex2.Message);
		}
	}

	internal static Func<LanguagePack, string> Provider(string source)
	{
		OnlineRecipe recipe = OnlineText.Find(source);
		if (recipe != null)
		{
			return (LanguagePack p) => (!(p.Id == "en")) ? recipe.Render(p) : null;
		}
		return null;
	}

	private static void ErrorPrefix(ref string message)
	{
		message = OnlineText.Message(message);
	}

	private static MethodInfo Wrapper(MethodBase method)
	{
		Type declaringType = method.DeclaringType;
		string text = null;
		ParameterInfo[] parameters = method.GetParameters();
		if ((object)declaringType == typeof(StringBuilder))
		{
			if (method is ConstructorInfo && parameters.Length == 0)
			{
				text = "Create";
			}
			if (method.Name == "Append" && parameters.Length == 1)
			{
				text = (((object)parameters[0].ParameterType == typeof(string)) ? "Append" : (((object)parameters[0].ParameterType == typeof(char)) ? "AppendChar" : null));
			}
			if (method.Name == "ToString" && parameters.Length == 0)
			{
				text = "Finish";
			}
		}
		if ((object)declaringType == typeof(object) && method.Name == "ToString" && parameters.Length == 0)
		{
			text = "ObjectString";
		}
		if ((object)declaringType == typeof(string) && method.Name == "Concat")
		{
			if (parameters.Length == 1)
			{
				text = (((object)parameters[0].ParameterType == typeof(string[])) ? "ConcatStrings" : (((object)parameters[0].ParameterType == typeof(object[])) ? "ConcatArray" : null));
			}
			else if ((object)parameters[0].ParameterType == typeof(string))
			{
				text = ((parameters.Length == 2) ? "ConcatTwo" : ((parameters.Length == 3) ? "ConcatThree" : ((parameters.Length == 4) ? "ConcatFour" : null)));
			}
			else if (parameters.Length == 3)
			{
				text = "ConcatObjects";
			}
		}
		if (text != null)
		{
			return AccessTools.Method(typeof(OnlineText), text, (Type[])null, (Type[])null);
		}
		if ((object)declaringType == typeof(TextMesh) && method.Name == "set_text")
		{
			return AccessTools.Method(typeof(OnlineOverlay), "SetText", (Type[])null, (Type[])null);
		}
		if ((object)declaringType == typeof(GUILayout) || (object)declaringType == typeof(GUI))
		{
			Type[] array = new Type[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				array[i] = parameters[i].ParameterType;
			}
			return typeof(OnlineOverlay).GetMethod((((object)declaringType == typeof(GUI)) ? "Gui" : "") + method.Name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, array, null);
		}
		return null;
	}

	internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		foreach (CodeInstruction original in instructions)
		{
			CodeInstruction val = new CodeInstruction(original);
			MethodBase method = val.operand as MethodBase;
			if ((object)method != null && (val.opcode == OpCodes.Call || val.opcode == OpCodes.Callvirt || val.opcode == OpCodes.Newobj))
			{
				MethodInfo methodInfo = Wrapper(method);
				if ((object)methodInfo != null)
				{
					val.opcode = OpCodes.Call;
					val.operand = methodInfo;
				}
			}
			yield return val;
			string text = null;
			if (original.opcode == OpCodes.Ldstr)
			{
				text = "Literal";
			}
			if (original.operand is FieldInfo fieldInfo && (object)fieldInfo.FieldType == typeof(string) && (original.opcode == OpCodes.Ldfld || original.opcode == OpCodes.Ldsfld))
			{
				string fullName = fieldInfo.DeclaringType.FullName;
				if ((fullName == "CrawlOnline.Online" && (fieldInfo.Name == "LastError" || fieldInfo.Name == "Status")) || (fullName == "CrawlOnline.Net.LobbyBrowser" && fieldInfo.Name == "Error"))
				{
					text = "Message";
				}
				if (fullName == "CrawlOnline.Net.LobbyEntry" && fieldInfo.Name == "State")
				{
					text = "Literal";
				}
			}
			if ((object)method != null && method.DeclaringType.FullName == "CrawlOnline.Net.SteamLobby" && method.Name == "TypeName")
			{
				text = "Literal";
			}
			if ((object)method != null && method.DeclaringType.FullName == "CrawlOnline.Net.LobbyBrowser" && method.Name == "get_Error")
			{
				text = "Message";
			}
			if ((object)method != null && (object)method.DeclaringType == typeof(List<string>.Enumerator) && method.Name == "get_Current")
			{
				text = "Roster";
			}
			if (text != null)
			{
				yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(OnlineText), text, (Type[])null, (Type[])null));
			}
		}
	}
}
internal static class OnlineOverlay
{
	private sealed class StyleEntry
	{
		internal GUIStyle Original;

		internal GUIStyle Display;

		internal Font Font;
	}

	private static readonly List<StyleEntry> styles = new List<StyleEntry>();

	internal static void Clear()
	{
		styles.Clear();
	}

	private static string Prepare(string source, ref GUIStyle style)
	{
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Expected O, but got Unknown
		Plugin instance = Plugin.Instance;
		OnlineRecipe onlineRecipe = OnlineText.Find(source);
		if ((Object)(object)instance == (Object)null || onlineRecipe == null || instance.Language.Id == "en")
		{
			return source;
		}
		string text = onlineRecipe.Render(instance.Language);
		if (text == source)
		{
			return source;
		}
		int num = ((style.fontSize > 0) ? style.fontSize : 12);
		if (!instance.Fonts.TryChooseGui(instance.Language, style.font ?? GUI.skin.font, num, style.fontStyle, text, out var font))
		{
			return source;
		}
		foreach (StyleEntry style2 in styles)
		{
			if (style2.Original == style && (Object)(object)style2.Font == (Object)(object)font)
			{
				style = style2.Display;
				return text;
			}
		}
		GUIStyle val = new GUIStyle(style);
		val.font = font;
		val.fontSize = num;
		styles.Add(new StyleEntry
		{
			Original = style,
			Display = val,
			Font = font
		});
		style = val;
		return text;
	}

	internal static void Label(string text, params GUILayoutOption[] options)
	{
		GUIStyle style = GUI.skin.label;
		text = Prepare(text, ref style);
		GUILayout.Label(text, style, options);
	}

	internal static void Label(string text, GUIStyle style, params GUILayoutOption[] options)
	{
		text = Prepare(text, ref style);
		GUILayout.Label(text, style, options);
	}

	internal static bool Button(string text, params GUILayoutOption[] options)
	{
		GUIStyle style = GUI.skin.button;
		text = Prepare(text, ref style);
		return GUILayout.Button(text, style, options);
	}

	internal static bool Toggle(bool value, string text, params GUILayoutOption[] options)
	{
		GUIStyle style = GUI.skin.toggle;
		text = Prepare(text, ref style);
		return GUILayout.Toggle(value, text, style, options);
	}

	internal static bool Toggle(bool value, string text, GUIStyle style, params GUILayoutOption[] options)
	{
		text = Prepare(text, ref style);
		return GUILayout.Toggle(value, text, style, options);
	}

	internal static void GuiLabel(Rect position, string text, GUIStyle style)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		text = Prepare(text, ref style);
		GUI.Label(position, text, style);
	}

	internal static Rect Window(int id, Rect rect, WindowFunction function, string text, params GUILayoutOption[] options)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		GUIStyle style = GUI.skin.window;
		text = Prepare(text, ref style);
		return GUILayout.Window(id, rect, function, text, style, options);
	}

	internal static void SetText(TextMesh mesh, string source)
	{
		mesh.text = source;
		if (!((Object)(object)mesh == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null))
		{
			LocalizedMenuLabel orCreate = LocalizedMenuLabel.GetOrCreate(((Component)mesh).gameObject);
			orCreate.ResetSource(source);
			orCreate.Refresh();
		}
	}
}
internal sealed class OnlineRecipe
{
	internal readonly string Source;

	private readonly bool literal;

	private readonly OnlineRecipe[] parts;

	internal OnlineRecipe(string source, bool literal, OnlineRecipe[] parts)
	{
		Source = new string((source ?? "").ToCharArray());
		this.literal = literal;
		this.parts = parts;
	}

	internal string Render(LanguagePack pack)
	{
		if (pack.Id == "en")
		{
			return Source;
		}
		if (parts != null)
		{
			StringBuilder stringBuilder = new StringBuilder();
			OnlineRecipe[] array = parts;
			foreach (OnlineRecipe onlineRecipe in array)
			{
				stringBuilder.Append(onlineRecipe.Render(pack));
			}
			return stringBuilder.ToString();
		}
		if (!literal || !pack.TryTranslate("@online:" + Source, out var translated))
		{
			return Source;
		}
		return translated;
	}
}
internal static class OnlineText
{
	private sealed class Trace
	{
		internal readonly List<OnlineRecipe> Parts = new List<OnlineRecipe>();
	}

	private static readonly WeakIdentityMap<OnlineRecipe> strings = new WeakIdentityMap<OnlineRecipe>();

	private static readonly WeakIdentityMap<Trace> builders = new WeakIdentityMap<Trace>();

	internal static OnlineRecipe Find(string source)
	{
		return strings.Get(source);
	}

	private static OnlineRecipe Capture(string source)
	{
		return Find(source) ?? new OnlineRecipe(source, literal: false, null);
	}

	internal static string Literal(string source)
	{
		if (string.IsNullOrEmpty(source))
		{
			return source;
		}
		string text = new string(source.ToCharArray());
		strings.Set(text, new OnlineRecipe(source, literal: true, null));
		return text;
	}

	private static void Remember(string source, OnlineRecipe[] parts)
	{
		if (!string.IsNullOrEmpty(source))
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (OnlineRecipe onlineRecipe in parts)
			{
				stringBuilder.Append(onlineRecipe.Source);
			}
			if (stringBuilder.ToString() == source)
			{
				strings.Set(source, new OnlineRecipe(source, literal: false, parts));
			}
		}
	}

	internal static StringBuilder Create()
	{
		StringBuilder stringBuilder = new StringBuilder();
		builders.Set(stringBuilder, new Trace());
		return stringBuilder;
	}

	internal static StringBuilder Append(StringBuilder b, string s)
	{
		b.Append(s);
		builders.Get(b)?.Parts.Add(Capture(s));
		return b;
	}

	internal static StringBuilder AppendChar(StringBuilder b, char c)
	{
		b.Append(c);
		builders.Get(b)?.Parts.Add(Capture(c.ToString()));
		return b;
	}

	internal static string Finish(StringBuilder b)
	{
		string text = b.ToString();
		Trace trace = builders.Get(b);
		if (trace != null)
		{
			Remember(text, trace.Parts.ToArray());
		}
		return text;
	}

	internal static string ObjectString(object o)
	{
		if (o is StringBuilder b)
		{
			return Finish(b);
		}
		return o.ToString();
	}

	private static string Join(string source, object[] args)
	{
		OnlineRecipe[] array = new OnlineRecipe[args.Length];
		for (int i = 0; i < args.Length; i++)
		{
			object obj = args[i];
			if (obj != null && !(obj is string) && !obj.GetType().IsPrimitive && !(obj is decimal))
			{
				return source;
			}
			array[i] = Capture((obj == null) ? "" : Convert.ToString(obj, CultureInfo.CurrentCulture));
		}
		Remember(source, array);
		return source;
	}

	internal static string ConcatTwo(string a, string b)
	{
		return Join(a + b, new object[2] { a, b });
	}

	internal static string ConcatThree(string a, string b, string c)
	{
		return Join(a + b + c, new object[3] { a, b, c });
	}

	internal static string ConcatFour(string a, string b, string c, string d)
	{
		return Join(a + b + c + d, new object[4] { a, b, c, d });
	}

	internal static string ConcatStrings(string[] a)
	{
		return Join(string.Concat(a), a);
	}

	internal static string ConcatObjects(object a, object b, object c)
	{
		return Join(string.Concat(a, b, c), new object[3] { a, b, c });
	}

	internal static string ConcatArray(object[] a)
	{
		return Join(string.Concat(a), a);
	}

	internal static string Roster(string source)
	{
		if (source == null)
		{
			return null;
		}
		Match match = Regex.Match(source, "\\A(.*)(  [0-9]+ ms  \\()([0-9]+)( controllers?)(\\))\\z", RegexOptions.Singleline);
		if (!match.Success)
		{
			return source;
		}
		return ConcatStrings(new string[5]
		{
			match.Groups[1].Value,
			match.Groups[2].Value,
			match.Groups[3].Value,
			Literal(match.Groups[4].Value),
			match.Groups[5].Value
		});
	}

	internal static string Message(string source)
	{
		if (source == null || Find(source) != null)
		{
			return source;
		}
		string[] array = new string[7] { "Host failed: ", "UDP host failed: ", "Connect failed: ", "lobby creation failed: ", "could not enter lobby (", "Rejected: ", "Host closed the session: " };
		foreach (string text in array)
		{
			if (source.StartsWith(text, StringComparison.Ordinal))
			{
				string text2 = source.Substring(text.Length);
				if (text == "Rejected: " || text == "Host closed the session: ")
				{
					text2 = Message(text2);
				}
				return ConcatTwo(Literal(text), text2);
			}
		}
		if (source.StartsWith("Hosting (Steam, ", StringComparison.Ordinal) && source.EndsWith(" lobby)", StringComparison.Ordinal))
		{
			return ConcatThree(Literal("Hosting (Steam, "), Literal(source.Substring(16, source.Length - 23)), Literal(" lobby)"));
		}
		if (source.StartsWith("Hosting (UDP port ", StringComparison.Ordinal) && source.EndsWith(")", StringComparison.Ordinal))
		{
			return ConcatThree(Literal("Hosting (UDP port "), source.Substring(18, source.Length - 19), ")");
		}
		if (source.StartsWith("Joining ", StringComparison.Ordinal) && source.EndsWith("...", StringComparison.Ordinal))
		{
			return ConcatThree(Literal("Joining "), source.Substring(8, source.Length - 11), "...");
		}
		if (source.StartsWith("Crawl Online version mismatch: host has ", StringComparison.Ordinal))
		{
			int num = source.IndexOf(", you have ", "Crawl Online version mismatch: host has ".Length, StringComparison.Ordinal);
			if (num >= 0)
			{
				string text3 = source.Substring(num + 11);
				string text4 = ". Use the same version.";
				if (text3.EndsWith(text4, StringComparison.Ordinal))
				{
					text3 = ConcatTwo(text3.Substring(0, text3.Length - text4.Length), Literal(text4));
				}
				return ConcatFour(Literal("Crawl Online version mismatch: host has "), source.Substring("Crawl Online version mismatch: host has ".Length, num - "Crawl Online version mismatch: host has ".Length), Literal(", you have "), text3);
			}
		}
		return Literal(source);
	}
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("crawl.localization", "Crawl Localization", "0.13.0")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Id = "crawl.localization";

	internal static Plugin Instance;

	internal LanguagePack Language;

	internal FontService Fonts;

	internal readonly HashSet<LocalizedMenuLabel> Labels = new HashSet<LocalizedMenuLabel>();

	private readonly HashSet<string> warnings = new HashSet<string>();

	private ConfigEntry<string> language;

	private ConfigEntry<KeyCode> reloadKey;

	private Harmony harmony;

	private string folder;

	private void Awake()
	{
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Expected O, but got Unknown
		Instance = this;
		folder = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
		language = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Language", "zh-Hans", "Language folder under Languages, e.g. zh-Hans, es or ru. Use en for original text. zh-TW selects zh-Hant. Missing entries or packs keep original game text.");
		reloadKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ReloadKey", (KeyCode)289, "Reload configuration and language tables. Restart to replace font files.");
		Fonts = new FontService(Warn, delegate(string message)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		});
		LoadLanguage();
		harmony = new Harmony("crawl.localization");
		harmony.PatchAll(typeof(Plugin).Assembly);
		OnlineIntegration.Install(harmony, Warn);
		SceneManager.sceneLoaded += OnSceneLoaded;
	}

	private void Start()
	{
		MenuTextMeshes.ScanLoadedScenes();
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		MenuTextMeshes.ScanLoadedScenes();
	}

	private void Update()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		if ((int)reloadKey.Value == 0 || !Input.GetKeyDown(reloadKey.Value))
		{
			return;
		}
		try
		{
			((BaseUnityPlugin)this).Config.Reload();
			LoadLanguage();
			MenuTextMeshes.ScanLoadedScenes();
			foreach (LocalizedMenuLabel item in new List<LocalizedMenuLabel>(Labels))
			{
				if ((Object)(object)item != (Object)null)
				{
					item.Refresh();
				}
			}
		}
		catch (Exception ex)
		{
			Warn("Reload failed: " + ex.Message);
		}
	}

	private void LoadLanguage()
	{
		LanguagePack languagePack = (Language = LanguagePack.Load(folder, language.Value, Warn));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Language=" + languagePack.Id + "; menu translations=" + languagePack.Count + "; font mappings=" + languagePack.Fonts.Count));
	}

	internal void Warn(string message)
	{
		if (warnings.Add(message))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)message);
		}
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		foreach (LocalizedMenuLabel item in new List<LocalizedMenuLabel>(Labels))
		{
			if ((Object)(object)item != (Object)null)
			{
				item.Restore(restoreText: true);
				Object.Destroy((Object)(object)item);
			}
		}
		LocalizedMenuLabel.ExternalProvider = null;
		OnlineOverlay.Clear();
		if (Fonts != null)
		{
			Fonts.Dispose();
		}
		Instance = null;
	}
}
[HarmonyPatch(typeof(MenuPrompt), "Initialise")]
internal static class PausePromptPatch
{
	private static void Prefix(string title, string text, GUIText ___m_textTitle, GUIText ___m_textText)
	{
		if ((Object)(object)Plugin.Instance == (Object)null)
		{
			return;
		}
		PausePlayerText snapshot = null;
		bool joined = false;
		if (title != null && title.StartsWith("_P", StringComparison.Ordinal) && (title.EndsWith("_DROPPED_IN_", StringComparison.Ordinal) || title.EndsWith("_DROPPED_OUT_", StringComparison.Ordinal)))
		{
			joined = title.EndsWith("_DROPPED_IN_", StringComparison.Ordinal);
			for (int i = 0; i < SystemPlayers.GetMaxPlayers(); i++)
			{
				PlayerData player = SystemPlayers.GetPlayer(i);
				if (player == null)
				{
					continue;
				}
				Deity val = ((joined && player.GameData != null) ? player.GameData.GetDeity() : null);
				if (!joined || val != null)
				{
					PausePlayerText pausePlayerText = new PausePlayerText(i + 1, player.Name, player.Level.ToString(), (val == null) ? "" : val.GetName(), (val == null) ? "" : val.GetText());
					if (pausePlayerText.Matches(title, text, joined))
					{
						snapshot = pausePlayerText;
						break;
					}
				}
			}
		}
		Bind(___m_textTitle, title, (snapshot == null) ? null : ((Func<LanguagePack, string>)((LanguagePack pack) => snapshot.Title(pack, joined))));
		Bind(___m_textText, text, (snapshot == null) ? null : ((Func<LanguagePack, string>)((LanguagePack pack) => snapshot.Body(pack, joined))));
	}

	private static void Bind(GUIText gui, string source, Func<LanguagePack, string> provider)
	{
		if (!((Object)(object)gui == (Object)null))
		{
			LocalizedMenuLabel orCreate = LocalizedMenuLabel.GetOrCreate(((Component)gui).gameObject);
			if (!((Object)(object)orCreate == (Object)null))
			{
				orCreate.TextProvider = provider;
				orCreate.ResetSource(source);
			}
		}
	}
}
[HarmonyPatch(typeof(MenuEvolvePlayer), "Initialise")]
internal static class EvolutionBindPatch
{
	private static void Postfix(MenuEvolvePlayer __instance)
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			MenuTextMeshes.Scan(((Component)__instance).gameObject);
		}
	}
}
[HarmonyPatch(typeof(MenuEvolvePlayer), "UpdateMenuDescriptionText")]
internal static class EvolutionDescriptionPatch
{
	private static void Postfix(TextMesh ___m_textMenuDescription)
	{
		if (!((Object)(object)Plugin.Instance == (Object)null) && !((Object)(object)___m_textMenuDescription == (Object)null))
		{
			LocalizedMenuLabel localizedMenuLabel = MenuTextMeshes.Bind(((Component)___m_textMenuDescription).gameObject, refresh: true);
			if ((Object)(object)localizedMenuLabel != (Object)null)
			{
				localizedMenuLabel.RefreshChangedText();
			}
		}
	}
}
[HarmonyPatch(typeof(MenuWin), "Start")]
internal static class ResultsTextPatch
{
	private static void Postfix(MenuWin __instance, bool ___m_heroWon, TextMesh ___m_winText, TextMesh ___m_bossKillerText)
	{
		if ((Object)(object)Plugin.Instance == (Object)null)
		{
			return;
		}
		if (___m_heroWon && (Object)(object)___m_bossKillerText != (Object)null)
		{
			LocalizedMenuLabel.GetOrCreate(((Component)___m_bossKillerText).gameObject).RefreshDirect(___m_bossKillerText.text.Trim(), null);
		}
		MenuTextMeshes.Scan(((Component)__instance).gameObject);
		if ((Object)(object)___m_winText != (Object)null)
		{
			LocalizedMenuLabel localizedMenuLabel = MenuTextMeshes.Bind(((Component)___m_winText).gameObject, refresh: true);
			if ((Object)(object)localizedMenuLabel != (Object)null)
			{
				localizedMenuLabel.RefreshChangedText();
			}
		}
	}
}
[HarmonyPatch(typeof(MenuTextMenuItem), "Start")]
internal static class BossEntranceTextPatch
{
	private static void Postfix(MenuTextMenuItem __instance)
	{
		if (!((Object)(object)Plugin.Instance == (Object)null))
		{
			string text = ((Object)((Component)__instance).gameObject).name.Replace("(Clone)", "");
			if ((!(text != "EffectBossName") || !(text != "EffectBossAttemptText")) && !((Object)(object)((Component)__instance).GetComponent<TextMesh>() == (Object)null))
			{
				LocalizedMenuLabel orCreate = LocalizedMenuLabel.GetOrCreate(((Component)__instance).gameObject);
				orCreate.Standalone = true;
				orCreate.RefreshChangedText();
			}
		}
	}
}
internal sealed class PausePlayerText
{
	internal readonly string Number;

	internal readonly string Name;

	internal readonly string Level;

	internal readonly string DeityName;

	internal readonly string DeityText;

	internal PausePlayerText(int number, string name, string level, string deityName, string deityText)
	{
		Number = number.ToString(CultureInfo.InvariantCulture);
		Name = name;
		Level = level;
		DeityName = deityName;
		DeityText = deityText;
	}

	internal string OriginalTitle(bool joined)
	{
		return "_P" + Number + "_'" + Name.ToUpper() + "'_DROPPED_" + (joined ? "IN_" : "OUT_");
	}

	internal string OriginalBody(bool joined)
	{
		if (!joined)
		{
			return "Player " + Number + " (" + Name + ") will be replaced with a bot.";
		}
		return "Joined as Player " + Number + " with alias: '" + Name + "'\n\nHero level: " + Level + "\nWorships: " + DeityName + "\n(" + DeityText + ")";
	}

	internal bool Matches(string title, string body, bool joined)
	{
		if (title == OriginalTitle(joined))
		{
			return body == OriginalBody(joined);
		}
		return false;
	}

	internal string Title(LanguagePack pack, bool joined)
	{
		return Format(pack, joined ? "@progression.join-title:{0}:{1}" : "@progression.leave-title:{0}:{1}", Number, Name.ToUpper());
	}

	internal string Body(LanguagePack pack, bool joined)
	{
		if (!joined)
		{
			return Format(pack, "@progression.leave-body:{0}:{1}", Number, Name);
		}
		return Format(pack, "@progression.join-body:{0}:{1}:{2}:{3}:{4}", Number, Name, Level, Term(pack, DeityName), Term(pack, DeityText));
	}

	private static string Term(LanguagePack pack, string source)
	{
		if (!pack.TryTranslate(source, out var translated))
		{
			return source;
		}
		return translated;
	}

	private static string Format(LanguagePack pack, string key, params object[] args)
	{
		if (!pack.TryTranslate(key, out var translated))
		{
			return null;
		}
		try
		{
			return string.Format(CultureInfo.InvariantCulture, translated, args);
		}
		catch (FormatException)
		{
			return null;
		}
	}
}
internal static class ShopDisplay
{
	private static LocalizedMenuLabel Bind(TextMesh mesh, string source)
	{
		if ((Object)(object)mesh == (Object)null || (Object)(object)Plugin.Instance == (Object)null)
		{
			return null;
		}
		ShopTextRecipe recipe = ShopText.Capture(source);
		LocalizedMenuLabel localizedMenuLabel = ((Component)mesh).GetComponent<LocalizedMenuLabel>();
		bool flag = false;
		if (source != null)
		{
			for (int i = 0; i < source.Length; i++)
			{
				if (char.IsLetter(source[i]))
				{
					flag = true;
					break;
				}
			}
		}
		if ((Object)(object)localizedMenuLabel == (Object)null && !flag)
		{
			return null;
		}
		if ((Object)(object)localizedMenuLabel == (Object)null)
		{
			localizedMenuLabel = LocalizedMenuLabel.GetOrCreate(((Component)mesh).gameObject);
		}
		localizedMenuLabel.TextProvider = (LanguagePack pack) => recipe.Translate(pack);
		localizedMenuLabel.ResetSource(source);
		return localizedMenuLabel;
	}

	internal static void SetText(TextMesh mesh, string source)
	{
		mesh.text = source;
		LocalizedMenuLabel localizedMenuLabel = Bind(mesh, source);
		if ((Object)(object)localizedMenuLabel != (Object)null)
		{
			localizedMenuLabel.Refresh();
		}
	}

	internal static void SetGuiText(GUIText gui, string source)
	{
		Bind(((Component)gui).GetComponent<TextMesh>(), source);
		gui.SetText(source);
	}
}
internal sealed class WeakIdentityMap<T> where T : class
{
	private sealed class Entry
	{
		internal WeakReference Key;

		internal T Value;
	}

	private readonly Dictionary<int, List<Entry>> buckets = new Dictionary<int, List<Entry>>();

	private int writes;

	internal T Get(object key)
	{
		if (key == null)
		{
			return null;
		}
		lock (buckets)
		{
			if (!buckets.TryGetValue(RuntimeHelpers.GetHashCode(key), out var value))
			{
				return null;
			}
			foreach (Entry item in value)
			{
				if (item.Key.Target == key)
				{
					return item.Value;
				}
			}
			return null;
		}
	}

	internal void Set(object key, T value)
	{
		if (key == null)
		{
			return;
		}
		lock (buckets)
		{
			if (++writes % 128 == 0)
			{
				foreach (int item in new List<int>(buckets.Keys))
				{
					buckets[item].RemoveAll((Entry e) => !e.Key.IsAlive);
					if (buckets[item].Count == 0)
					{
						buckets.Remove(item);
					}
				}
			}
			int hashCode = RuntimeHelpers.GetHashCode(key);
			if (!buckets.TryGetValue(hashCode, out var value2))
			{
				value2 = new List<Entry>();
				buckets.Add(hashCode, value2);
			}
			foreach (Entry item2 in value2)
			{
				if (item2.Key.Target == key)
				{
					item2.Value = value;
					return;
				}
			}
			value2.Add(new Entry
			{
				Key = new WeakReference(key),
				Value = value
			});
		}
	}
}
internal sealed class ShopTextRecipe
{
	internal readonly string Source;

	private readonly ShopTextRecipe[] parts;

	private readonly string format;

	private readonly object[] arguments;

	private readonly CultureInfo culture;

	private static readonly Regex Structural = new Regex("</?(?:b|i|size|color|material)(?:=[^>]*)?>|\\{[0-9]+(?:[^}]*)?\\}");

	internal ShopTextRecipe(string source, ShopTextRecipe[] parts, string format, object[] arguments)
	{
		Source = new string((source ?? "").ToCharArray());
		this.parts = parts;
		this.format = ((format == null) ? null : new string(format.ToCharArray()));
		this.arguments = arguments;
		culture = CultureInfo.CurrentCulture;
	}

	private static string Term(LanguagePack pack, string source, bool format)
	{
		if (pack.TryTranslate("@shop.il:" + source, out var translated) || pack.TryTranslate(source, out translated))
		{
			return translated;
		}
		string[] array = new string[5] { "@shop.prefix:", "@shop.magic-name:", "@shop.status:", "@shop.magic:", "@shop.stat:" };
		foreach (string text in array)
		{
			if (pack.TryTranslate(text + source, out translated))
			{
				return translated;
			}
		}
		if (!format && !source.EndsWith(".", StringComparison.Ordinal) && pack.TryTranslate(source + ".", out translated))
		{
			return translated;
		}
		string text2 = Structural.Replace(source, "");
		for (int i = 0; i < text2.Length; i++)
		{
			if (char.IsLetter(text2[i]))
			{
				return null;
			}
		}
		return source;
	}

	internal string Translate(LanguagePack pack)
	{
		if (pack.Id == "en")
		{
			return null;
		}
		try
		{
			return Render(pack);
		}
		catch (FormatException)
		{
			return null;
		}
	}

	private string Render(LanguagePack pack)
	{
		if (pack.TryTranslate(Source, out var translated))
		{
			return translated;
		}
		if (parts != null)
		{
			StringBuilder stringBuilder = new StringBuilder();
			ShopTextRecipe[] array = parts;
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Render(pack);
				if (text == null)
				{
					return null;
				}
				stringBuilder.Append(text);
			}
			return stringBuilder.ToString();
		}
		if (format != null)
		{
			string text2 = Term(pack, format, format: true);
			if (text2 == null)
			{
				return null;
			}
			object[] array2 = new object[arguments.Length];
			for (int j = 0; j < array2.Length; j++)
			{
				ShopTextRecipe shopTextRecipe = arguments[j] as ShopTextRecipe;
				array2[j] = ((shopTextRecipe == null) ? arguments[j] : shopTextRecipe.Render(pack));
				if (shopTextRecipe != null && array2[j] == null)
				{
					return null;
				}
			}
			return string.Format(culture, text2, array2);
		}
		return Term(pack, Source, format: false);
	}
}
internal static class ShopText
{
	private sealed class BuilderTrace
	{
		internal readonly List<ShopTextRecipe> Parts = new List<ShopTextRecipe>();

		internal bool Invalid;
	}

	private static readonly WeakIdentityMap<BuilderTrace> builders = new WeakIdentityMap<BuilderTrace>();

	private static readonly WeakIdentityMap<ShopTextRecipe> strings = new WeakIdentityMap<ShopTextRecipe>();

	internal static ShopTextRecipe Capture(string source)
	{
		return strings.Get(source) ?? new ShopTextRecipe(source, null, null, null);
	}

	private static ShopTextRecipe Sequence(string source, ShopTextRecipe[] parts)
	{
		StringBuilder stringBuilder = new StringBuilder();
		foreach (ShopTextRecipe shopTextRecipe in parts)
		{
			stringBuilder.Append(shopTextRecipe.Source);
		}
		if (!(stringBuilder.ToString() == source))
		{
			return null;
		}
		return new ShopTextRecipe(source, parts, null, null);
	}

	private static void Remember(string source, ShopTextRecipe recipe)
	{
		if (!string.IsNullOrEmpty(source) && recipe != null)
		{
			strings.Set(source, recipe);
		}
	}

	private static ShopTextRecipe FormatRecipe(string source, string format, object[] args)
	{
		object[] array = new object[args.Length];
		for (int i = 0; i < args.Length; i++)
		{
			object obj = args[i];
			if (obj is string)
			{
				array[i] = Capture((string)obj);
				continue;
			}
			if (obj == null || obj.GetType().IsPrimitive || obj is decimal)
			{
				array[i] = obj;
				continue;
			}
			return null;
		}
		return new ShopTextRecipe(source, null, format, array);
	}

	private static void Add(StringBuilder builder, ShopTextRecipe node)
	{
		BuilderTrace builderTrace = builders.Get(builder);
		if (builderTrace != null)
		{
			if (node == null)
			{
				builderTrace.Invalid = true;
			}
			else
			{
				builderTrace.Parts.Add(node);
			}
		}
	}

	internal static StringBuilder Create()
	{
		StringBuilder stringBuilder = new StringBuilder();
		builders.Set(stringBuilder, new BuilderTrace());
		return stringBuilder;
	}

	internal static StringBuilder Append(StringBuilder builder, string value)
	{
		builder.Append(value);
		try
		{
			Add(builder, Capture(value));
		}
		catch (Exception)
		{
			Invalidate(builder);
		}
		return builder;
	}

	internal static StringBuilder AppendChar(StringBuilder builder, char value)
	{
		builder.Append(value);
		try
		{
			Add(builder, Capture(value.ToString()));
		}
		catch (Exception)
		{
			Invalidate(builder);
		}
		return builder;
	}

	private static void Invalidate(StringBuilder builder)
	{
		BuilderTrace builderTrace = builders.Get(builder);
		if (builderTrace != null)
		{
			builderTrace.Invalid = true;
		}
	}

	internal static StringBuilder AppendFormatOne(StringBuilder builder, string format, object arg)
	{
		return AppendFormatMany(builder, format, new object[1] { arg });
	}

	internal static StringBuilder AppendFormatTwo(StringBuilder builder, string format, object a, object b)
	{
		return AppendFormatMany(builder, format, new object[2] { a, b });
	}

	internal static StringBuilder AppendFormatMany(StringBuilder builder, string format, object[] args)
	{
		int length = builder.Length;
		builder.AppendFormat(format, args);
		try
		{
			Add(builder, FormatRecipe(builder.ToString(length, builder.Length - length), format, args));
		}
		catch (Exception)
		{
			Invalidate(builder);
		}
		return builder;
	}

	internal static string Finish(StringBuilder builder)
	{
		string text = builder.ToString();
		RecordFinished(builder, text);
		return text;
	}

	internal static string ObjectString(object value)
	{
		string text = value.ToString();
		if (value is StringBuilder builder)
		{
			RecordFinished(builder, text);
		}
		return text;
	}

	private static void RecordFinished(StringBuilder builder, string source)
	{
		try
		{
			BuilderTrace builderTrace = builders.Get(builder);
			if (builderTrace != null && !builderTrace.Invalid)
			{
				Remember(source, Sequence(source, builderTrace.Parts.ToArray()));
			}
		}
		catch (Exception)
		{
		}
	}

	internal static string FormatOne(string format, object arg)
	{
		string text = string.Format(format, arg);
		try
		{
			Remember(text, FormatRecipe(text, format, new object[1] { arg }));
		}
		catch (Exception)
		{
		}
		return text;
	}

	internal static string ConcatTwo(string a, string b)
	{
		return Join(a + b, new object[2] { a, b });
	}

	internal static string ConcatThree(string a, string b, string c)
	{
		return Join(a + b + c, new object[3] { a, b, c });
	}

	internal static string ConcatObjects(object a, object b, object c)
	{
		return Join(string.Concat(a, b, c), new object[3] { a, b, c });
	}

	internal static string ConcatArray(object[] args)
	{
		return Join(string.Concat(args), args);
	}

	private static string Join(string source, object[] args)
	{
		try
		{
			ShopTextRecipe[] array = new ShopTextRecipe[args.Length];
			for (int i = 0; i < args.Length; i++)
			{
				object obj = args[i];
				if (obj != null && !(obj is string) && !obj.GetType().IsPrimitive && !(obj is decimal))
				{
					return source;
				}
				array[i] = Capture((obj == null) ? "" : Convert.ToString(obj, CultureInfo.CurrentCulture));
			}
			Remember(source, Sequence(source, array));
		}
		catch (Exception)
		{
		}
		return source;
	}
}
[HarmonyPatch]
internal static class ShopTextIlPatch
{
	private static readonly Dictionary<MethodBase, MethodInfo> replacements = CreateMap();

	private static void Map(Dictionary<MethodBase, MethodInfo> map, Type type, string name, Type[] args, string replacement)
	{
		MethodBase key = ((name == ".ctor") ? ((MethodBase)type.GetConstructor(args)) : ((MethodBase)type.GetMethod(name, args)));
		map.Add(key, AccessTools.Method(typeof(ShopText), replacement, (Type[])null, (Type[])null));
	}

	private static Dictionary<MethodBase, MethodInfo> CreateMap()
	{
		Dictionary<MethodBase, MethodInfo> dictionary = new Dictionary<MethodBase, MethodInfo>();
		Map(dictionary, typeof(StringBuilder), ".ctor", Type.EmptyTypes, "Create");
		Map(dictionary, typeof(StringBuilder), "Append", new Type[1] { typeof(string) }, "Append");
		Map(dictionary, typeof(StringBuilder), "Append", new Type[1] { typeof(char) }, "AppendChar");
		Map(dictionary, typeof(StringBuilder), "AppendFormat", new Type[2]
		{
			typeof(string),
			typeof(object)
		}, "AppendFormatOne");
		Map(dictionary, typeof(StringBuilder), "AppendFormat", new Type[3]
		{
			typeof(string),
			typeof(object),
			typeof(object)
		}, "AppendFormatTwo");
		Map(dictionary, typeof(StringBuilder), "AppendFormat", new Type[2]
		{
			typeof(string),
			typeof(object[])
		}, "AppendFormatMany");
		Map(dictionary, typeof(StringBuilder), "ToString", Type.EmptyTypes, "Finish");
		Map(dictionary, typeof(object), "ToString", Type.EmptyTypes, "ObjectString");
		Map(dictionary, typeof(string), "Format", new Type[2]
		{
			typeof(string),
			typeof(object)
		}, "FormatOne");
		Map(dictionary, typeof(string), "Concat", new Type[2]
		{
			typeof(string),
			typeof(string)
		}, "ConcatTwo");
		Map(dictionary, typeof(string), "Concat", new Type[3]
		{
			typeof(string),
			typeof(string),
			typeof(string)
		}, "ConcatThree");
		Map(dictionary, typeof(string), "Concat", new Type[3]
		{
			typeof(object),
			typeof(object),
			typeof(object)
		}, "ConcatObjects");
		Map(dictionary, typeof(string), "Concat", new Type[1] { typeof(object[]) }, "ConcatArray");
		return dictionary;
	}

	private static IEnumerable<MethodBase> TargetMethods()
	{
		yield return AccessTools.Method(typeof(WeaponData), "GetDisplayName", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(WeaponData), "GetDescription", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(PropPurchasable), "ShowInfoBox", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(GUIStore), "Show", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(GUIStoreStatsDisplay), "Show", (Type[])null, (Type[])null);
	}

	internal static IEnumerable<CodeInstruction> Rewrite(IEnumerable<CodeInstruction> instructions, bool display)
	{
		foreach (CodeInstruction instruction in instructions)
		{
			MethodBase methodBase = instruction.operand as MethodBase;
			MethodInfo value = null;
			if ((object)methodBase != null && (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt || instruction.opcode == OpCodes.Newobj))
			{
				replacements.TryGetValue(methodBase, out value);
				if (display && methodBase.Equals(AccessTools.PropertySetter(typeof(TextMesh), "text")))
				{
					value = AccessTools.Method(typeof(ShopDisplay), "SetText", (Type[])null, (Type[])null);
				}
				if (display && methodBase.Equals(AccessTools.Method(typeof(GUIText), "SetText", (Type[])null, (Type[])null)))
				{
					value = AccessTools.Method(typeof(ShopDisplay), "SetGuiText", (Type[])null, (Type[])null);
				}
			}
			if ((object)value == null)
			{
				yield return instruction;
				continue;
			}
			CodeInstruction val = new CodeInstruction(instruction);
			val.opcode = OpCodes.Call;
			val.operand = value;
			yield return val;
		}
	}

	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
	{
		return Rewrite(instructions, (object)__originalMethod.DeclaringType == typeof(GUIStore) || (object)__originalMethod.DeclaringType == typeof(GUIStoreStatsDisplay));
	}
}
[HarmonyPatch(typeof(GuiTutorialMessageBox), "SetContent")]
internal static class TutorialMessagePatch
{
	private static void Prefix(GUIText ___m_textMessage)
	{
		Bind(___m_textMessage);
	}

	internal static void Bind(GUIText gui)
	{
		if (!((Object)(object)Plugin.Instance == (Object)null) && !((Object)(object)gui == (Object)null))
		{
			LocalizedMenuLabel.GetOrCreate(((Component)gui).gameObject);
		}
	}
}
[HarmonyPatch(typeof(GuiTutorialMessageBox), "ShowButton")]
internal static class TutorialButtonPatch
{
	private static void Prefix(GUIText ___m_textButton)
	{
		TutorialMessagePatch.Bind(___m_textButton);
	}
}
[HarmonyPatch(typeof(TextWrapper), "WrapText")]
internal static class TutorialWrapPatch
{
	private static bool Prefix(string input, float width, TextMesh ___textMesh, ref string __result)
	{
		if ((Object)(object)Plugin.Instance == (Object)null || !Plugin.Instance.Language.UsesChineseTextRules || (Object)(object)___textMesh == (Object)null || (Object)(object)___textMesh.font == (Object)null || !ChineseTextWrapper.ContainsChinese(input) || (Object)(object)((Component)___textMesh).GetComponent<LocalizedMenuLabel>() == (Object)null || (Object)(object)((Component)___textMesh).GetComponentInParent<GuiTutorialMessageBox>() == (Object)null)
		{
			return true;
		}
		TutorialTextMetrics tutorialTextMetrics = new TutorialTextMetrics(___textMesh);
		__result = ChineseTextWrapper.Wrap(input, width, tutorialTextMetrics.Advance, tutorialTextMetrics.TagWidth);
		return false;
	}
}
internal sealed class TutorialTextMetrics
{
	private static readonly Regex Size = new Regex("\\bsize\\s*=\\s*[\"']?([0-9]+(?:\\.[0-9]+)?)", RegexOptions.IgnoreCase);

	private readonly Font font;

	private readonly float scale;

	private readonly int baseSize;

	private readonly FontStyle baseStyle;

	private readonly Stack<int> sizes = new Stack<int>();

	private int size;

	private int bold;

	private int italic;

	internal TutorialTextMetrics(TextMesh mesh)
	{
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		font = mesh.font;
		baseSize = (size = ((mesh.fontSize > 0) ? mesh.fontSize : font.fontSize));
		baseStyle = mesh.fontStyle;
		scale = mesh.characterSize * 0.1f * Mathf.Abs(((Component)mesh).transform.lossyScale.x);
	}

	internal float Advance(char ch)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Invalid comparison between Unknown and I4
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Invalid comparison between Unknown and I4
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Invalid comparison between Unknown and I4
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Invalid comparison between Unknown and I4
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		bool num = bold > 0 || (int)baseStyle == 1 || (int)baseStyle == 3;
		bool flag = italic > 0 || (int)baseStyle == 2 || (int)baseStyle == 3;
		FontStyle val = (FontStyle)((!num) ? (flag ? 2 : 0) : ((!flag) ? 1 : 3));
		if (font.dynamic)
		{
			font.RequestCharactersInTexture(ch.ToString(), size, val);
		}
		CharacterInfo val2 = default(CharacterInfo);
		if (!font.GetCharacterInfo(ch, ref val2, size, val))
		{
			return (float)size * scale;
		}
		return (float)((CharacterInfo)(ref val2)).advance * scale;
	}

	internal float TagWidth(string tag)
	{
		if (tag.StartsWith("<quad", StringComparison.OrdinalIgnoreCase))
		{
			Match match = Size.Match(tag);
			if (!match.Success || !float.TryParse(match.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return (float)size * scale;
			}
			return result * scale;
		}
		if (tag.StartsWith("<size=", StringComparison.OrdinalIgnoreCase))
		{
			Match match2 = Size.Match(tag);
			if (match2.Success && int.TryParse(match2.Groups[1].Value, out var result2) && result2 > 0)
			{
				sizes.Push(size);
				size = result2;
			}
		}
		else if (string.Equals(tag, "</size>", StringComparison.OrdinalIgnoreCase))
		{
			size = ((sizes.Count > 0) ? sizes.Pop() : baseSize);
		}
		else if (string.Equals(tag, "<b>", StringComparison.OrdinalIgnoreCase))
		{
			bold++;
		}
		else if (string.Equals(tag, "</b>", StringComparison.OrdinalIgnoreCase))
		{
			bold = Math.Max(0, bold - 1);
		}
		else if (string.Equals(tag, "<i>", StringComparison.OrdinalIgnoreCase))
		{
			italic++;
		}
		else if (string.Equals(tag, "</i>", StringComparison.OrdinalIgnoreCase))
		{
			italic = Math.Max(0, italic - 1);
		}
		return 0f;
	}
}
internal static class BuildVersion
{
	internal const string Plugin = "0.13.0";

	internal const string Assembly = "0.13.0.0";
}