Decompiled source of CrashoutCrewKorean v0.1.1

BepInEx\plugins\CrashoutCrewKorean\CrashoutCrewKorean.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Aggro.Core;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace CrashoutCrewKorean;

[BepInPlugin("dezin.crashoutcrew.korean", "CrashoutCrew Korean", "0.1.0")]
public sealed class CrashoutCrewKoreanPlugin : BaseUnityPlugin
{
	private sealed class RenderedGlyph
	{
		public uint CodePoint;

		public int Width;

		public int Height;

		public float BearingX;

		public float BearingY;

		public float Advance;

		public byte[] Alpha;

		public int AtlasX;

		public int AtlasY;
	}

	private sealed class LocalizationWorker : MonoBehaviour
	{
		private float _nextTick;

		private void Update()
		{
			if (!((Object)(object)Instance == (Object)null) && !(Time.unscaledTime < _nextTick))
			{
				_nextTick = Time.unscaledTime + 0.5f;
				Instance.RefreshLocalizationAndFonts("worker");
			}
		}
	}

	public const string PluginGuid = "dezin.crashoutcrew.korean";

	public const string PluginName = "CrashoutCrew Korean";

	public const string PluginVersion = "0.1.0";

	private const int TargetLanguageIndex = 1;

	private const string TargetLanguageLabel = "Korean";

	private const string TargetLanguageLabelWip = "Korean (WIP)";

	private const int FontSize = 90;

	private const int GdiFontSize = 88;

	private const int GlyphCanvasPadding = 24;

	private const int AtlasPadding = 8;

	private const int AtlasSize = 1024;

	private const int MaxAtlasSize = 2048;

	private const uint FR_PRIVATE = 16u;

	private const string KoreanFallbackFontAssetName = "PinkfongBabyShark-Korean-Fallback";

	private const string KoreanMaterialName = "PinkfongBabyShark-Korean-Material";

	private static readonly Regex ShiftCounterNumberRegex = new Regex("(\\d+)", RegexOptions.Compiled);

	private static readonly Regex RichTextTagRegex = new Regex("<[^>]+>", RegexOptions.Compiled);

	private static readonly Regex ShiftCounterCompositeRegex = new Regex("(?i)(?:NEXT|다음|IN)?\\s*(\\d+)\\s*(?:SHIFTS|번\\s*근무|회차|번의\\s*근무\\s*동안)?", RegexOptions.Compiled);

	private static readonly Dictionary<string, string> ExtraDirectOverrides = new Dictionary<string, string>(StringComparer.Ordinal)
	{
		{ "HOST STEAM", "스팀으로 호스트" },
		{ "HOST w/ IP", "IP로 호스트" },
		{ "CHOOSE SAFETY VIOLATION", "안전 위반 선택" },
		{ "HOW TO PLAY", "플레이 방법" },
		{ "READY UP!", "준비하기!" },
		{ "GET READY!", "준비하세요!" },
		{ "START SHIFT", "근무 시작" },
		{ "CASH EARNED:", "획득 금액:" },
		{ "Buy <>", "구매 <>" },
		{ "OK!", "확인!" },
		{ "NEXT \n<color=#EE6F52>2</color> SHIFTS", "2번의 근무 동안" },
		{ "NEXT\n<color=#EE6F52>2</color> SHIFTS", "2번의 근무 동안" },
		{ "다음 \\\\n<color=#EE6F52>2</color>번 근무", "<color=#EE6F52>2</color>번의 근무 동안" },
		{ "다음 \\n<color=#EE6F52>2</color>번 근무", "<color=#EE6F52>2</color>번의 근무 동안" },
		{ "다음\n<color=#EE6F52>2</color>회차", "2번의 근무 동안" },
		{ "<color=#EE6F52>2</color>회차", "2번의 근무 동안" },
		{ "<color=#EE6F52>2</color>번 근무", "2번의 근무 동안" },
		{ "In2번 근무", "2번의 근무 동안" },
		{ "2번 근무", "2번의 근무 동안" },
		{ "2회차", "2번의 근무 동안" },
		{ "Quit active contract + return to the parking Lot", "진행 중인 계약을 종료하고 주차장으로 돌아가기" },
		{ "Local IPs", "로컬 IP" },
		{ "QUIT TO LOBBY", "로비로 나가기" },
		{ "Change Game settings", "게임 설정 변경" },
		{ "QUIT TO TITLE", "타이틀로 나가기" },
		{ "Quit to the Title", "타이틀로 나가기" },
		{ "Quit to title", "타이틀로 나가기" },
		{ "Only Available In Parking Lot", "주차장에서만 가능" },
		{ "Enter text...", "텍스트를 입력하세요..." },
		{ "External IP", "외부 IP" },
		{ "Player has joined the team!", "플레이어가 팀에 합류했습니다!" },
		{ "PLAYER shared a TipTap with you!", "플레이어가 TipTap을 공유했습니다!" },
		{ "NOT IN DEMO!", "데모에서는 이용할 수 없습니다!" },
		{ "number required", "필요 수량" },
		{ "End Run", "계약 종료" },
		{ "contract Selection", "계약 선택" },
		{ "Project Manager", "프로젝트 매니저" },
		{ "Project Manager & Editor", "프로젝트 매니저 및 편집" },
		{ "Localization Project Manager", "현지화 프로젝트 매니저" },
		{ "Lead Localization Project Manager", "총괄 현지화 프로젝트 매니저" },
		{ "LQA Testing", "LQA 테스트" },
		{ "In Loving Memory", "추모하며" },
		{ "Discord Moderators", "디스코드 관리자" },
		{ "Made using FMOD Studio by Firelight Technologies Pty Ltd.", "Firelight Technologies Pty Ltd.의 FMOD Studio로 제작" },
		{ "Chinese Publishing", "중국어 퍼블리싱" },
		{ "QA Coordination", "QA 코디네이션" },
		{ "Localization QA Manager", "현지화 QA 매니저" },
		{ "Project Manager & Translator", "프로젝트 매니저 및 번역" },
		{ "Functionality QA Project Manager", "기능 QA 프로젝트 매니저" },
		{ "Senior Tester", "시니어 테스터" },
		{ "Additional Development", "추가 개발" },
		{ "Test Lead", "테스트 리드" },
		{ "Test Leads", "테스트 리드" },
		{ "Localization QA Project Managers", "현지화 QA 프로젝트 매니저" },
		{ "European Spanish", "유럽 스페인어" },
		{ "Promotional Assets", "홍보용 자산" },
		{ "Special Thanks", "특별 감사" },
		{ "Localization Testers", "현지화 테스터" },
		{ "Discord Management", "디스코드 운영" },
		{ "QA Coordinators", "QA 코디네이터" },
		{ "FQA Testing", "기능 QA 테스트" },
		{ "Pre-production Development", "사전 제작 개발" },
		{ "Functionality Testers", "기능 테스터" },
		{ "Functionality QA Manager", "기능 QA 매니저" },
		{ "Birthday Boy", "생일 소년" },
		{ "Snake Kigurumi", "뱀 키구루미" },
		{ "Floppy Ears", "축 늘어진 귀" },
		{ "Cat Ears", "고양이 귀" },
		{ "German Shepherd Ears", "셰퍼드 귀" },
		{ "Drag Queen", "드랙 퀸" },
		{ "Mascot Head", "마스코트 머리" },
		{ "Propeller Hat", "프로펠러 모자" },
		{ "Fish Hat", "물고기 모자" },
		{ "Gamer Headset", "게이머 헤드셋" },
		{ "Wizard Hat", "마법사 모자" },
		{ "Chef Hat", "셰프 모자" },
		{ "Kril Head", "크릴 머리" },
		{ "Cowboy Hat", "카우보이 모자" },
		{ "Firefighter Hat", "소방관 모자" },
		{ "Glorp Antenna", "글롭 안테나" },
		{ "Open Box", "열린 박스" },
		{ "Giant Moustache", "거대한 콧수염" },
		{ "Triangle Sunglasses", "삼각 선글라스" },
		{ "Straw Hat", "밀짚모자" },
		{ "Bull Fighter", "투우사" },
		{ "Biker Helmet", "바이커 헬멧" },
		{ "Bee Keeper Hat", "양봉가 모자" },
		{ "Oil Baron", "석유 재벌" },
		{ "Trucker Hat", "트러커 모자" },
		{ "Arrow Head", "화살 머리" },
		{ "Safari Hat", "사파리 모자" },
		{ "Roman Helmet", "로마 투구" },
		{ "Detective Hat", "탐정 모자" },
		{ "Lion's Mane", "사자의 갈기" },
		{ "Hard Hat", "안전모" }
	};

	internal static CrashoutCrewKoreanPlugin Instance;

	internal static ManualLogSource PluginLog;

	internal static TMP_FontAsset FallbackFontAsset;

	internal static Font UiKoreanFont;

	internal static string PluginDirectory;

	internal static string LocalizationDirectory;

	internal static string ReferenceDirectory;

	internal static string FontDirectory;

	internal static string FontPath;

	internal static string GameDataPatchDirectory;

	internal static bool HasLoadedOverrides;

	internal static bool HasInitializedFromTextHook;

	private Harmony _harmony;

	private float _nextSweepTime;

	private bool _hasDumpedMainReferenceTable;

	private bool _hasDumpedDialogueReferenceTable;

	private bool _isRefreshing;

	private bool _isApplyingLanguage;

	private bool _isMutatingText;

	private bool _hasLoggedUiFontSelection;

	private bool _hasLoggedObjectCounts;

	private bool _hasLoggedGlobalFallback;

	private bool _hasLoggedTmpFontAssignment;

	private bool _hasLoggedDropdownPreserve;

	private int _traceCount;

	private readonly HashSet<string> _seenTextTraces = new HashSet<string>(StringComparer.Ordinal);

	private readonly HashSet<string> _seenRuntimeUntranslated = new HashSet<string>(StringComparer.Ordinal);

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

	private readonly HashSet<uint> _preparedCodePoints = new HashSet<uint>();

	private LocalizationWorker _worker;

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

