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");
}
}