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 ExtraDirectOverrides = new Dictionary(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 \n2 SHIFTS", "2번의 근무 동안" }, { "NEXT\n2 SHIFTS", "2번의 근무 동안" }, { "다음 \\\\n2번 근무", "2번의 근무 동안" }, { "다음 \\n2번 근무", "2번의 근무 동안" }, { "다음\n2회차", "2번의 근무 동안" }, { "2회차", "2번의 근무 동안" }, { "2번 근무", "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 _seenTextTraces = new HashSet(StringComparer.Ordinal); private readonly HashSet _seenRuntimeUntranslated = new HashSet(StringComparer.Ordinal); private readonly Dictionary _directTextOverrides = new Dictionary(StringComparer.Ordinal); private readonly HashSet _preparedCodePoints = new HashSet(); 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(); ((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 list = new List(); 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(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 GetSystemFontCandidates() { List list = new List(); 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 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 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 list = new List(); 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(StringComparer.OrdinalIgnoreCase).ToList(); } private Font TryCreateDynamicFontFromNames(IEnumerable candidates, int size) { string[] array = candidates.Where((string name) => !string.IsNullOrWhiteSpace(name)).Distinct(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(); foreach (TMP_FontAsset val in array) { if (!((Object)(object)val == (Object)null) && ((Object)val).name.IndexOf("ChelseaMarket", StringComparison.OrdinalIgnoreCase) >= 0) { return val; } } return ((IEnumerable)array).FirstOrDefault((Func)((TMP_FontAsset f) => (Object)(object)f != (Object)null)); } private void EnsureFallbackFontContainsCharacters(IEnumerable codePoints, TMP_FontAsset templateFontAsset, string reason) { HashSet hashSet = new HashSet(); 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 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 list = new List(); 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 list2 = new List(list.Count); List list3 = new List(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 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 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(); 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 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 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 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 glyphTable, List 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(); val.atlasPopulationMode = (AtlasPopulationMode)0; val.isMultiAtlasTexturesEnabled = false; val.fallbackFontAssetTable = new List(); 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(); 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((FindObjectsInactive)1, (FindObjectsSortMode)0); array3 = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); } catch { array2 = Resources.FindObjectsOfTypeAll(); array3 = Resources.FindObjectsOfTypeAll(); } 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 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(); 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 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 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 ResolveTmpSettingsFallbackList() { PropertyInfo propertyInfo = AccessTools.Property(typeof(TMP_Settings), "fallbackFontAssets"); if (propertyInfo != null) { List list = propertyInfo.GetValue(null, null) as List; if (list == null) { list = new List(); 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 list2 = fieldInfo.GetValue(obj) as List; if (list2 == null) { list2 = new List(); 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(); } 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 extraDirectOverride in ExtraDirectOverrides) { _directTextOverrides[extraDirectOverride.Key] = extraDirectOverride.Value; } } private void BuildDirectTextOverridesFromTable(Dictionary> table) { if (table == null) { return; } foreach (KeyValuePair> 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 hashSet = new HashSet(); CollectCharacters(LocalizedText.mainTable, hashSet); CollectCharacters(LocalizedText.dialogueTable, hashSet); EnsureFallbackFontContainsCharacters(hashSet, FindTemplateFontAsset(), "table warmup"); } private void CollectCharacters(Dictionary> table, HashSet target) { if (table == null) { return; } foreach (KeyValuePair> 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 CollectCodePoints(string text) { HashSet hashSet = new HashSet(); 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> 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> 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> item in table.OrderBy>, string>((KeyValuePair> k) => k.Key, StringComparer.Ordinal)) { List list = new List(); 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"); } }