using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("TABSKorean")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("TABSKorean")] [assembly: AssemblyTitle("TABSKorean")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TABSKorean { public static class KoreanLanguage { public const int LanguageIndex = 9; public static bool Enabled { get; private set; } public static void Enable() { Enabled = true; } public static void Disable() { Enabled = false; } } [HarmonyPatch(typeof(GlobalSettingsHandler), "GetSettingsInstance", new Type[] { typeof(string) })] internal static class GlobalSettingsHandler_GetSettingsInstance_Patch { private static void Postfix(string settingsKey, ref SettingsInstance __result) { if (__result != null && !(settingsKey != "VIDEO_LANGUAGE")) { AddKoreanOption(__result); } } private static void AddKoreanOption(SettingsInstance settings) { if (settings.options == null) { Plugin.Log.LogWarning((object)"VIDEO_LANGUAGE has no options array."); } else { if (settings.options.Length > 9 && settings.options[9] == "한국어") { return; } if (settings.options.Length > 9) { Plugin.Log.LogWarning((object)"VIDEO_LANGUAGE already has an option at index 9. Korean option was not overwritten."); return; } string[] array = new string[10]; for (int i = 0; i < settings.options.Length; i++) { array[i] = settings.options[i]; } array[9] = "한국어(Korean)"; settings.options = array; Plugin.Log.LogInfo((object)"Added Korean language option to VIDEO_LANGUAGE."); } } } internal static class KoreanLanguageSwitchPath { public const int LanguageIndex = 9; public static bool Enabled { get; private set; } public static void Enable() { Enabled = true; } public static void Disable() { Enabled = false; } } [HarmonyPatch(typeof(Localizer), "SetLanguage")] internal static class Localizer_SetLanguage_Patch { private static readonly FieldInfo CallbacksField = typeof(Localizer).GetField("m_callbacks", BindingFlags.Static | BindingFlags.NonPublic); private static bool Prefix(Language lang) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Expected I4, but got Unknown int num = (int)lang; Plugin.Log.LogInfo((object)$"Localizer.SetLanguage called: {num}"); if (num == 9) { KoreanLanguage.Enable(); SetKoreanCulture(); RefreshLocalization(); Plugin.Log.LogInfo((object)"Korean language enabled."); return false; } KoreanLanguage.Disable(); Plugin.Log.LogInfo((object)$"Korean language disabled. Using TABS language {num}."); return true; } private static void SetKoreanCulture() { try { CultureInfo cultureInfo = CultureInfo.GetCultureInfo("ko-KR"); Thread.CurrentThread.CurrentCulture = cultureInfo; Thread.CurrentThread.CurrentUICulture = cultureInfo; } catch (CultureNotFoundException) { Plugin.Log.LogWarning((object)"Could not set Korean culture."); } } private static void RefreshLocalization() { if (CallbacksField == null) { Plugin.Log.LogWarning((object)"Could not find Localizer.m_callbacks."); return; } try { object value = CallbacksField.GetValue(null); if (value == null) { Plugin.Log.LogWarning((object)"Localizer.m_callbacks is null."); return; } if (!(value is IDictionary dictionary)) { Plugin.Log.LogWarning((object)"Localizer.m_callbacks is not an IDictionary."); return; } List list = new List(); foreach (DictionaryEntry item2 in dictionary) { if (item2.Value is Delegate item) { list.Add(item); } } Plugin.Log.LogInfo((object)$"Refreshing {list.Count} localization callbacks."); foreach (Delegate item3 in list) { try { item3.DynamicInvoke(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Localization callback failed: " + ex.Message)); } } } catch (Exception ex2) { Plugin.Log.LogError((object)("Failed to refresh localization: " + ex2)); } } } internal static class KoreanLocalization { private static readonly Dictionary Translations = new Dictionary(StringComparer.Ordinal); private static string FilePath; public static int Count => Translations.Count; public static void Initialize() { try { string directoryName = Path.GetDirectoryName(typeof(KoreanLocalization).Assembly.Location); string path = Path.Combine(directoryName, "Localization"); FilePath = Path.Combine(path, "tabs_ko_kr.txt"); LoadFile(FilePath); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to initialize Korean localization: {arg}"); } } private static void LoadFile(string path) { Translations.Clear(); if (!File.Exists(path)) { Plugin.Log.LogError((object)"Korean localization file was not found."); Plugin.Log.LogError((object)("Expected path: " + path)); return; } string[] array = File.ReadAllLines(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); int num = 0; int num2 = 0; string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (string.IsNullOrEmpty(text2) || text2.StartsWith("#")) { continue; } int num3 = text2.IndexOf('='); if (num3 <= 0) { num2++; Plugin.Log.LogWarning((object)("Invalid localization line: " + text)); continue; } string text3 = text2.Substring(0, num3).Trim(); string value = text2.Substring(num3 + 1); if (string.IsNullOrEmpty(text3)) { num2++; continue; } Translations[text3] = value; num++; } Plugin.Log.LogInfo((object)$"Korean localization loaded: {num} entries."); if (num2 > 0) { Plugin.Log.LogWarning((object)$"Skipped {num2} invalid localization lines."); } } public static bool TryGet(string key, out string value) { return Translations.TryGetValue(key, out value); } public static string Translate(string phraseID, string[] args) { if (string.IsNullOrEmpty(phraseID)) { return phraseID; } if (!TryGet(phraseID, out var value)) { return null; } value = ResolveNestedLocalization(value); if (args != null && args.Length != 0) { string[] array = new string[args.Length]; for (int i = 0; i < args.Length; i++) { array[i] = args[i]; if (!string.IsNullOrEmpty(array[i]) && array[i].Length > 1 && array[i][0] == '$') { string phraseID2 = array[i].Substring(1); string text = Translate(phraseID2, null); if (text != null) { array[i] = text; } } } try { string format = value; object[] args2 = array; value = string.Format(format, args2); } catch (FormatException) { Plugin.Log.LogWarning((object)("Invalid format string for key: " + phraseID)); } } return value; } private static string ResolveNestedLocalization(string text) { if (string.IsNullOrEmpty(text)) { return text; } for (int i = 0; i < 32; i++) { int num = text.IndexOf('$'); if (num < 0) { break; } int num2 = text.IndexOf('$', num + 1); if (num2 < 0) { break; } string text2 = text.Substring(num + 1, num2 - num - 1); if (string.IsNullOrEmpty(text2) || !TryGet(text2, out var value)) { break; } value = ResolveNestedLocalization(value); string oldValue = text.Substring(num, num2 - num + 1); text = text.Replace(oldValue, value); } return text; } } [HarmonyPatch(typeof(Localizer), "GetSinglePhrase", new Type[] { typeof(string), typeof(string[]) })] internal static class Localizer_GetSinglePhrase_Patch { private static bool Prefix(string phraseID, string[] args, ref string __result) { if (!KoreanLanguage.Enabled) { return true; } string text = KoreanLocalization.Translate(phraseID, args); if (text == null) { return true; } __result = text; return false; } } [BepInPlugin("com.anhyeongseop.tabs.korean", "TABS Korean", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string GUID = "com.anhyeongseop.tabs.korean"; public const string NAME = "TABS Korean"; public const string VERSION = "1.0.0"; internal static ManualLogSource Log; internal static Harmony HarmonyInstance; private void Awake() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"================================="); Log.LogInfo((object)"TABS Korean starting..."); Log.LogInfo((object)"================================="); KoreanLocalization.Initialize(); HarmonyInstance = new Harmony("com.anhyeongseop.tabs.korean"); HarmonyInstance.PatchAll(); Log.LogInfo((object)"TABS Korean loaded successfully."); Log.LogInfo((object)$"Loaded Korean strings: {KoreanLocalization.Count}"); } private void OnDestroy() { if (HarmonyInstance != null) { HarmonyInstance.UnpatchSelf(); HarmonyInstance = null; } ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"TABS Korean unloaded."); } } } }