	private void Awake()
	{
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Expected O, but got Unknown
		try
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Awake begin");
			Instance = this;
			PluginLog = ((BaseUnityPlugin)this).Logger;
			PluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			LocalizationDirectory = Path.Combine(PluginDirectory, "Localization");
			ReferenceDirectory = Path.Combine(PluginDirectory, "Reference");
			FontDirectory = Path.Combine(PluginDirectory, "Fonts");
			GameDataPatchDirectory = Path.Combine(PluginDirectory, "GameData");
			Directory.CreateDirectory(LocalizationDirectory);
			Directory.CreateDirectory(ReferenceDirectory);
			Directory.CreateDirectory(FontDirectory);
			Directory.CreateDirectory(GameDataPatchDirectory);
			ApplyBundledGameDataPatches();
			EnsureTemplateFiles();
			PrimePreparedCodePointsFromOverrideFiles();
			FontPath = ResolveFontPath();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Resolved font path: " + FontPath));
			_harmony = new Harmony("dezin.crashoutcrew.korean");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Harmony patches applied: " + _harmony.GetPatchedMethods().Count()));
			EnsureWorker();
			TryCreateFallbackFont();
			ApplyGlobalTmpFallbackSettings();
			SceneManager.sceneLoaded += OnSceneLoaded;
			RefreshLocalizationAndFonts("awake");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Awake complete");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Awake failed: " + ex));
			throw;
		}
	}

	private void Start()
	{
		try
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Start begin");
			((MonoBehaviour)this).StartCoroutine(DelayedInitialize());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Start complete");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Start failed: " + ex));
			throw;
		}
	}

	private IEnumerator DelayedInitialize()
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)"DelayedInitialize begin");
		yield return null;
		yield return null;
		yield return null;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"DelayedInitialize creating fallback");
		TryCreateFallbackFont();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"DelayedInitialize refreshing");
		RefreshLocalizationAndFonts("startup");
		((BaseUnityPlugin)this).Logger.LogInfo((object)"DelayedInitialize complete");
	}

	private void Update()
	{
		if (!(Time.unscaledTime < _nextSweepTime))
		{
			_nextSweepTime = Time.unscaledTime + 2f;
			RefreshLocalizationAndFonts("periodic");
		}
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
	}

	private void EnsureWorker()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		if (!((Object)(object)_worker != (Object)null))
		{
			GameObject val = new GameObject("CrashoutCrewKoreanWorker");
			((Object)val).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)val);
			_worker = val.AddComponent<LocalizationWorker>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Worker created");
		}
	}

	private void ApplyBundledGameDataPatches()
	{
		try
		{
			if (string.IsNullOrEmpty(GameDataPatchDirectory) || !Directory.Exists(GameDataPatchDirectory))
			{
				return;
			}
			string dataPath = Application.dataPath;
			if (string.IsNullOrEmpty(dataPath) || !Directory.Exists(dataPath))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Game data directory not found for bundled patch copy.");
				return;
			}
			string[] array = new string[4] { "resources.assets", "level1", "level3", "level5" };
			string[] array2 = array;
			foreach (string text in array2)
			{
				string text2 = Path.Combine(GameDataPatchDirectory, text);
				string text3 = Path.Combine(dataPath, text);
				if (File.Exists(text2) && File.Exists(text3) && !FilesEqual(text2, text3))
				{
					string text4 = text3 + ".crashoutcrewkorean-backup";
					if (!File.Exists(text4))
					{
						File.Copy(text3, text4, overwrite: false);
					}
					File.Copy(text2, text3, overwrite: true);
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Applied bundled game data patch: " + text));
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Failed to apply bundled game data patches: " + ex));
		}
	}

	private static bool FilesEqual(string leftPath, string rightPath)
	{
		FileInfo fileInfo = new FileInfo(leftPath);
		FileInfo fileInfo2 = new FileInfo(rightPath);
		if (fileInfo.Length != fileInfo2.Length)
		{
			return false;
		}
		using FileStream inputStream = File.OpenRead(leftPath);
		using FileStream inputStream2 = File.OpenRead(rightPath);
		using SHA256 sHA = SHA256.Create();
		using SHA256 sHA2 = SHA256.Create();
		byte[] first = sHA.ComputeHash(inputStream);
		byte[] second = sHA2.ComputeHash(inputStream2);
		return first.SequenceEqual(second);
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Scene loaded: " + ((Scene)(ref scene)).name));
		RefreshLocalizationAndFonts("scene:" + ((Scene)(ref scene)).name);
	}

	internal void RefreshLocalizationAndFonts(string reason)
	{
		if (_isRefreshing)
		{
			return;
		}
		_isRefreshing = true;
		try
		{
			TryCreateFallbackFont();
			ApplyGlobalTmpFallbackSettings();
			EnsureLocalizationTables();
			ApplyGlobalTmpFallbackSettings();
		}
		catch (Exception ex)
		{
			PluginLog.LogError((object)("Failed to refresh localization tables (" + reason + "): " + ex));
		}
		try
		{
			ApplyFontFallbackToLoadedAssets();
		}
		catch (Exception ex2)
		{
			PluginLog.LogError((object)("Failed to apply font fallback (" + reason + "): " + ex2));
		}
		finally
		{
			_isRefreshing = false;
		}
	}

	internal static void EnsureInitializedFromTextHook(string reason)
	{
		if (!((Object)(object)Instance == (Object)null))
		{
			if (!HasInitializedFromTextHook)
			{
				PluginLog.LogInfo((object)("Text hook initialization: " + reason));
				HasInitializedFromTextHook = true;
			}
			Instance.RefreshLocalizationAndFonts(reason);
		}
	}

	internal static void EnsureTextComponentReady(TMP_Text textComponent, string reason)
	{
		if (!((Object)(object)Instance == (Object)null) && !((Object)(object)textComponent == (Object)null))
		{
			Instance.EnsureStringReady(textComponent.text, textComponent.font, reason + ":" + ((Object)textComponent).name);
			Instance.ApplyTmpTextFont(textComponent, reason + ":" + ((Object)textComponent).name);
		}
	}

	internal static void RefreshTmpTextComponent(TMP_Text textComponent, string reason)
	{
		if (!((Object)(object)Instance == (Object)null) && !((Object)(object)textComponent == (Object)null))
		{
			Instance.TraceTextComponent("TMP", ((Object)textComponent).name, textComponent.text, reason);
			Instance.ApplyDirectTextOverride(textComponent, reason);
			Instance.EnsureStringReady(textComponent.text, textComponent.font, reason + ":" + ((Object)textComponent).name);
			Instance.ApplyTmpTextFont(textComponent, reason + ":" + ((Object)textComponent).name);
		}
	}

	internal static void EnsureIncomingTextReady(TMP_Text textComponent, string value, string reason)
	{
		if (!((Object)(object)Instance == (Object)null))
		{
			Instance.EnsureStringReady(value, ((Object)(object)textComponent != (Object)null) ? textComponent.font : null, reason);
			if ((Object)(object)textComponent != (Object)null)
			{
				Instance.ApplyTmpTextFont(textComponent, reason);
			}
		}
	}

	internal static string TranslateIncomingText(string value, string reason)
	{
		if ((Object)(object)Instance == (Object)null)
		{
			return value;
		}
		return Instance.TryTranslateDirectText(value, reason);
	}

	internal static string TranslateIncomingText(TMP_Text textComponent, string value, string reason)
	{
		if ((Object)(object)Instance == (Object)null)
		{
			return value;
		}
		return Instance.NormalizeTmpTextForComponent(textComponent, Instance.TryTranslateDirectText(value, reason));
	}

	internal static string TranslateIncomingText(Text textComponent, string value, string reason)
	{
		if ((Object)(object)Instance == (Object)null)
		{
			return value;
		}
		return Instance.NormalizeUiTextForComponent(textComponent, Instance.TryTranslateDirectText(value, reason));
	}

	internal static void EnsureUiTextReady(Text textComponent, string reason)
	{
		if (!((Object)(object)Instance == (Object)null) && !((Object)(object)textComponent == (Object)null))
		{
			Instance.ApplyUiTextFont(textComponent, textComponent.text, reason + ":" + ((Object)textComponent).name);
		}
	}

	internal static void RefreshUiTextComponent(Text textComponent, string reason)
	{
		if (!((Object)(object)Instance == (Object)null) && !((Object)(object)textComponent == (Object)null))
		{
			Instance.TraceTextComponent("UI", ((Object)textComponent).name, textComponent.text, reason);
			Instance.ApplyDirectTextOverride(textComponent, reason);
			Instance.ApplyUiTextFont(textComponent, textComponent.text, reason + ":" + ((Object)textComponent).name);
		}
	}

	internal static void EnsureIncomingUiTextReady(Text textComponent, string value, string reason)
	{
		if (!((Object)(object)Instance == (Object)null))
		{
			Instance.ApplyUiTextFont(textComponent, value, reason);
		}
	}

	private void EnsureStringReady(string text, TMP_FontAsset fontAsset, string reason)
	{
		EnsureFallbackFontContainsCharacters(CollectCodePoints(text), fontAsset ?? FindTemplateFontAsset(), reason);
		ApplyGlobalTmpFallbackSettings();
		if ((Object)(object)fontAsset != (Object)null && (Object)(object)FallbackFontAsset != (Object)null)
		{
			EnsureFontFallback(fontAsset);
			EnsureFontFallback(FallbackFontAsset, fontAsset);
		}
	}

	private void ApplyTmpTextFont(TMP_Text textComponent, string reason)
	{
		if ((Object)(object)textComponent == (Object)null || (Object)(object)FallbackFontAsset == (Object)null || !ContainsHangul(textComponent.text))
		{
			return;
		}
		TMP_FontAsset font = textComponent.font;
		if ((Object)(object)font != (Object)null && (Object)(object)font != (Object)(object)FallbackFontAsset)
		{
			EnsureFontFallback(FallbackFontAsset, font);
			EnsureFontFallback(font, FallbackFontAsset);
		}
		if (ShouldPreserveTmpAppearance(textComponent))
		{
			if (!_hasLoggedDropdownPreserve)
			{
				_hasLoggedDropdownPreserve = true;
				PluginLog.LogInfo((object)("Preserving TMP appearance while injecting Korean fallback via " + reason + "."));
			}
			textComponent.havePropertiesChanged = true;
			return;
		}
		if ((Object)(object)textComponent.font != (Object)(object)FallbackFontAsset)
		{
			textComponent.font = FallbackFontAsset;
		}
		if ((Object)(object)((TMP_Asset)FallbackFontAsset).material != (Object)null && (Object)(object)textComponent.fontSharedMaterial != (Object)(object)((TMP_Asset)FallbackFontAsset).material)
		{
			textComponent.fontSharedMaterial = ((TMP_Asset)FallbackFontAsset).material;
		}
		if (!_hasLoggedTmpFontAssignment)
		{
			_hasLoggedTmpFontAssignment = true;
			PluginLog.LogInfo((object)("Assigned TMP Korean fallback font directly via " + reason + "."));
		}
	}

	private string NormalizeTmpTextForComponent(TMP_Text textComponent, string text)
	{
		if ((Object)(object)textComponent == (Object)null || string.IsNullOrEmpty(text))
		{
			return text;
		}
		return NormalizeSpecialUiText(GetHierarchyPath(textComponent.transform), text);
	}

	private string NormalizeUiTextForComponent(Text textComponent, string text)
	{
		if ((Object)(object)textComponent == (Object)null || string.IsNullOrEmpty(text))
		{
			return text;
		}
		return NormalizeSpecialUiText(GetHierarchyPath(((Component)textComponent).transform), text);
	}

	private string NormalizeSpecialUiText(string hierarchy, string text)
	{
		if (string.IsNullOrEmpty(text))
		{
			return text;
		}
		if (TryNormalizeShiftDurationText(text, out var normalized))
		{
			return normalized;
		}
		if (IsShiftCounterHierarchy(hierarchy))
		{
			string text2 = RichTextTagRegex.Replace(text.Trim(), string.Empty).Replace("\\n", " ").Replace("\r", " ")
				.Replace("\n", " ")
				.Trim();
			if (text2.Equals("SHIFTS", StringComparison.OrdinalIgnoreCase) || text2.Equals("In", StringComparison.OrdinalIgnoreCase) || text2.Equals("IN", StringComparison.OrdinalIgnoreCase))
			{
				return string.Empty;
			}
		}
		return text;
	}

	private bool TryNormalizeShiftDurationText(string text, out string normalized)
	{
		normalized = text;
		if (string.IsNullOrWhiteSpace(text))
		{
			return false;
		}
		string text2 = RichTextTagRegex.Replace(text.Trim(), string.Empty).Replace("\\n", " ").Replace("\r", " ")
			.Replace("\n", " ")
			.Trim();
		if (text2.Length == 0)
		{
			return false;
		}
		if (text2 == "다음" || text2.Equals("NEXT", StringComparison.OrdinalIgnoreCase))
		{
			normalized = "다음";
			return true;
		}
		if (text2.Equals("In", StringComparison.OrdinalIgnoreCase) || text2.Equals("IN", StringComparison.OrdinalIgnoreCase) || text2.Equals("SHIFTS", StringComparison.OrdinalIgnoreCase))
		{
			normalized = string.Empty;
			return true;
		}
		Match match = ShiftCounterCompositeRegex.Match(text2);
		if (match.Success && IsLikelyShiftDurationText(text2))
		{
			normalized = match.Groups[1].Value + "번의 근무 동안";
			return true;
		}
		Match match2 = ShiftCounterNumberRegex.Match(text2);
		if (match2.Success && IsLikelyShiftDurationText(text2))
		{
			normalized = match2.Groups[1].Value + "번의 근무 동안";
			return true;
		}
		return false;
	}

	private bool IsLikelyShiftDurationText(string plain)
	{
		if (string.IsNullOrWhiteSpace(plain))
		{
			return false;
		}
		if (plain.Length > 32)
		{
			return false;
		}
		if (plain.IndexOf("shift", StringComparison.OrdinalIgnoreCase) < 0 && plain.IndexOf("근무", StringComparison.OrdinalIgnoreCase) < 0 && plain.IndexOf("회차", StringComparison.OrdinalIgnoreCase) < 0 && plain.IndexOf("동안", StringComparison.OrdinalIgnoreCase) < 0 && plain.IndexOf("NEXT", StringComparison.OrdinalIgnoreCase) < 0 && plain.IndexOf("다음", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return plain.IndexOf("In", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private bool IsShiftCounterHierarchy(string hierarchy)
	{
		if (string.IsNullOrEmpty(hierarchy))
		{
			return false;
		}
		if (hierarchy.IndexOf("NextXShifts", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return hierarchy.IndexOf("ShiftCounter", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private string GetHierarchyPath(Transform transform)
	{
		if ((Object)(object)transform == (Object)null)
		{
			return string.Empty;
		}
		List<string> list = new List<string>();
		Transform val = transform;
		while ((Object)(object)val != (Object)null && list.Count < 10)
		{
			list.Add(((Object)val).name ?? string.Empty);
			val = val.parent;
		}
		list.Reverse();
		return string.Join("/", list.ToArray());
	}

	private bool ShouldPreserveTmpAppearance(TMP_Text textComponent)
	{
		if ((Object)(object)textComponent == (Object)null)
		{
			return false;
		}
		if ((Object)(object)((Component)textComponent).GetComponentInParent<TMP_Dropdown>(true) != (Object)null)
		{
			return true;
		}
		string text = ((Object)textComponent).name ?? string.Empty;
		if (text.IndexOf("caption", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return text.IndexOf("dropdown", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private string TryTranslateDirectText(string text, string reason)
	{
		if (_isMutatingText || string.IsNullOrEmpty(text) || _directTextOverrides.Count == 0)
		{
			CaptureRuntimeUntranslated("TEXT", "direct", reason, text);
			return text;
		}
		if (_directTextOverrides.TryGetValue(text, out var value) && !string.IsNullOrEmpty(value) && value != text)
		{
			PluginLog.LogInfo((object)("Direct text override (" + reason + "): [" + text + "] -> [" + value + "]"));
			return value;
		}
		CaptureRuntimeUntranslated("TEXT", "direct", reason, text);
		return text;
	}

	private void TraceTextComponent(string family, string name, string text, string reason)
	{
		if (_traceCount < 60 && !string.IsNullOrWhiteSpace(text))
		{
			string text2 = text.Replace("\r", "\\r").Replace("\n", "\\n");
			string item = family + "|" + name + "|" + text2;
			if (_seenTextTraces.Add(item))
			{
				_traceCount++;
				PluginLog.LogInfo((object)("Text trace [" + family + "] (" + reason + ") name=[" + name + "] text=[" + text2 + "]"));
				CaptureRuntimeUntranslated(family, name, reason, text);
			}
		}
	}

	private void CaptureRuntimeUntranslated(string family, string name, string reason, string text)
	{
		if (string.IsNullOrWhiteSpace(text) || ContainsHangul(text) || !LooksLikeTranslatableEnglish(text))
		{
			return;
		}
		string text2 = text.Replace("\r", "\\r").Replace("\n", "\\n");
		string item = family + "|" + name + "|" + text2;
		if (!_seenRuntimeUntranslated.Add(item))
		{
			return;
		}
		try
		{
			string path = Path.Combine(ReferenceDirectory, "runtime-untranslated.tsv");
			string text3 = EscapeTsv(family) + "\t" + EscapeTsv(name) + "\t" + EscapeTsv(reason) + "\t" + EscapeTsv(text2);
			File.AppendAllText(path, text3 + Environment.NewLine);
		}
		catch (Exception ex)
		{
			PluginLog.LogWarning((object)("Failed to record untranslated runtime text: " + ex));
		}
	}

	private bool LooksLikeTranslatableEnglish(string text)
	{
		if (string.IsNullOrWhiteSpace(text))
		{
			return false;
		}
		bool flag = false;
		foreach (char c in text)
		{
			if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return false;
		}
		if (!(text != text.ToUpperInvariant()))
		{
			return text.Length > 1;
		}
		return true;
	}

	private string EscapeTsv(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		return value.Replace("\t", " ").Replace("\r", "\\r").Replace("\n", "\\n");
	}

	private void ApplyUiTextFont(Text textComponent, string text, string reason)
	{
		if ((Object)(object)textComponent == (Object)null || !ContainsHangul(text))
		{
			return;
		}
		Font val = ResolveUiKoreanFont();
		if (!((Object)(object)val == (Object)null) && (Object)(object)textComponent.font != (Object)(object)val)
		{
			textComponent.font = val;
			if (!_hasLoggedUiFontSelection)
			{
				_hasLoggedUiFontSelection = true;
				PluginLog.LogInfo((object)("Assigned UI Korean font [" + ((Object)val).name + "] via " + reason + "."));
			}
		}
	}

	internal void ForceTargetLanguage(string reason)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		if (!HasLoadedOverrides || _isApplyingLanguage)
		{
			return;
		}
		Language val = (Language)1;
		if (LocalizedText.CURRENT_LANGUAGE == val)
		{
			return;
		}
		try
		{
			_isApplyingLanguage = true;
			PluginLog.LogInfo((object)("Switching language to Korean slot (" + reason + "). Previous=" + LocalizedText.CURRENT_LANGUAGE));
			LocalizedText.SetLanguage(1);
			LocalizedText.CURRENT_LANGUAGE = val;
			LocalizedText.RefreshAllText();
		}
		catch (Exception ex)
		{
			PluginLog.LogWarning((object)("Failed to switch language to Korean slot (" + reason + "): " + ex));
		}
		finally
		{
			_isApplyingLanguage = false;
		}
	}

	private void EnsureTemplateFiles()
	{
		string path = Path.Combine(LocalizationDirectory, "ko-main.tsv");
		if (!File.Exists(path))
		{
			File.WriteAllText(path, "# key\tkorean text" + Environment.NewLine + "# Example: MENU_OPTIONS\tOPTION" + Environment.NewLine);
		}
		string path2 = Path.Combine(LocalizationDirectory, "ko-dialogue.tsv");
		if (!File.Exists(path2))
		{
			File.WriteAllText(path2, "# key\tkorean dialogue" + Environment.NewLine);
		}
		string path3 = Path.Combine(ReferenceDirectory, "runtime-untranslated.tsv");
		if (!File.Exists(path3))
		{
			File.WriteAllText(path3, "family\tname\treason\ttext" + Environment.NewLine);
		}
	}

	private string ResolveFontPath()
	{
		string[] array = new string[4]
		{
			Path.Combine(FontDirectory, "Pinkfong Baby Shark Font_ Regular.ttf"),
			Path.Combine(FontDirectory, "Pinkfong Baby Shark Font_ Regular.otf"),
			"C:\\Users\\dezin\\OneDrive\\바탕 화면\\Pinkfong Baby Shark Font_ Regular.ttf",
			"C:\\Users\\dezin\\OneDrive\\바탕 화면\\Pinkfong Baby Shark Font_ Regular.otf"
		};
		for (int i = 0; i < array.Length; i++)
		{
			if (File.Exists(array[i]))
			{
				return array[i];
			}
		}
		return array[0];
	}

	private void TryCreateFallbackFont()
	{
		if (!((Object)(object)FallbackFontAsset != (Object)null) && _preparedCodePoints.Count != 0)
		{
			EnsureFallbackFontContainsCharacters(_preparedCodePoints, FindTemplateFontAsset(), "startup");
		}
	}

	private void PrimePreparedCodePointsFromOverrideFiles()
	{
		CollectCharactersFromOverrideFile(Path.Combine(LocalizationDirectory, "ko-main.tsv"));
		CollectCharactersFromOverrideFile(Path.Combine(LocalizationDirectory, "ko-dialogue.tsv"));
	}

	private void CollectCharactersFromOverrideFile(string path)
	{
		if (!File.Exists(path))
		{
			return;
		}
		string[] array = File.ReadAllLines(path);
		foreach (string text in array)
		{
			if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#"))
			{
				continue;
			}
			string[] array2 = text.Split(new char[1] { '\t' }, 2);
			if (array2.Length < 2)
			{
				continue;
			}
			foreach (uint item in CollectCodePoints(Unescape(array2[1])))
			{
				_preparedCodePoints.Add(item);
			}
		}
	}

	private List<string> GetSystemFontCandidates()
	{
		List<string> list = new List<string>();
		list.Add("Malgun Gothic");
		list.Add("맑은 고딕");
		list.Add("Noto Sans KR");
		list.Add("Noto Serif KR");
		list.Add("Gulim");
		list.Add("굴림");
		list.Add("Dotum");
		list.Add("돋움");
		list.Add("Batang");
		list.Add("바탕");
		return list;
	}

	private Font ResolveUiKoreanFont()
	{
		if ((Object)(object)UiKoreanFont != (Object)null)
		{
			return UiKoreanFont;
		}
		List<string> list = TryRegisterFontFamilies(FontPath);
		list.Add("Pinkfong Baby Shark Regular");
		list.Add("핑크퐁 아기상어 Regular");
		list.AddRange(GetSystemFontCandidates());
		UiKoreanFont = TryCreateDynamicFontFromNames(list, 72);
		if ((Object)(object)UiKoreanFont != (Object)null)
		{
			PluginLog.LogInfo((object)("Resolved UI Korean font: " + ((Object)UiKoreanFont).name));
		}
		else
		{
			PluginLog.LogWarning((object)"Could not resolve a UI Korean font.");
		}
		return UiKoreanFont;
	}

	private List<string> TryRegisterFontFamilies(string path)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		List<string> list = new List<string>();
		if (string.IsNullOrEmpty(path) || !File.Exists(path))
		{
			return list;
		}
		try
		{
			PrivateFontCollection val = new PrivateFontCollection();
			try
			{
				val.AddFontFile(path);
				for (int i = 0; i < ((FontCollection)val).Families.Length; i++)
				{
					list.Add(((FontCollection)val).Families[i].Name);
				}
			}
			finally
			{
				((IDisposable)val)?.Dispose();
			}
			AddFontResourceEx(path, 0u, IntPtr.Zero);
			AddFontResourceEx(path, 16u, IntPtr.Zero);
		}
		catch (Exception ex)
		{
			PluginLog.LogWarning((object)("Failed to register UI font families: " + ex));
		}
		return list.Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
	}

	private Font TryCreateDynamicFontFromNames(IEnumerable<string> candidates, int size)
	{
		string[] array = candidates.Where((string name) => !string.IsNullOrWhiteSpace(name)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
		if (array.Length == 0)
		{
			return null;
		}
		try
		{
			return Font.CreateDynamicFontFromOSFont(array, size);
		}
		catch (Exception ex)
		{
			PluginLog.LogWarning((object)("Dynamic UI font creation failed: " + ex));
			return null;
		}
	}

	private bool ContainsHangul(string text)
	{
		if (string.IsNullOrEmpty(text))
		{
			return false;
		}
		for (int i = 0; i < text.Length; i++)
		{
			if (text[i] >= 'ᄀ')
			{
				return true;
			}
		}
		return false;
	}

	private TMP_FontAsset FindTemplateFontAsset()
	{
		TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
		foreach (TMP_FontAsset val in array)
		{
			if (!((Object)(object)val == (Object)null) && ((Object)val).name.IndexOf("ChelseaMarket", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return val;
			}
		}
		return ((IEnumerable<TMP_FontAsset>)array).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => (Object)(object)f != (Object)null));
	}

	private void EnsureFallbackFontContainsCharacters(IEnumerable<uint> codePoints, TMP_FontAsset templateFontAsset, string reason)
	{
		HashSet<uint> hashSet = new HashSet<uint>();
		if (codePoints != null)
		{
			foreach (uint codePoint in codePoints)
			{
				if (codePoint >= 4352)
				{
					hashSet.Add(codePoint);
				}
			}
		}
		if (hashSet.Count == 0)
		{
			return;
		}
		bool flag = (Object)(object)FallbackFontAsset == (Object)null;
		foreach (uint item in hashSet)
		{
			if (_preparedCodePoints.Add(item))
			{
				flag = true;
			}
		}
		if (!flag)
		{
			return;
		}
		try
		{
			TMP_FontAsset val = BuildKoreanFallbackFontAsset(_preparedCodePoints, templateFontAsset);
			if ((Object)(object)val == (Object)null)
			{
				PluginLog.LogWarning((object)("Failed to rebuild Korean fallback font (" + reason + ")."));
				return;
			}
			FallbackFontAsset = val;
			((Object)FallbackFontAsset).name = "PinkfongBabyShark-Korean-Fallback";
			((Object)FallbackFontAsset).hideFlags = (HideFlags)32;
			RemoveStaleKoreanFallbackAssets();
			ApplyGlobalTmpFallbackSettings();
			PluginLog.LogInfo((object)("Rebuilt Korean fallback font with " + _preparedCodePoints.Count + " glyphs (" + reason + ")."));
		}
		catch (Exception ex)
		{
			PluginLog.LogWarning((object)("Korean fallback rebuild failed (" + reason + "): " + ex));
		}
	}

	private TMP_FontAsset BuildBitmapFallbackFontAsset(IEnumerable<uint> codePoints, TMP_FontAsset templateFontAsset)
	{
		//IL_0204: Unknown result type (might be due to invalid IL or missing references)
		//IL_0206: Unknown result type (might be due to invalid IL or missing references)
		//IL_020e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Expected O, but got Unknown
		//IL_021e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0225: Expected O, but got Unknown
		//IL_0257: Unknown result type (might be due to invalid IL or missing references)
		//IL_025e: Expected O, but got Unknown
		if (!TryCreateDrawingFont(88f, out var privateFonts, out var drawFont, out var familyName, out var ascentPixels, out var lineHeightPixels, out var unitsPerEm))
		{
			return null;
		}
		try
		{
			List<RenderedGlyph> list = new List<RenderedGlyph>();
			foreach (uint item2 in codePoints.OrderBy((uint v) => v))
			{
				RenderedGlyph renderedGlyph = RenderGlyph(item2, drawFont, ascentPixels, lineHeightPixels);
				if (renderedGlyph != null)
				{
					list.Add(renderedGlyph);
				}
			}
			if (list.Count == 0)
			{
				PluginLog.LogWarning((object)("No Korean glyphs were rendered from " + familyName + "."));
				return null;
			}
			int num = 1024;
			int num2 = 1024;
			while (!TryPackGlyphs(list, num, num2))
			{
				if (num >= 2048 && num2 >= 2048)
				{
					PluginLog.LogWarning((object)("Korean atlas overflow at " + num + "x" + num2 + "."));
					return null;
				}
				num = Math.Min(num * 2, 2048);
				num2 = Math.Min(num2 * 2, 2048);
			}
			Color32[] array = (Color32[])(object)new Color32[num * num2];
			List<Glyph> list2 = new List<Glyph>(list.Count);
			List<TMP_Character> list3 = new List<TMP_Character>(list.Count);
			uint num3 = 0u;
			GlyphRect val = default(GlyphRect);
			GlyphMetrics val2 = default(GlyphMetrics);
			for (int i = 0; i < list.Count; i++)
			{
				RenderedGlyph renderedGlyph2 = list[i];
				BlitGlyph(renderedGlyph2, array, num, num2);
				((GlyphRect)(ref val))..ctor(renderedGlyph2.AtlasX, num2 - renderedGlyph2.AtlasY - renderedGlyph2.Height, renderedGlyph2.Width, renderedGlyph2.Height);
				((GlyphMetrics)(ref val2))..ctor((float)renderedGlyph2.Width, (float)renderedGlyph2.Height, renderedGlyph2.BearingX, renderedGlyph2.BearingY, renderedGlyph2.Advance);
				Glyph val3 = new Glyph(num3, val2, val, 1f, 0);
				TMP_Character item = new TMP_Character(renderedGlyph2.CodePoint, val3);
				list2.Add(val3);
				list3.Add(item);
				num3++;
			}
			Texture2D val4 = new Texture2D(num, num2, (TextureFormat)5, false);
			((Object)val4).name = "PinkfongBabyShark-Korean-Atlas";
			val4.SetPixels32(array);
			val4.Apply(false, false);
			((Texture)val4).wrapMode = (TextureWrapMode)1;
			((Texture)val4).filterMode = (FilterMode)1;
			return CreateBitmapFontAsset(val4, list2, list3, familyName, unitsPerEm, ascentPixels, lineHeightPixels, templateFontAsset);
		}
		finally
		{
			if (drawFont != null)
			{
				drawFont.Dispose();
			}
			if (privateFonts != null)
			{
				((FontCollection)privateFonts).Dispose();
			}
		}
	}

	private TMP_FontAsset BuildKoreanFallbackFontAsset(IEnumerable<uint> codePoints, TMP_FontAsset templateFontAsset)
	{
		TMP_FontAsset val = BuildDynamicFallbackFontAsset(codePoints, templateFontAsset);
		if ((Object)(object)val != (Object)null)
		{
			return val;
		}
		return BuildBitmapFallbackFontAsset(codePoints, templateFontAsset);
	}

	private TMP_FontAsset BuildDynamicFallbackFontAsset(IEnumerable<uint> codePoints, TMP_FontAsset templateFontAsset)
	{
		Font val = ResolveUiKoreanFont();
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		try
		{
			TMP_FontAsset val2 = InvokeCreateFontAsset(val);
			if ((Object)(object)val2 == (Object)null)
			{
				PluginLog.LogWarning((object)"TMP dynamic font asset creation returned null.");
				return null;
			}
			((Object)val2).name = "PinkfongBabyShark-Korean-Fallback";
			((Object)val2).hideFlags = (HideFlags)32;
			val2.atlasPopulationMode = (AtlasPopulationMode)1;
			val2.isMultiAtlasTexturesEnabled = true;
			val2.fallbackFontAssetTable = new List<TMP_FontAsset>();
			if ((Object)(object)templateFontAsset != (Object)null && (Object)(object)((TMP_Asset)templateFontAsset).material != (Object)null && (Object)(object)((TMP_Asset)val2).material != (Object)null)
			{
				((TMP_Asset)val2).material.shader = ((TMP_Asset)templateFontAsset).material.shader;
			}
			TryAddCharactersToDynamicAsset(val2, codePoints);
			PluginLog.LogInfo((object)("Built TMP dynamic Korean fallback from font: " + ((Object)val).name));
			return val2;
		}
		catch (Exception ex)
		{
			PluginLog.LogWarning((object)("TMP dynamic fallback build failed: " + ex));
			return null;
		}
	}

	private TMP_FontAsset InvokeCreateFontAsset(Font sourceFont)
	{
		MethodInfo[] array = (from method in typeof(TMP_FontAsset).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
			where string.Equals(method.Name, "CreateFontAsset", StringComparison.Ordinal)
			orderby method.GetParameters().Length descending
			select method).ToArray();
		foreach (MethodInfo methodInfo in array)
		{
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length == 0 || parameters[0].ParameterType != typeof(Font))
			{
				continue;
			}
			try
			{
				object[] array2 = new object[parameters.Length];
				bool flag = true;
				for (int j = 0; j < parameters.Length; j++)
				{
					if (!TryBuildCreateFontAssetArgument(parameters[j], sourceFont, out array2[j]))
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					object? obj = methodInfo.Invoke(null, array2);
					TMP_FontAsset val = (TMP_FontAsset)((obj is TMP_FontAsset) ? obj : null);
					if ((Object)(object)val != (Object)null)
					{
						PluginLog.LogInfo((object)("Using TMP font factory: " + methodInfo));
						return val;
					}
				}
			}
			catch
			{
			}
		}
		return null;
	}

	private bool TryBuildCreateFontAssetArgument(ParameterInfo parameter, Font sourceFont, out object value)
	{
		value = null;
		string text = ((parameter.Name != null) ? parameter.Name.ToLowerInvariant() : string.Empty);
		Type parameterType = parameter.ParameterType;
		if (parameterType == typeof(Font))
		{
			value = sourceFont;
			return true;
		}
		if (parameterType == typeof(int))
		{
			if (text.Contains("sampling") || text.Contains("point"))
			{
				value = 90;
			}
			else if (text.Contains("padding"))
			{
				value = 8;
			}
			else if (text.Contains("width") || text.Contains("height"))
			{
				value = 1024;
			}
			else
			{
				value = 0;
			}
			return true;
		}
		if (parameterType == typeof(GlyphRenderMode))
		{
			value = (object)(GlyphRenderMode)4165;
			return true;
		}
		if (parameterType == typeof(AtlasPopulationMode))
		{
			value = (object)(AtlasPopulationMode)1;
			return true;
		}
		if (parameterType == typeof(bool))
		{
			value = true;
			return true;
		}
		return false;
	}

	private void TryAddCharactersToDynamicAsset(TMP_FontAsset fontAsset, IEnumerable<uint> codePoints)
	{
		if ((Object)(object)fontAsset == (Object)null || codePoints == null)
		{
			return;
		}
		string text = BuildStringFromCodePoints(codePoints);
		if (string.IsNullOrEmpty(text))
		{
			return;
		}
		MethodInfo[] array = (from method in typeof(TMP_FontAsset).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
			where string.Equals(method.Name, "TryAddCharacters", StringComparison.Ordinal)
			orderby method.GetParameters().Length descending
			select method).ToArray();
		uint[] array2 = codePoints.ToArray();
		foreach (MethodInfo methodInfo in array)
		{
			ParameterInfo[] parameters = methodInfo.GetParameters();
			try
			{
				if (parameters.Length >= 1 && parameters[0].ParameterType == typeof(string))
				{
					object[] array3 = new object[parameters.Length];
					array3[0] = text;
					for (int j = 1; j < parameters.Length; j++)
					{
						array3[j] = null;
					}
					methodInfo.Invoke(fontAsset, array3);
					break;
				}
				if (parameters.Length >= 1 && parameters[0].ParameterType == typeof(uint[]))
				{
					object[] array4 = new object[parameters.Length];
					array4[0] = array2;
					for (int k = 1; k < parameters.Length; k++)
					{
						array4[k] = null;
					}
					methodInfo.Invoke(fontAsset, array4);
					break;
				}
			}
			catch
			{
			}
		}
	}

	private string BuildStringFromCodePoints(IEnumerable<uint> codePoints)
	{
		if (codePoints == null)
		{
			return string.Empty;
		}
		StringBuilder stringBuilder = new StringBuilder();
		foreach (uint item in codePoints.OrderBy((uint value) => value))
		{
			stringBuilder.Append(char.ConvertFromUtf32((int)item));
		}
		return stringBuilder.ToString();
	}

	private bool TryCreateDrawingFont(float sizePixels, out PrivateFontCollection privateFonts, out Font drawFont, out string familyName, out float ascentPixels, out float lineHeightPixels, out int unitsPerEm)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Expected O, but got Unknown
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Expected O, but got Unknown
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Expected O, but got Unknown
		privateFonts = null;
		drawFont = null;
		familyName = null;
		ascentPixels = 0f;
		lineHeightPixels = 0f;
		unitsPerEm = 0;
		if (File.Exists(FontPath))
		{
			try
			{
				privateFonts = new PrivateFontCollection();
				privateFonts.AddFontFile(FontPath);
				if (((FontCollection)privateFonts).Families.Length > 0)
				{
					drawFont = new Font(((FontCollection)privateFonts).Families[0], sizePixels, (FontStyle)0, (GraphicsUnit)2);
					familyName = ((FontCollection)privateFonts).Families[0].Name;
					PopulateFontMetrics(drawFont, sizePixels, out ascentPixels, out lineHeightPixels, out unitsPerEm);
					return true;
				}
			}
			catch (Exception ex)
			{
				PluginLog.LogWarning((object)("Failed to open private Korean font: " + ex));
				if (privateFonts != null)
				{
					((FontCollection)privateFonts).Dispose();
					privateFonts = null;
				}
			}
		}
		foreach (string systemFontCandidate in GetSystemFontCandidates())
		{
			try
			{
				FontFamily val = new FontFamily(systemFontCandidate);
				drawFont = new Font(val, sizePixels, (FontStyle)0, (GraphicsUnit)2);
				familyName = val.Name;
				PopulateFontMetrics(drawFont, sizePixels, out ascentPixels, out lineHeightPixels, out unitsPerEm);
				return true;
			}
			catch
			{
			}
		}
		PluginLog.LogWarning((object)"No usable Korean GDI font was found.");
		return false;
	}

	private void PopulateFontMetrics(Font drawFont, float sizePixels, out float ascentPixels, out float lineHeightPixels, out int unitsPerEm)
	{
		FontFamily fontFamily = drawFont.FontFamily;
		unitsPerEm = fontFamily.GetEmHeight((FontStyle)0);
		int cellAscent = fontFamily.GetCellAscent((FontStyle)0);
		int lineSpacing = fontFamily.GetLineSpacing((FontStyle)0);
		float num = ((unitsPerEm > 0) ? (sizePixels / (float)unitsPerEm) : 1f);
		ascentPixels = (float)cellAscent * num;
		lineHeightPixels = (float)lineSpacing * num;
	}

	private RenderedGlyph RenderGlyph(uint codePoint, Font drawFont, float ascentPixels, float lineHeightPixels)
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Expected O, but got Unknown
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Expected O, but got Unknown
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		string text = char.ConvertFromUtf32((int)codePoint);
		int num = 24;
		int num2 = 24;
		float num3 = (float)num2 + ascentPixels;
		int num4 = Math.Max(264, 224);
		int num5 = Math.Max((int)Math.Ceiling(lineHeightPixels) + 72, 264);
		Bitmap val = new Bitmap(num4, num5, (PixelFormat)2498570);
		try
		{
			Graphics val2 = Graphics.FromImage((Image)(object)val);
			try
			{
				SolidBrush val3 = new SolidBrush(Color.White);
				try
				{
					StringFormat val4 = (StringFormat)StringFormat.GenericTypographic.Clone();
					try
					{
						val2.Clear(Color.Transparent);
						val2.TextRenderingHint = (TextRenderingHint)3;
						val2.SmoothingMode = (SmoothingMode)4;
						val2.PixelOffsetMode = (PixelOffsetMode)2;
						val4.FormatFlags = (StringFormatFlags)(val4.FormatFlags | 0x800);
						float width = val2.MeasureString(text, drawFont, num4, val4).Width;
						val2.DrawString(text, drawFont, (Brush)(object)val3, new PointF(num, num2), val4);
						if (!TryFindOpaqueBounds(val, out var minX, out var minY, out var maxX, out var maxY))
						{
							return null;
						}
						int width2 = maxX - minX + 1;
						int height = maxY - minY + 1;
						byte[] alpha = ExtractAlpha(val, minX, minY, width2, height);
						RenderedGlyph renderedGlyph = new RenderedGlyph();
						renderedGlyph.CodePoint = codePoint;
						renderedGlyph.Width = width2;
						renderedGlyph.Height = height;
						renderedGlyph.BearingX = minX - num;
						renderedGlyph.BearingY = num3 - (float)minY;
						renderedGlyph.Advance = width;
						renderedGlyph.Alpha = alpha;
						return renderedGlyph;
					}
					finally
					{
						((IDisposable)val4)?.Dispose();
					}
				}
				finally
				{
					((IDisposable)val3)?.Dispose();
				}
			}
			finally
			{
				((IDisposable)val2)?.Dispose();
			}
		}
		finally
		{
			((IDisposable)val)?.Dispose();
		}
	}

	private bool TryFindOpaqueBounds(Bitmap bitmap, out int minX, out int minY, out int maxX, out int maxY)
	{
		minX = ((Image)bitmap).Width;
		minY = ((Image)bitmap).Height;
		maxX = -1;
		maxY = -1;
		for (int i = 0; i < ((Image)bitmap).Height; i++)
		{
			for (int j = 0; j < ((Image)bitmap).Width; j++)
			{
				if (bitmap.GetPixel(j, i).A > 0)
				{
					if (j < minX)
					{
						minX = j;
					}
					if (i < minY)
					{
						minY = i;
					}
					if (j > maxX)
					{
						maxX = j;
					}
					if (i > maxY)
					{
						maxY = i;
					}
				}
			}
		}
		if (maxX >= minX)
		{
			return maxY >= minY;
		}
		return false;
	}

	private byte[] ExtractAlpha(Bitmap bitmap, int startX, int startY, int width, int height)
	{
		byte[] array = new byte[width * height];
		for (int i = 0; i < height; i++)
		{
			int num = i * width;
			for (int j = 0; j < width; j++)
			{
				array[num + j] = bitmap.GetPixel(startX + j, startY + i).A;
			}
		}
		return array;
	}

	private bool TryPackGlyphs(List<RenderedGlyph> glyphs, int atlasWidth, int atlasHeight)
	{
		int num = 8;
		int num2 = 8;
		int num3 = 0;
		for (int i = 0; i < glyphs.Count; i++)
		{
			RenderedGlyph renderedGlyph = glyphs[i];
			if (renderedGlyph.Width + 16 > atlasWidth || renderedGlyph.Height + 16 > atlasHeight)
			{
				return false;
			}
			if (num + renderedGlyph.Width + 8 > atlasWidth)
			{
				num = 8;
				num2 += num3 + 8;
				num3 = 0;
			}
			if (num2 + renderedGlyph.Height + 8 > atlasHeight)
			{
				return false;
			}
			renderedGlyph.AtlasX = num;
			renderedGlyph.AtlasY = num2;
			num += renderedGlyph.Width + 8;
			num3 = Math.Max(num3, renderedGlyph.Height);
		}
		return true;
	}

	private void BlitGlyph(RenderedGlyph glyph, Color32[] atlasPixels, int atlasWidth, int atlasHeight)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < glyph.Height; i++)
		{
			int num = i * glyph.Width;
			int num2 = atlasHeight - glyph.AtlasY - 1 - i;
			int num3 = num2 * atlasWidth + glyph.AtlasX;
			for (int j = 0; j < glyph.Width; j++)
			{
				byte b = glyph.Alpha[num + j];
				ref Color32 reference = ref atlasPixels[num3 + j];
				reference = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b);
			}
		}
	}

	private TMP_FontAsset CreateBitmapFontAsset(Texture2D atlasTexture, List<Glyph> glyphTable, List<TMP_Character> characterTable, string familyName, int unitsPerEm, float ascentPixels, float lineHeightPixels, TMP_FontAsset templateFontAsset)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Expected O, but got Unknown
		//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_030d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0317: Expected O, but got Unknown
		//IL_032c: Unknown result type (might be due to invalid IL or missing references)
		TMP_FontAsset val = ScriptableObject.CreateInstance<TMP_FontAsset>();
		val.atlasPopulationMode = (AtlasPopulationMode)0;
		val.isMultiAtlasTexturesEnabled = false;
		val.fallbackFontAssetTable = new List<TMP_FontAsset>();
		val.atlasTextures = (Texture2D[])(object)new Texture2D[1] { atlasTexture };
		((Object)val).name = "PinkfongBabyShark-Korean-Fallback";
		FaceInfo val2 = default(FaceInfo);
		((FaceInfo)(ref val2)).familyName = familyName;
		((FaceInfo)(ref val2)).styleName = "Regular";
		((FaceInfo)(ref val2)).pointSize = 90;
		((FaceInfo)(ref val2)).scale = 1f;
		((FaceInfo)(ref val2)).ascentLine = ascentPixels;
		((FaceInfo)(ref val2)).capLine = ascentPixels;
		((FaceInfo)(ref val2)).meanLine = ascentPixels * 0.55f;
		((FaceInfo)(ref val2)).baseline = 0f;
		((FaceInfo)(ref val2)).descentLine = 0f - (lineHeightPixels - ascentPixels);
		((FaceInfo)(ref val2)).lineHeight = lineHeightPixels;
		((FaceInfo)(ref val2)).underlineOffset = (0f - lineHeightPixels) * 0.1f;
		((FaceInfo)(ref val2)).underlineThickness = Math.Max(1f, lineHeightPixels * 0.04f);
		((FaceInfo)(ref val2)).strikethroughOffset = ascentPixels * 0.3f;
		((FaceInfo)(ref val2)).strikethroughThickness = Math.Max(1f, lineHeightPixels * 0.04f);
		((FaceInfo)(ref val2)).subscriptOffset = (0f - lineHeightPixels) * 0.2f;
		((FaceInfo)(ref val2)).superscriptOffset = ascentPixels * 0.2f;
		((FaceInfo)(ref val2)).tabWidth = 360f;
		val.faceInfo = val2;
		Shader val3 = null;
		if ((Object)(object)templateFontAsset != (Object)null && (Object)(object)((TMP_Asset)templateFontAsset).material != (Object)null)
		{
			val3 = ((TMP_Asset)templateFontAsset).material.shader;
		}
		if ((Object)(object)val3 == (Object)null)
		{
			val3 = Shader.Find("TextMeshPro/Distance Field");
		}
		if ((Object)(object)val3 == (Object)null)
		{
			val3 = Shader.Find("TextMeshPro/Mobile/Distance Field");
		}
		if ((Object)(object)val3 == (Object)null)
		{
			val3 = Shader.Find("TextMeshPro/Sprite");
		}
		Material val4 = (((Object)(object)templateFontAsset != (Object)null && (Object)(object)((TMP_Asset)templateFontAsset).material != (Object)null) ? new Material(((TMP_Asset)templateFontAsset).material) : new Material(val3));
		val4.shader = val3;
		((Object)val4).name = "PinkfongBabyShark-Korean-Material";
		val4.mainTexture = (Texture)(object)atlasTexture;
		if (val4.HasProperty("_MainTex"))
		{
			val4.SetTexture("_MainTex", (Texture)(object)atlasTexture);
		}
		if (val4.HasProperty("_FaceTex"))
		{
			val4.SetTexture("_FaceTex", (Texture)(object)atlasTexture);
		}
		AccessTools.Field(typeof(TMP_FontAsset), "m_AtlasRenderMode").SetValue(val, (object)(GlyphRenderMode)4122);
		AccessTools.Field(typeof(TMP_FontAsset), "m_AtlasPadding").SetValue(val, 8);
		AccessTools.Field(typeof(TMP_FontAsset), "m_AtlasWidth").SetValue(val, ((Texture)atlasTexture).width);
		AccessTools.Field(typeof(TMP_FontAsset), "m_AtlasHeight").SetValue(val, ((Texture)atlasTexture).height);
		AccessTools.Field(typeof(TMP_FontAsset), "m_AtlasTextureIndex").SetValue(val, 0);
		AccessTools.Field(typeof(TMP_FontAsset), "m_CharacterTable").SetValue(val, characterTable);
		AccessTools.Field(typeof(TMP_FontAsset), "m_GlyphTable").SetValue(val, glyphTable);
		AccessTools.Field(typeof(TMP_FontAsset), "m_FontFeatureTable").SetValue(val, (object?)new TMP_FontFeatureTable());
		AccessTools.Field(typeof(TMP_FontAsset), "m_FaceInfo").SetValue(val, val2);
		AccessTools.Field(typeof(TMP_FontAsset), "m_Version").SetValue(val, "1.1.0");
		if ((Object)(object)templateFontAsset != (Object)null)
		{
			AccessTools.Field(typeof(TMP_FontAsset), "m_SourceFontFile").SetValue(val, templateFontAsset.sourceFontFile);
			AccessTools.Field(typeof(TMP_FontAsset), "m_SourceFontFileGUID").SetValue(val, AccessTools.Field(typeof(TMP_FontAsset), "m_SourceFontFileGUID").GetValue(templateFontAsset));
		}
		AccessTools.Field(typeof(TMP_FontAsset), "m_AtlasTexture").SetValue(val, atlasTexture);
		AccessTools.Field(typeof(TMP_FontAsset), "m_AtlasTextures").SetValue(val, new Texture2D[1] { atlasTexture });
		AccessTools.Field(typeof(TMP_FontAsset), "material").SetValue(val, val4);
		AccessTools.Method(typeof(TMP_FontAsset), "SortAllTables", (Type[])null, (Type[])null).Invoke(val, null);
		AccessTools.Method(typeof(TMP_FontAsset), "InitializeDictionaryLookupTables", (Type[])null, (Type[])null).Invoke(val, null);
		AccessTools.Field(typeof(TMP_FontAsset), "IsFontAssetLookupTablesDirty").SetValue(val, false);
		return val;
	}

	private void ApplyFontFallbackToLoadedAssets()
	{
		ApplyGlobalTmpFallbackSettings();
		TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
		for (int i = 0; i < array.Length; i++)
		{
			if ((Object)(object)FallbackFontAsset != (Object)null)
			{
				EnsureFontFallback(array[i]);
			}
		}
		TMP_Text[] array2;
		Text[] array3;
		try
		{
			array2 = Object.FindObjectsByType<TMP_Text>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			array3 = Object.FindObjectsByType<Text>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		}
		catch
		{
			array2 = Resources.FindObjectsOfTypeAll<TMP_Text>();
			array3 = Resources.FindObjectsOfTypeAll<Text>();
		}
		if (!_hasLoggedObjectCounts)
		{
			_hasLoggedObjectCounts = true;
			PluginLog.LogInfo((object)("Object sweep counts: TMP_Text=" + array2.Length + ", UI.Text=" + array3.Length + ", TMP_FontAsset=" + array.Length));
		}
		foreach (TMP_Text val in array2)
		{
			if (!((Object)(object)val == (Object)null))
			{
				ApplyDirectTextOverride(val, "LoadedSweep");
				EnsureTextComponentReady(val, "LoadedSweep");
				if ((Object)(object)FallbackFontAsset != (Object)null)
				{
					EnsureFontFallback(val.font);
				}
				ApplyTmpTextFont(val, "LoadedSweep");
				val.ForceMeshUpdate(true, true);
			}
		}
		foreach (Text val2 in array3)
		{
			if (!((Object)(object)val2 == (Object)null))
			{
				ApplyDirectTextOverride(val2, "LoadedSweep");
				EnsureUiTextReady(val2, "LoadedSweep");
			}
		}
	}

	private void ApplyGlobalTmpFallbackSettings()
	{
		if ((Object)(object)FallbackFontAsset == (Object)null)
		{
			return;
		}
		try
		{
			bool flag = false;
			List<TMP_FontAsset> list = ResolveTmpSettingsFallbackList();
			if (list != null)
			{
				flag |= RemoveNamedFallbacks(list, FallbackFontAsset);
				if (!list.Contains(FallbackFontAsset))
				{
					list.Insert(0, FallbackFontAsset);
					flag = true;
				}
			}
			TMP_FontAsset val = FindTemplateFontAsset();
			if ((Object)(object)val != (Object)null)
			{
				EnsureFontFallback(val);
				flag = true;
			}
			if (flag && !_hasLoggedGlobalFallback)
			{
				_hasLoggedGlobalFallback = true;
				PluginLog.LogInfo((object)"Registered Korean TMP fallback globally.");
			}
		}
		catch (Exception ex)
		{
			PluginLog.LogWarning((object)("Failed to register TMP global fallback: " + ex));
		}
	}

	private void RemoveStaleKoreanFallbackAssets()
	{
		try
		{
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			foreach (TMP_FontAsset val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)FallbackFontAsset) && val.fallbackFontAssetTable != null)
				{
					RemoveNamedFallbacks(val.fallbackFontAssetTable, FallbackFontAsset);
				}
			}
			List<TMP_FontAsset> list = ResolveTmpSettingsFallbackList();
			if (list != null)
			{
				RemoveNamedFallbacks(list, FallbackFontAsset);
			}
		}
		catch (Exception ex)
		{
			PluginLog.LogWarning((object)("Failed to clean stale Korean TMP fallbacks: " + ex));
		}
	}

	private bool RemoveNamedFallbacks(List<TMP_FontAsset> fallbackTable, TMP_FontAsset keepAsset)
	{
		if (fallbackTable == null)
		{
			return false;
		}
		bool result = false;
		for (int num = fallbackTable.Count - 1; num >= 0; num--)
		{
			TMP_FontAsset val = fallbackTable[num];
			if ((Object)(object)val == (Object)null)
			{
				fallbackTable.RemoveAt(num);
				result = true;
			}
			else if (!((Object)(object)val == (Object)(object)keepAsset) && (string.Equals(((Object)val).name, "PinkfongBabyShark-Korean-Fallback", StringComparison.Ordinal) || string.Equals(((Object)val).name, "PinkfongBabyShark-Korean-Material", StringComparison.Ordinal)))
			{
				fallbackTable.RemoveAt(num);
				result = true;
			}
		}
		return result;
	}

	private List<TMP_FontAsset> ResolveTmpSettingsFallbackList()
	{
		PropertyInfo propertyInfo = AccessTools.Property(typeof(TMP_Settings), "fallbackFontAssets");
		if (propertyInfo != null)
		{
			List<TMP_FontAsset> list = propertyInfo.GetValue(null, null) as List<TMP_FontAsset>;
			if (list == null)
			{
				list = new List<TMP_FontAsset>();
				if (propertyInfo.CanWrite)
				{
					propertyInfo.SetValue(null, list, null);
				}
			}
			if (list != null)
			{
				return list;
			}
		}
		FieldInfo fieldInfo = AccessTools.Field(typeof(TMP_Settings), "m_fallbackFontAssets");
		if (fieldInfo == null)
		{
			return null;
		}
		TMP_Settings obj = (fieldInfo.IsStatic ? null : TMP_Settings.instance);
		List<TMP_FontAsset> list2 = fieldInfo.GetValue(obj) as List<TMP_FontAsset>;
		if (list2 == null)
		{
			list2 = new List<TMP_FontAsset>();
			fieldInfo.SetValue(obj, list2);
		}
		return list2;
	}

	private void ApplyDirectTextOverride(TMP_Text textComponent, string reason)
	{
		if ((Object)(object)textComponent == (Object)null || string.IsNullOrEmpty(textComponent.text))
		{
			return;
		}
		string text = textComponent.text;
		string text2 = NormalizeTmpTextForComponent(textComponent, TryTranslateDirectText(text, reason + ":" + ((Object)textComponent).name));
		if (text2 == text)
		{
			return;
		}
		try
		{
			_isMutatingText = true;
			textComponent.text = text2;
		}
		finally
		{
			_isMutatingText = false;
		}
	}

	private void ApplyDirectTextOverride(Text textComponent, string reason)
	{
		if ((Object)(object)textComponent == (Object)null || string.IsNullOrEmpty(textComponent.text) || _directTextOverrides.Count == 0)
		{
			return;
		}
		string text = textComponent.text;
		string text2 = NormalizeUiTextForComponent(textComponent, TryTranslateDirectText(text, reason + ":" + ((Object)textComponent).name));
		if (text2 == text)
		{
			return;
		}
		try
		{
			_isMutatingText = true;
			textComponent.text = text2;
		}
		finally
		{
			_isMutatingText = false;
		}
	}

	private void EnsureFontFallback(TMP_FontAsset fontAsset)
	{
		EnsureFontFallback(fontAsset, FallbackFontAsset);
	}

	private void EnsureFontFallback(TMP_FontAsset fontAsset, TMP_FontAsset fallbackAsset)
	{
		if (!((Object)(object)fontAsset == (Object)null) && !((Object)(object)fallbackAsset == (Object)null) && !((Object)(object)fontAsset == (Object)(object)fallbackAsset))
		{
			if (fontAsset.fallbackFontAssetTable == null)
			{
				fontAsset.fallbackFontAssetTable = new List<TMP_FontAsset>();
			}
			RemoveNamedFallbacks(fontAsset.fallbackFontAssetTable, fallbackAsset);
			if (!fontAsset.fallbackFontAssetTable.Contains(fallbackAsset))
			{
				fontAsset.fallbackFontAssetTable.Insert(0, fallbackAsset);
			}
		}
	}

	internal void EnsureLocalizationTables()
	{
		LocalizedText.TryInitTables();
		if (LocalizedText.mainTable != null && LocalizedText.mainTable.Count != 0)
		{
			if (!_hasDumpedMainReferenceTable && LocalizedText.mainTable != null && LocalizedText.mainTable.Count > 0)
			{
				DumpReferenceTable(Path.Combine(ReferenceDirectory, "main-reference.tsv"), LocalizedText.mainTable);
				_hasDumpedMainReferenceTable = true;
			}
			if (!_hasDumpedDialogueReferenceTable && LocalizedText.dialogueTable != null && LocalizedText.dialogueTable.Count > 0)
			{
				DumpReferenceTable(Path.Combine(ReferenceDirectory, "dialogue-reference.tsv"), LocalizedText.dialogueTable);
				_hasDumpedDialogueReferenceTable = true;
			}
			int num = ApplyOverrideFile(Path.Combine(LocalizationDirectory, "ko-main.tsv"), LocalizedText.mainTable);
			int num2 = ApplyOverrideFile(Path.Combine(LocalizationDirectory, "ko-dialogue.tsv"), LocalizedText.dialogueTable);
			HasLoadedOverrides = num > 0 || num2 > 0;
			RebuildDirectTextOverrides();
			if (HasLoadedOverrides)
			{
				WarmFallbackWithLoadedKoreanText();
				ForceTargetLanguage("EnsureLocalizationTables");
			}
		}
	}

	private void RebuildDirectTextOverrides()
	{
		_directTextOverrides.Clear();
		BuildDirectTextOverridesFromTable(LocalizedText.mainTable);
		BuildDirectTextOverridesFromTable(LocalizedText.dialogueTable);
		foreach (KeyValuePair<string, string> extraDirectOverride in ExtraDirectOverrides)
		{
			_directTextOverrides[extraDirectOverride.Key] = extraDirectOverride.Value;
		}
	}

	private void BuildDirectTextOverridesFromTable(Dictionary<string, List<string>> table)
	{
		if (table == null)
		{
			return;
		}
		foreach (KeyValuePair<string, List<string>> item in table)
		{
			if (item.Value == null || item.Value.Count == 0)
			{
				continue;
			}
			string text = item.Value[0];
			if (!string.IsNullOrWhiteSpace(text) && item.Value.Count > 1)
			{
				string text2 = item.Value[1];
				if (!string.IsNullOrWhiteSpace(text2) && !(text == text2))
				{
					_directTextOverrides[text] = text2;
				}
			}
		}
	}

	private void WarmFallbackWithLoadedKoreanText()
	{
		HashSet<uint> hashSet = new HashSet<uint>();
		CollectCharacters(LocalizedText.mainTable, hashSet);
		CollectCharacters(LocalizedText.dialogueTable, hashSet);
		EnsureFallbackFontContainsCharacters(hashSet, FindTemplateFontAsset(), "table warmup");
	}

	private void CollectCharacters(Dictionary<string, List<string>> table, HashSet<uint> target)
	{
		if (table == null)
		{
			return;
		}
		foreach (KeyValuePair<string, List<string>> item in table)
		{
			if (item.Value == null || item.Value.Count <= 1)
			{
				continue;
			}
			string text = item.Value[1];
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			foreach (char c in text)
			{
				if (c >= 'ᄀ')
				{
					target.Add(c);
				}
			}
		}
	}

	private static HashSet<uint> CollectCodePoints(string text)
	{
		HashSet<uint> hashSet = new HashSet<uint>();
		if (string.IsNullOrEmpty(text))
		{
			return hashSet;
		}
		for (int i = 0; i < text.Length; i++)
		{
			int num = char.ConvertToUtf32(text, i);
			if (num >= 4352)
			{
				hashSet.Add((uint)num);
			}
			if (char.IsHighSurrogate(text[i]))
			{
				i++;
			}
		}
		return hashSet;
	}

	private int ApplyOverrideFile(string path, Dictionary<string, List<string>> table)
	{
		if (table == null || !File.Exists(path))
		{
			return 0;
		}
		int num = 0;
		string[] array = File.ReadAllLines(path);
		foreach (string text in array)
		{
			if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#"))
			{
				continue;
			}
			string[] array2 = text.Split(new char[1] { '\t' }, 2);
			if (array2.Length < 2)
			{
				continue;
			}
			string text2 = array2[0].Trim();
			string text3 = Unescape(array2[1]);
			if (string.IsNullOrEmpty(text2))
			{
				continue;
			}
			if (!table.TryGetValue(text2, out var value))
			{
				PluginLog.LogWarning((object)("Translation key not found: " + text2));
				continue;
			}
			while (value.Count <= 1)
			{
				value.Add((value.Count > 0) ? value[0] : string.Empty);
			}
			if (!(value[1] == text3))
			{
				value[1] = text3;
				num++;
			}
		}
		return num;
	}

	private void DumpReferenceTable(string path, Dictionary<string, List<string>> table)
	{
		if (table == null)
		{
			return;
		}
		string[] array = new string[10] { "key", "English", "French", "German", "Italian", "SpanishLatam", "BRPortuguese", "Russian", "SimplifiedChinese", "Japanese" };
		using StreamWriter streamWriter = new StreamWriter(path, append: false);
		streamWriter.WriteLine(string.Join("\t", array));
		foreach (KeyValuePair<string, List<string>> item in table.OrderBy<KeyValuePair<string, List<string>>, string>((KeyValuePair<string, List<string>> k) => k.Key, StringComparer.Ordinal))
		{
			List<string> list = new List<string>();
			list.Add(Escape(item.Key));
			for (int i = 0; i < array.Length - 1; i++)
			{
				string value = ((item.Value != null && item.Value.Count > i) ? item.Value[i] : string.Empty);
				list.Add(Escape(value));
			}
			streamWriter.WriteLine(string.Join("\t", list.ToArray()));
		}
	}

	private string Escape(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		return value.Replace("\\", "\\\\").Replace("\r", "\\r").Replace("\n", "\\n")
			.Replace("\t", "\\t");
	}

	private string Unescape(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		return value.Replace("\\t", "\t").Replace("\\n", "\n").Replace("\\r", "\r")
			.Replace("\\\\", "\\");
	}

	internal static void UpdateLanguageDropdown(LanguageSettingUI instance)
	{
		if (!((Object)(object)instance == (Object)null))
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(LanguageSettingUI), "dropdown");
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(LanguageSettingUI), "languages");
			TMP_Dropdown val = (TMP_Dropdown)((fieldInfo != null) ? /*isinst with value type is only supported in some contexts*/: null);
			string[] array = ((fieldInfo2 != null) ? (fieldInfo2.GetValue(instance) as string[]) : null);
			string text = (HasLoadedOverrides ? "Korean" : "Korean (WIP)");
			if (array != null && array.Length > 1)
			{
				array[1] = text;
			}
			if (!((Object)(object)val == (Object)null) && val.options != null && val.options.Count > 1)
			{
				val.options[1].text = text;
				val.RefreshShownValue();
			}
		}
	}
}
[HarmonyPatch(typeof(LocalizedText), "LoadMainTable")]
internal static class LocalizedTextLoadMainTablePatch
{
	private static void Postfix()
	{
		if ((Object)(object)CrashoutCrewKoreanPlugin.Instance != (Object)null)
		{
			CrashoutCrewKoreanPlugin.Instance.RefreshLocalizationAndFonts("LoadMainTable");
		}
	}
}
[HarmonyPatch(typeof(LocalizedText), "RuntimeInitialization")]
internal static class LocalizedTextRuntimeInitializationPatch
{
	private static void Postfix()
	{
		CrashoutCrewKoreanPlugin.EnsureInitializedFromTextHook("RuntimeInitialization");
	}
}
[HarmonyPatch(typeof(LocalizedText), "TryInitTables")]
internal static class LocalizedTextTryInitTablesPatch
{
	private static void Postfix()
	{
		CrashoutCrewKoreanPlugin.EnsureInitializedFromTextHook("TryInitTables");
	}
}
[HarmonyPatch(typeof(LocalizedText), "SetLanguageToSystemLanguage")]
internal static class LocalizedTextSetLanguageToSystemLanguagePatch
{
	private static void Postfix()
	{
		if ((Object)(object)CrashoutCrewKoreanPlugin.Instance != (Object)null)
		{
			CrashoutCrewKoreanPlugin.Instance.ForceTargetLanguage("SetLanguageToSystemLanguage");
		}
	}
}
[HarmonyPatch(typeof(LocalizedText), "SetLanguage", new Type[] { typeof(int) })]
internal static class LocalizedTextSetLanguagePatch
{
	private static void Postfix(int languageInt)
	{
		if ((Object)(object)CrashoutCrewKoreanPlugin.Instance != (Object)null && languageInt != 1)
		{
			CrashoutCrewKoreanPlugin.Instance.ForceTargetLanguage("SetLanguage:" + languageInt);
		}
	}
}
[HarmonyPatch(typeof(LocalizedText), "InitDialogueTable")]
internal static class LocalizedTextInitDialogueTablePatch
{
	private static void Postfix()
	{
		if ((Object)(object)CrashoutCrewKoreanPlugin.Instance != (Object)null)
		{
			CrashoutCrewKoreanPlugin.Instance.RefreshLocalizationAndFonts("InitDialogueTable");
		}
	}
}
[HarmonyPatch(typeof(LocalizedText), "OnEnable")]
internal static class LocalizedTextOnEnablePatch
{
	private static void Postfix()
	{
		CrashoutCrewKoreanPlugin.EnsureInitializedFromTextHook("LocalizedText.OnEnable");
	}
}
[HarmonyPatch(typeof(LocalizedText), "RefreshText")]
internal static class LocalizedTextRefreshTextPatch
{
	private static void Postfix()
	{
		CrashoutCrewKoreanPlugin.EnsureInitializedFromTextHook("LocalizedText.RefreshText");
	}
}
[HarmonyPatch(typeof(LanguageSettingUI), "Refresh")]
internal static class LanguageSettingUIRefreshPatch
{
	private static void Postfix(LanguageSettingUI __instance)
	{
		CrashoutCrewKoreanPlugin.UpdateLanguageDropdown(__instance);
	}
}
[HarmonyPatch(typeof(LanguageSettingUI), "Set")]
internal static class LanguageSettingUISetPatch
{
	private static void Postfix(LanguageSettingUI __instance)
	{
		CrashoutCrewKoreanPlugin.UpdateLanguageDropdown(__instance);
	}
}
[HarmonyPatch(typeof(LocalizedText), "GetText", new Type[]
{
	typeof(string),
	typeof(Text)
})]
internal static class LocalizedTextGetTextForTextPatch
{
	private static void Postfix(string id)
	{
		CrashoutCrewKoreanPlugin.EnsureInitializedFromTextHook("GetTextText:" + id);
	}
}
[HarmonyPatch(typeof(LocalizedText), "GetText", new Type[]
{
	typeof(string),
	typeof(TextMeshPro)
})]
internal static class LocalizedTextGetTextForTmpPatch
{
	private static void Postfix(string id, TextMeshPro text)
	{
		CrashoutCrewKoreanPlugin.EnsureInitializedFromTextHook("GetTextTMP:" + id);
		CrashoutCrewKoreanPlugin.EnsureTextComponentReady((TMP_Text)(object)text, "LocalizedText.GetTextTMP");
	}
}
[HarmonyPatch(typeof(LocalizedText), "GetText", new Type[]
{
	typeof(string),
	typeof(bool)
})]
internal static class LocalizedTextGetTextPatch
{
	private static void Postfix(string id)
	{
		CrashoutCrewKoreanPlugin.EnsureInitializedFromTextHook("GetText:" + id);
	}
}
[HarmonyPatch(typeof(LocalizedText), "GetText", new Type[]
{
	typeof(string),
	typeof(TextMeshProUGUI)
})]
internal static class LocalizedTextGetTextForTmpUguiPatch
{
	private static void Postfix(string id, TextMeshProUGUI text)
	{
		CrashoutCrewKoreanPlugin.EnsureInitializedFromTextHook("GetTextTMPUGUI:" + id);
		CrashoutCrewKoreanPlugin.EnsureTextComponentReady((TMP_Text)(object)text, "LocalizedText.GetTextTMPUGUI");
	}
}
[HarmonyPatch(typeof(TMP_Text), "ParseInputText")]
internal static class TmpTextParseInputTextPatch
{
	private static void Prefix(TMP_Text __instance)
	{
		CrashoutCrewKoreanPlugin.EnsureTextComponentReady(__instance, "TMP.ParseInputText");
	}
}
[HarmonyPatch(typeof(TMP_Text), "set_text")]
internal static class TmpTextSetTextPropertyPatch
{
	private static void Prefix(TMP_Text __instance, ref string value)
	{
		value = CrashoutCrewKoreanPlugin.TranslateIncomingText(__instance, value, "TMP.set_text");
		CrashoutCrewKoreanPlugin.EnsureIncomingTextReady(__instance, value, "TMP.set_text");
	}
}
[HarmonyPatch(typeof(TMP_Text), "SetText", new Type[]
{
	typeof(string),
	typeof(bool)
})]
internal static class TmpTextSetTextMethodPatch
{
	private static void Prefix(TMP_Text __instance, ref string sourceText)
	{
		sourceText = CrashoutCrewKoreanPlugin.TranslateIncomingText(__instance, sourceText, "TMP.SetText");
		CrashoutCrewKoreanPlugin.EnsureIncomingTextReady(__instance, sourceText, "TMP.SetText");
	}
}
[HarmonyPatch(typeof(TMP_Dropdown), "RefreshShownValue")]
internal static class TmpDropdownRefreshShownValuePatch
{
	private static void Postfix(TMP_Dropdown __instance)
	{
		if (!((Object)(object)__instance == (Object)null))
		{
			CrashoutCrewKoreanPlugin.RefreshTmpTextComponent(__instance.captionText, "TMP_Dropdown.RefreshShownValue:caption");
			CrashoutCrewKoreanPlugin.RefreshTmpTextComponent(__instance.itemText, "TMP_Dropdown.RefreshShownValue:item");
		}
	}
}
[HarmonyPatch(typeof(TMP_Dropdown), "Show")]
internal static class TmpDropdownShowPatch
{
	private static void Postfix(TMP_Dropdown __instance)
	{
		if (!((Object)(object)__instance == (Object)null))
		{
			CrashoutCrewKoreanPlugin.RefreshTmpTextComponent(__instance.captionText, "TMP_Dropdown.Show:caption");
			CrashoutCrewKoreanPlugin.RefreshTmpTextComponent(__instance.itemText, "TMP_Dropdown.Show:item");
		}
	}
}
[HarmonyPatch(typeof(TMP_Text), "LoadFontAsset")]
internal static class TmpTextLoadFontAssetPatch
{
	private static void Postfix(TMP_Text __instance)
	{
		CrashoutCrewKoreanPlugin.RefreshTmpTextComponent(__instance, "TMP.LoadFontAsset");
	}
}
[HarmonyPatch(typeof(TextMeshProUGUI), "OnEnable")]
internal static class TmpTextUguiOnEnablePatch
{
	private static void Postfix(TextMeshProUGUI __instance)
	{
		CrashoutCrewKoreanPlugin.RefreshTmpTextComponent((TMP_Text)(object)__instance, "TMPUGUI.OnEnable");
	}
}
[HarmonyPatch(typeof(TextMeshProUGUI), "OnPreRenderCanvas")]
internal static class TmpTextUguiOnPreRenderCanvasPatch
{
	private static void Prefix(TextMeshProUGUI __instance)
	{
		CrashoutCrewKoreanPlugin.RefreshTmpTextComponent((TMP_Text)(object)__instance, "TMPUGUI.OnPreRenderCanvas");
	}
}
[HarmonyPatch(typeof(TextMeshPro), "OnEnable")]
internal static class TmpText3dOnEnablePatch
{
	private static void Postfix(TextMeshPro __instance)
	{
		CrashoutCrewKoreanPlugin.RefreshTmpTextComponent((TMP_Text)(object)__instance, "TMP3D.OnEnable");
	}
}
[HarmonyPatch(typeof(Text), "set_text")]
internal static class UiTextSetTextPropertyPatch
{
	private static void Prefix(Text __instance, ref string value)
	{
		value = CrashoutCrewKoreanPlugin.TranslateIncomingText(__instance, value, "UI.Text.set_text");
		CrashoutCrewKoreanPlugin.EnsureIncomingUiTextReady(__instance, value, "UI.Text.set_text");
	}
}
[HarmonyPatch(typeof(Text), "OnEnable")]
internal static class UiTextOnEnablePatch
{
	private static void Postfix(Text __instance)
	{
		CrashoutCrewKoreanPlugin.RefreshUiTextComponent(__instance, "UI.Text.OnEnable");
	}
}
[HarmonyPatch(typeof(Text), "OnPopulateMesh", new Type[] { typeof(VertexHelper) })]
internal static class UiTextOnPopulateMeshPatch
{
	private static void Prefix(Text __instance)
	{
		CrashoutCrewKoreanPlugin.RefreshUiTextComponent(__instance, "UI.Text.OnPopulateMesh");
	}
}