using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Logging; using Febucci.UI; using Febucci.UI.Core; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TMPro; using UnityEngine; using UnityEngine.LowLevel; using UnityEngine.PlayerLoop; using UnityEngine.UI; using WKMSTranslation.Core; using WKMSTranslation.Utils; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("WKMSTranslation")] [assembly: AssemblyDescription("Русификатор под White Knuckle")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Musya Team")] [assembly: AssemblyProduct("")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyFileVersion("1.3.0")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 WKMSTranslation { [BepInPlugin("com.musya.wk.translation", "WKMSTranslation", "1.3.3")] public class Plugin : BaseUnityPlugin { private void Awake() { string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); TranslationEngine.Initialize(directoryName); FontManager.Initialize(((BaseUnityPlugin)this).Logger, directoryName); Exporter.Initialize(directoryName); Harmony.CreateAndPatchAll(typeof(Plugin).Assembly, (string)null); PlayerLoopHelper.Inject(typeof(Plugin), MyUpdate); } private void MyUpdate() { if (Input.GetKeyDown((KeyCode)290)) { TranslationEngine.IsEnabled = !TranslationEngine.IsEnabled; RefreshUI(forceReset: true); } if (Input.GetKeyDown((KeyCode)291)) { TranslationEngine.Load(); RefreshUI(forceReset: false); } if (Input.GetKeyDown((KeyCode)292)) { Exporter.Save(); } } private void RefreshUI(bool forceReset) { TextProcessor.IsPatching = true; TMP_Text[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_Text val in array) { if (!((Component)val).gameObject.activeInHierarchy || !IsSafe((Component)(object)val)) { continue; } string original = TranslationEngine.GetOriginal(val, val.text); string text = (TranslationEngine.IsEnabled ? TranslationEngine.GetTranslation(original) : original); TAnimCore component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { if (HasTypewriter((Component)(object)val)) { continue; } if (TranslationEngine.IsAssignedTranslation(val, text)) { if (TranslationEngine.IsEnabled) { FontManager.TryReplace(val); } continue; } TranslationEngine.SetAssignedTranslation(val, text); component.textFull = text; if (TranslationEngine.IsEnabled) { FontManager.TryReplace(val); } continue; } if (TranslationEngine.IsAssignedTranslation(val, text)) { if (TranslationEngine.IsEnabled) { FontManager.TryReplace(val); if (TranslationEngine.IsFitRequired(original.GetExactKey())) { TextProcessor.FitTMP(val); } } continue; } TranslationEngine.SetAssignedTranslation(val, text); val.text = text; if (TranslationEngine.IsEnabled) { FontManager.TryReplace(val); if (TranslationEngine.IsFitRequired(original.GetExactKey())) { TextProcessor.FitTMP(val); } } val.ForceMeshUpdate(true, true); } Text[] array2 = Resources.FindObjectsOfTypeAll(); foreach (Text val2 in array2) { if (!((Component)val2).gameObject.activeInHierarchy || !IsSafe((Component)(object)val2)) { continue; } string original2 = TranslationEngine.GetOriginal(val2, val2.text); string text2 = (TranslationEngine.IsEnabled ? TranslationEngine.GetTranslation(original2) : original2); if (TranslationEngine.IsAssignedTranslation(val2, text2)) { if (TranslationEngine.IsEnabled && TranslationEngine.IsFitRequired(original2.GetExactKey())) { FitLegacyTextManually(val2); } continue; } TranslationEngine.SetAssignedTranslation(val2, text2); val2.text = text2; if (TranslationEngine.IsEnabled && TranslationEngine.IsFitRequired(original2.GetExactKey())) { FitLegacyTextManually(val2); } ((Behaviour)val2).enabled = false; ((Behaviour)val2).enabled = true; try { val2.FontTextureChanged(); } catch { } } InputField[] array3 = Resources.FindObjectsOfTypeAll(); foreach (InputField val3 in array3) { if (((Component)val3).gameObject.activeInHierarchy) { string original3 = TranslationEngine.GetOriginal(val3, val3.text); string text3 = (TranslationEngine.IsEnabled ? TranslationEngine.GetTranslation(original3) : original3); if (!TranslationEngine.IsAssignedTranslation(val3, text3)) { TranslationEngine.SetAssignedTranslation(val3, text3); val3.text = text3; } } } TMP_InputField[] array4 = Resources.FindObjectsOfTypeAll(); foreach (TMP_InputField val4 in array4) { if (((Component)val4).gameObject.activeInHierarchy) { string original4 = TranslationEngine.GetOriginal(val4, val4.text); string text4 = (TranslationEngine.IsEnabled ? TranslationEngine.GetTranslation(original4) : original4); if (!TranslationEngine.IsAssignedTranslation(val4, text4)) { TranslationEngine.SetAssignedTranslation(val4, text4); val4.text = text4; } } } TextProcessor.IsPatching = false; } private void FitLegacyTextManually(Text t) { if ((Object)(object)t == (Object)null) { return; } try { ContentSizeFitter component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null) { component.horizontalFit = (FitMode)0; } t.horizontalOverflow = (HorizontalWrapMode)0; t.verticalOverflow = (VerticalWrapMode)0; if (!t.resizeTextForBestFit) { t.resizeTextMinSize = Mathf.Max(1, Mathf.RoundToInt((float)t.fontSize * 0.4f)); t.resizeTextMaxSize = t.fontSize; t.resizeTextForBestFit = true; } } catch { } } private bool HasTypewriter(Component c) { if ((Object)(object)c == (Object)null || (Object)(object)c.gameObject == (Object)null) { return false; } Component[] components = c.gameObject.GetComponents(); foreach (Component val in components) { if ((Object)(object)val != (Object)null && ((object)val).GetType().Name.Contains("Typewriter")) { return true; } } return false; } private bool IsSafe(Component c) { UT_TextScrawl val = c.GetComponentInParent() ?? c.GetComponent(); if ((Object)(object)val == (Object)null) { return true; } FieldInfo field = typeof(UT_TextScrawl).GetField("typing", BindingFlags.Instance | BindingFlags.NonPublic); return field == null || (bool)field.GetValue(val); } } } namespace WKMSTranslation.Utils { public static class Extensions { public static string GetExactKey(this string str) { if (string.IsNullOrEmpty(str)) { return string.Empty; } return str.Replace("\r\n", "\\n").Replace("\n", "\\n").Replace("\r", "") .Replace("\0", "") .Replace("\u200b", "") .Replace("\u00a0", "") .Replace("", "") .Trim(); } } public static class PlayerLoopHelper { public static void Inject(Type ownerType, Action updateAction) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_00fa: Expected O, but got Unknown PlayerLoopSystem currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); UpdateFunction val2 = default(UpdateFunction); for (int i = 0; i < currentPlayerLoop.subSystemList.Length; i++) { if (!(currentPlayerLoop.subSystemList[i].type == typeof(Update))) { continue; } List list = new List(); if (currentPlayerLoop.subSystemList[i].subSystemList != null) { list.AddRange(currentPlayerLoop.subSystemList[i].subSystemList); } foreach (PlayerLoopSystem item2 in list) { if (item2.type == ownerType) { return; } } PlayerLoopSystem val = new PlayerLoopSystem { type = ownerType }; UpdateFunction obj = val2; if (obj == null) { UpdateFunction val3 = delegate { updateAction(); }; UpdateFunction val4 = val3; val2 = val3; obj = val4; } val.updateDelegate = obj; PlayerLoopSystem item = val; list.Add(item); currentPlayerLoop.subSystemList[i].subSystemList = list.ToArray(); PlayerLoop.SetPlayerLoop(currentPlayerLoop); Debug.Log((object)("[WKMSTranslation] PlayerLoop injected successfully for " + ownerType.Name)); break; } } } } namespace WKMSTranslation.Hooks { public static class ThirdPartyHooks { [HarmonyPatch(typeof(UT_TextScrawl), "ShowText")] public static class ScrawlHook { [HarmonyPrefix] public static void Prefix(ref string s) { if (!string.IsNullOrWhiteSpace(s)) { s = TranslationEngine.GetTranslation(s); } ThirdPartyDepth++; } [HarmonyFinalizer] public static void Finalizer() { ThirdPartyDepth--; } } [HarmonyPatch(typeof(TypewriterCore), "ShowText", new Type[] { typeof(string) })] public static class TypewriterCore_ShowText_Hook { [HarmonyPrefix] public static void Prefix(ref string text) { if (!string.IsNullOrWhiteSpace(text)) { text = TranslationEngine.GetTranslation(text); } ThirdPartyDepth++; } [HarmonyFinalizer] public static void Finalizer() { ThirdPartyDepth--; } } [HarmonyPatch(typeof(TAnimCore), "ConvertText")] public static class TAnimCore_ConvertText_Hook { [HarmonyPrefix] public static void Prefix(ref string textToParse) { if (!string.IsNullOrWhiteSpace(textToParse)) { textToParse = TranslationEngine.GetTranslation(textToParse); } ThirdPartyDepth++; } [HarmonyFinalizer] public static void Finalizer() { ThirdPartyDepth--; } } [HarmonyPatch(typeof(TextAnimator_TMP), "SetTextToSource")] public static class TextAnimator_SetText_Hook { [HarmonyPrefix] public static void Prefix() { ThirdPartyDepth++; } [HarmonyFinalizer] public static void Finalizer(TextAnimator_TMP __instance) { ThirdPartyDepth--; if ((Object)(object)__instance != (Object)null && (Object)(object)__instance.TMProComponent != (Object)null && TranslationEngine.IsEnabled) { FontManager.TryReplace(__instance.TMProComponent); } } } [ThreadStatic] public static int ThirdPartyDepth; } [HarmonyPatch] public static class UnityHooks { [HarmonyPatch(typeof(TextMeshProUGUI), "Awake")] [HarmonyPostfix] public static void UGUI_Awake(TextMeshProUGUI __instance) { SyncTMP((TMP_Text)(object)__instance); } [HarmonyPatch(typeof(TextMeshProUGUI), "OnEnable")] [HarmonyPostfix] public static void UGUI_Enable(TextMeshProUGUI __instance) { SyncTMP((TMP_Text)(object)__instance); } [HarmonyPatch(typeof(TextMeshPro), "Awake")] [HarmonyPostfix] public static void World_Awake(TextMeshPro __instance) { SyncTMP((TMP_Text)(object)__instance); } [HarmonyPatch(typeof(TextMeshPro), "OnEnable")] [HarmonyPostfix] public static void World_Enable(TextMeshPro __instance) { SyncTMP((TMP_Text)(object)__instance); } [HarmonyPatch(typeof(Text), "OnEnable")] [HarmonyPostfix] public static void Legacy_Enable(Text __instance) { SyncLegacy(__instance); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] public static void TMP_Setter_Prefix(TMP_Text __instance, ref string value) { if (ThirdPartyHooks.ThirdPartyDepth <= 0) { TextProcessor.Process(__instance, ref value); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] public static void Legacy_Setter_Prefix(Text __instance, ref string value) { TextProcessor.Process(__instance, ref value); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] public static void Legacy_Setter_Postfix(Text __instance) { TextProcessor.PostProcess(__instance); try { if (__instance != null) { __instance.FontTextureChanged(); } } catch { } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] public static void LegacyInputField_Setter_Prefix(InputField __instance, ref string value) { if (ThirdPartyHooks.ThirdPartyDepth <= 0) { TextProcessor.Process(__instance, ref value); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] public static void TMPInputField_Setter_Prefix(TMP_InputField __instance, ref string value) { if (ThirdPartyHooks.ThirdPartyDepth <= 0) { TextProcessor.Process(__instance, ref value); } } private static void SyncTMP(TMP_Text component) { if (!((Object)(object)component == (Object)null)) { string text = component.text; TextProcessor.Process(component, ref text); if (text != component.text) { TextProcessor.IsPatching = true; component.text = text; TextProcessor.IsPatching = false; } TextProcessor.PostProcess(component); } } private static void SyncLegacy(Text component) { if ((Object)(object)component == (Object)null) { return; } string text = component.text; TextProcessor.Process(component, ref text); if (text != component.text) { TextProcessor.IsPatching = true; component.text = text; TextProcessor.IsPatching = false; } TextProcessor.PostProcess(component); try { component.FontTextureChanged(); } catch { } } } } namespace WKMSTranslation.Core { public static class Exporter { private static string _folder = ""; private static readonly HashSet _exported = new HashSet(StringComparer.Ordinal); private static readonly Regex _numFinder = new Regex("\\d+(?:[.,]\\d+)?", RegexOptions.Compiled); private static readonly Regex _ghostFilter = new Regex("[a-zA-Z]\\d+$", RegexOptions.Compiled); private static readonly Regex _gameTokenFinder = new Regex("\\{[a-zA-Z_][a-zA-Z0-9_]*\\}", RegexOptions.Compiled); private static readonly Regex _garbageFilter = new Regex("^[.\\-_~=\\s\\/\\\\|:;()\\[\\]{}*+?!@#$%^&<>,\"']*$", RegexOptions.Compiled); private static readonly HashSet _ignoredKeywords = new HashSet(StringComparer.OrdinalIgnoreCase) { "Shift", "Tab", "Space", "Ctrl", "Alt", "Enter", "Escape", "Caps", "Backspace", "LMB", "RMB", "MMB", "Wheel", "WheelUp", "WheelDown", "M4", "M5", "Up", "Down", "Left", "Right", "W", "A", "S", "D", "Insert", "Delete", "Home", "End", "PageUp", "PageDown", "PrintScreen", "ScrollLock", "Pause", "NumLock", "NumSlash", "NumStar", "NumMinus", "NumPlus", "NumEnter", "Num0", "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Win", "Cmd", "Fn", "Tilde", "LB", "RB", "LT", "RT", "L1", "R1", "L2", "R2", "L3", "R3", "D-PadUp", "D-PadDown", "D-PadLeft", "D-PadRight", "A", "B", "X", "Y", "Cross", "Circle", "Square", "Triangle", "LS", "RS", "LS-Click", "RS-Click", "LeftStick", "RightStick", "Start", "Select", "Back", "Options", "Share", "Menu", "View", "Guide", "Home", "Touchpad" }; public static void Initialize(string path) { _folder = path; } public static void Register(string text) { if (string.IsNullOrWhiteSpace(text) || Regex.IsMatch(text, "[а-яА-Я]")) { return; } string exactKey = text.GetExactKey(); if (exactKey.Length <= 1 || _garbageFilter.IsMatch(exactKey) || (exactKey.StartsWith("<") && exactKey.EndsWith(">") && !exactKey.Contains(" ")) || _ignoredKeywords.Contains(exactKey) || Regex.IsMatch(exactKey, "^[+-]?\\d+(?:[.,]\\d+)?$") || Regex.IsMatch(exactKey, "^\\d{1,2}(?::\\d{1,2}){1,3}$") || _ghostFilter.IsMatch(exactKey) || _gameTokenFinder.IsMatch(exactKey) || TranslationEngine.IsTranslated(text)) { return; } string text2 = ConvertToTemplate(exactKey); if (TranslationEngine.IsTranslated(text2)) { return; } lock (_exported) { _exported.Add(text2); } } private static string ConvertToTemplate(string text) { if (string.IsNullOrEmpty(text)) { return text; } if (!Regex.IsMatch(text, "\\d")) { return text; } int index = 0; return _numFinder.Replace(text, (Match m) => $"{{{index++}}}"); } public static void Save() { CollectFromMemory(); try { if (string.IsNullOrEmpty(_folder)) { return; } Directory.CreateDirectory(_folder); string path = Path.Combine(_folder, "DumpedTexts.json"); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (File.Exists(path)) { try { Dictionary dictionary2 = JsonConvert.DeserializeObject>(File.ReadAllText(path)); if (dictionary2 != null) { dictionary = dictionary2; } } catch { } } int num = 0; lock (_exported) { foreach (string item in _exported) { if (!dictionary.ContainsKey(item) && !TranslationEngine.IsTranslatedDeep(item)) { dictionary[item] = ""; num++; } } } File.WriteAllText(path, JsonConvert.SerializeObject((object)dictionary, (Formatting)1)); Debug.Log((object)$"[Exporter] Saved {num} new strings. Total: {dictionary.Count}"); } catch (Exception ex) { Debug.LogError((object)ex); } } private static void CollectFromMemory() { try { TMP_Text[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_Text val in array) { if (((Component)val).gameObject.activeInHierarchy && !string.IsNullOrEmpty(val.text)) { Register(val.text); } } Text[] array2 = Resources.FindObjectsOfTypeAll(); foreach (Text val2 in array2) { if (((Component)val2).gameObject.activeInHierarchy && !string.IsNullOrEmpty(val2.text)) { Register(val2.text); } } } catch { } } } public static class FontManager { private static readonly Dictionary _tmpCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _legacyCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static ManualLogSource _log; public static void Initialize(ManualLogSource log, string path) { _log = log; string text = Path.Combine(path, "customfonts"); if (!File.Exists(text)) { return; } AssetBundle val = AssetBundle.LoadFromFile(text); if (!((Object)(object)val == (Object)null)) { TMP_FontAsset[] array = val.LoadAllAssets(); foreach (TMP_FontAsset val2 in array) { string text2 = ((Object)val2).name.Trim(); _tmpCache[text2] = val2; _log.LogInfo((object)("Loaded TMP font: " + text2)); } Font[] array2 = val.LoadAllAssets(); foreach (Font val3 in array2) { string text3 = ((Object)val3).name.Trim(); _legacyCache[text3] = val3; _log.LogInfo((object)("Loaded Legacy font: " + text3)); } val.Unload(false); } } public static void TryReplace(TMP_Text text) { if (!((Object)(object)text == (Object)null) && _tmpCache.Count != 0) { string key = (((Object)(object)text.font != (Object)null) ? ((Object)text.font).name.Trim() : ""); if ((_tmpCache.TryGetValue(key, out TMP_FontAsset value) || _tmpCache.TryGetValue("default", out value)) && (Object)(object)text.font != (Object)(object)value) { text.font = value; } } } public static void TryReplace(Text text) { if (!((Object)(object)text == (Object)null) && _legacyCache != null && _legacyCache.Count != 0) { string key = (((Object)(object)text.font != (Object)null) ? ((Object)text.font).name.Trim() : ""); if ((_legacyCache.TryGetValue(key, out Font value) || _legacyCache.TryGetValue("default", out value)) && (Object)(object)text.font != (Object)(object)value) { text.font = value; } } } } public static class TextProcessor { [ThreadStatic] public static bool IsPatching; [ThreadStatic] private static bool _tmpNeedsFit; public static void Process(object component, ref string text) { if (IsPatching || component == null || string.IsNullOrWhiteSpace(text) || !Regex.IsMatch(text, "[a-zA-Zа-яА-ЯёЁ]") || TranslationEngine.IsAssignedTranslation(component, text)) { return; } if (!TranslationEngine.RegisterOriginal(component, text)) { TranslationEngine.SetAssignedTranslation(component, text); ReplaceFontSafe(component); } else if (TranslationEngine.IsEnabled) { string original = TranslationEngine.GetOriginal(component, text); string translation = TranslationEngine.GetTranslation(original); if (!(translation == original) && !(translation == text) && (!translation.Contains("{0}") || original.Contains("{0}"))) { text = translation; TranslationEngine.SetAssignedTranslation(component, text); _tmpNeedsFit = TranslationEngine.IsFitRequired(original.GetExactKey()); ReplaceFontSafe(component); } } } public static void PostProcess(object component) { if (!_tmpNeedsFit || component == null) { return; } _tmpNeedsFit = false; try { IsPatching = true; TMP_Text val = (TMP_Text)((component is TMP_Text) ? component : null); if (val != null) { if ((Object)(object)((Component)val).GetComponent() == (Object)null) { FitTMP(val); } return; } Text val2 = (Text)((component is Text) ? component : null); if (val2 != null) { FitLegacy(val2); return; } InputField val3 = (InputField)((component is InputField) ? component : null); if (val3 != null && (Object)(object)val3.textComponent != (Object)null) { FitLegacy(val3.textComponent); return; } TMP_InputField val4 = (TMP_InputField)((component is TMP_InputField) ? component : null); if (val4 != null && (Object)(object)val4.textComponent != (Object)null && (Object)(object)((Component)val4.textComponent).GetComponent() == (Object)null) { FitTMP(val4.textComponent); } } finally { IsPatching = false; } } private static void ReplaceFontSafe(object component) { TMP_Text val = (TMP_Text)((component is TMP_Text) ? component : null); if (val != null) { FontManager.TryReplace(val); return; } Text val2 = (Text)((component is Text) ? component : null); if (val2 != null) { FontManager.TryReplace(val2); return; } InputField val3 = (InputField)((component is InputField) ? component : null); if (val3 != null && (Object)(object)val3.textComponent != (Object)null) { FontManager.TryReplace(val3.textComponent); return; } TMP_InputField val4 = (TMP_InputField)((component is TMP_InputField) ? component : null); if (val4 != null && (Object)(object)val4.textComponent != (Object)null) { FontManager.TryReplace(val4.textComponent); } } public static void FitTMP(TMP_Text t) { if ((Object)(object)t == (Object)null) { return; } try { t.enableWordWrapping = false; ContentSizeFitter component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null) { component.horizontalFit = (FitMode)0; } if (!t.enableAutoSizing) { t.fontSizeMin = Mathf.Max(4f, t.fontSize * 0.4f); t.fontSizeMax = t.fontSize; t.enableAutoSizing = true; } t.overflowMode = (TextOverflowModes)0; t.ForceMeshUpdate(true, false); } catch { } } private static void FitLegacy(Text t) { if ((Object)(object)t == (Object)null) { return; } try { ContentSizeFitter component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null) { component.horizontalFit = (FitMode)0; } t.horizontalOverflow = (HorizontalWrapMode)0; t.verticalOverflow = (VerticalWrapMode)0; if (!t.resizeTextForBestFit) { t.resizeTextMinSize = Mathf.Max(1, Mathf.RoundToInt((float)t.fontSize * 0.4f)); t.resizeTextMaxSize = t.fontSize; t.resizeTextForBestFit = true; } } catch { } } } public static class TranslationEngine { private class TemplateEntry { public Regex CompiledRegex; public string TranslationTemplate; public string OriginalPattern; public List GroupPlaceholders; public string AnchorAnchorText; } private class SampleTemplateEntry { public string OriginalKey; public Regex CompiledRegex; public string TranslationTemplate; public List Placeholders; public bool IsAtomic; public bool IsLiteral; public string AnchorAnchorText; } private class SampleGroupEntry { public List Headers = new List(); public List Rules = new List(); } private class SampleREntry { public int RequiredGroupCount; public List MandatoryRules = new List(); public Dictionary> IndexedRules = new Dictionary>(); } private static string _lastPath = ""; private static readonly HashSet _allKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet _allValues = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _exact = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly List _templates = new List(); private static readonly ConditionalWeakTable _originals = new ConditionalWeakTable(); private static readonly HashSet _fitKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _cache = new Dictionary(StringComparer.Ordinal); private static readonly ConditionalWeakTable _assignedTranslations = new ConditionalWeakTable(); private static readonly List _sampleTemplates = new List(); private static readonly List _groupTemplates = new List(); private static readonly List _conditionalTemplates = new List(); public static bool IsEnabled { get; set; } = true; public static void Initialize(string path) { _lastPath = path; Load(); } public static void Load() { _exact.Clear(); _templates.Clear(); _allKeys.Clear(); _allValues.Clear(); _fitKeys.Clear(); _cache.Clear(); _sampleTemplates.Clear(); _groupTemplates.Clear(); _conditionalTemplates.Clear(); if (!Directory.Exists(_lastPath)) { Directory.CreateDirectory(_lastPath); } string[] files = Directory.GetFiles(_lastPath, "*.json"); foreach (string path in files) { if (Path.GetFileName(path).Equals("DumpedTexts.json", StringComparison.OrdinalIgnoreCase)) { continue; } try { JObject val = JObject.Parse(File.ReadAllText(path)); foreach (JProperty item in val.Properties()) { ProcessToken(item.Value, item.Name); } } catch { } } _sampleTemplates.Sort((SampleTemplateEntry a, SampleTemplateEntry b) => (a.IsAtomic != b.IsAtomic) ? a.IsAtomic.CompareTo(b.IsAtomic) : b.TranslationTemplate.Length.CompareTo(a.TranslationTemplate.Length)); Debug.Log((object)$"[WKTranslator] Loaded {_exact.Count} exact, {_templates.Count} templates, {_sampleTemplates.Count} samples, {_groupTemplates.Count} groups."); } private static void ProcessToken(JToken token, string keyName) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Invalid comparison between Unknown and I4 //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Invalid comparison between Unknown and I4 //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Invalid comparison between Unknown and I4 //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_042e: Invalid comparison between Unknown and I4 //IL_03d9: Unknown result type (might be due to invalid IL or missing references) if (keyName.StartsWith("sampleR", StringComparison.OrdinalIgnoreCase) && (int)token.Type == 1) { SampleREntry sampleREntry = new SampleREntry(); Match match = Regex.Match(keyName, "\\d+"); sampleREntry.RequiredGroupCount = (match.Success ? int.Parse(match.Value) : 2); foreach (JProperty item4 in ((JObject)token).Properties()) { string name = item4.Name; string text = ((object)item4.Value).ToString(); if (name.StartsWith("request:", StringComparison.OrdinalIgnoreCase)) { string exactKey = name.Substring(8).GetExactKey(); sampleREntry.MandatoryRules.Add(CreateSampleEntry(exactKey, text)); continue; } Match match2 = Regex.Match(name, "^request(\\d+):(.*)", RegexOptions.IgnoreCase); if (match2.Success) { int key = int.Parse(match2.Groups[1].Value); string exactKey2 = match2.Groups[2].Value.GetExactKey(); SampleTemplateEntry sampleTemplateEntry = CreateSampleEntry(exactKey2, text); if (!sampleREntry.IndexedRules.ContainsKey(key)) { sampleREntry.IndexedRules[key] = new List(); } sampleREntry.IndexedRules[key].Add(sampleTemplateEntry); if (!string.IsNullOrWhiteSpace(text)) { _allKeys.Add(sampleTemplateEntry.OriginalKey); } } } _conditionalTemplates.Add(sampleREntry); return; } if (keyName.StartsWith("sampleG:", StringComparison.OrdinalIgnoreCase) && (int)token.Type == 1) { SampleGroupEntry sampleGroupEntry = new SampleGroupEntry(); foreach (JProperty item5 in ((JObject)token).Properties()) { if (item5.Name.StartsWith("header:", StringComparison.OrdinalIgnoreCase)) { string exactKey3 = item5.Name.Substring(7).GetExactKey(); string item = (exactKey3.StartsWith("tag:", StringComparison.OrdinalIgnoreCase) ? exactKey3.Substring(4) : exactKey3); sampleGroupEntry.Headers.Add(item); if (!string.IsNullOrEmpty(((object)item5.Value).ToString())) { SampleTemplateEntry sampleTemplateEntry2 = CreateSampleEntry(exactKey3, ((object)item5.Value).ToString()); sampleGroupEntry.Rules.Add(sampleTemplateEntry2); _allKeys.Add(sampleTemplateEntry2.OriginalKey); } } else { SampleTemplateEntry sampleTemplateEntry3 = CreateSampleEntry(item5.Name, ((object)item5.Value).ToString()); sampleGroupEntry.Rules.Add(sampleTemplateEntry3); _allKeys.Add(sampleTemplateEntry3.OriginalKey); } } _groupTemplates.Add(sampleGroupEntry); return; } if (keyName.StartsWith("sample:", StringComparison.OrdinalIgnoreCase) && (int)token.Type == 1) { foreach (JProperty item6 in ((JObject)token).Properties()) { SampleTemplateEntry item2 = CreateSampleEntry(item6.Name, ((object)item6.Value).ToString()); _sampleTemplates.Add(item2); } return; } string exactKey4 = keyName.GetExactKey(); if (string.IsNullOrEmpty(exactKey4)) { return; } if ((int)token.Type == 1) { _allKeys.Add(exactKey4); { foreach (JProperty item7 in ((JObject)token).Properties()) { ProcessToken(item7.Value, item7.Name); } return; } } if ((int)token.Type != 8) { return; } string text2 = ((object)token).ToString().Trim(); if (string.IsNullOrEmpty(text2)) { return; } if (text2.StartsWith("sample:", StringComparison.OrdinalIgnoreCase)) { SampleTemplateEntry item3 = CreateSampleEntry(keyName, text2.Substring(7).Trim()); _sampleTemplates.Add(item3); return; } if (text2.StartsWith("fit:", StringComparison.OrdinalIgnoreCase)) { text2 = text2.Substring(4).Trim(); _fitKeys.Add(exactKey4); } _allKeys.Add(exactKey4); _allValues.Add(text2.GetExactKey()); if (Regex.IsMatch(exactKey4, "\\{#?\\d+\\}")) { AddTemplate(keyName, text2); } else { _exact[exactKey4] = text2; } } private static SampleTemplateEntry CreateSampleEntry(string key, string val) { string text = key.GetExactKey(); bool flag = false; if (text.StartsWith("tag:", StringComparison.OrdinalIgnoreCase)) { flag = true; text = text.Substring(4); } List list = new List(); foreach (Match item in Regex.Matches(text, "\\{(\\d+)\\}")) { list.Add(item.Groups[1].Value); } bool flag2 = list.Count == 0; string input = Regex.Escape(text); input = Regex.Replace(input, "\\\\\\{\\d+\\}", "(.+?)"); if (flag2 && !flag) { string text2 = (Regex.IsMatch(text, "^\\w") ? "(?<=^|\\W|\\\\n|\\\\r)" : ""); string text3 = (Regex.IsMatch(text, "\\w$") ? "(?=\\W|$)" : ""); input = text2 + input + text3; } string longestStaticChunk = GetLongestStaticChunk(text); return new SampleTemplateEntry { OriginalKey = text, CompiledRegex = new Regex(input, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant), TranslationTemplate = val, Placeholders = list, IsAtomic = flag2, IsLiteral = flag, AnchorAnchorText = longestStaticChunk }; } private static void AddTemplate(string key, string val) { try { string text = key.Trim(); List list = new List(); foreach (Match item in Regex.Matches(text, "\\{(#?\\d+)\\}")) { list.Add(item.Groups[1].Value); } string input = text; input = Regex.Replace(input, "\\{\\#\\d+\\}", "__KEYBINDGROUP__"); input = Regex.Replace(input, "\\{\\d+\\}", "__DIGITGROUP__"); string text2 = Regex.Escape(input); text2 = text2.Replace("\\\"", "[\"']?").Replace("\"", "[\"']?"); text2 = text2.Replace("\\'", "[\"']?").Replace("'", "[\"']?"); text2 = text2.Replace("__DIGITGROUP__", "([+-]?\\d+(?:[.,]\\d+)?)"); text2 = text2.Replace("__KEYBINDGROUP__", "([^\\n<>]+?)"); text2 = text2.Replace("\\n", "\\n").Replace("\\\\n", "(?:\\r?\\n|\\\\n)"); text2 = "^\\s*" + text2 + "\\s*$"; string longestStaticChunk = GetLongestStaticChunk(text); _templates.Add(new TemplateEntry { CompiledRegex = new Regex(text2, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant), TranslationTemplate = val, OriginalPattern = key.GetExactKey(), GroupPlaceholders = list, AnchorAnchorText = longestStaticChunk }); } catch (Exception ex) { Debug.LogError((object)("[TranslationEngine] Error key: " + key + ": " + ex.Message)); } } public static string GetTranslation(string currentText) { if (string.IsNullOrEmpty(currentText) || currentText.Length <= 1) { return currentText; } if (_cache.TryGetValue(currentText, out string value)) { return value; } string exactKey = currentText.GetExactKey(); if (_exact.TryGetValue(exactKey, out string value2)) { return _cache[currentText] = value2.Replace("\\n", "\n"); } string text2 = exactKey.Replace("\\n", "\n"); foreach (TemplateEntry template in _templates) { if (!string.IsNullOrEmpty(template.AnchorAnchorText) && text2.IndexOf(template.AnchorAnchorText, StringComparison.OrdinalIgnoreCase) == -1) { continue; } Match match = template.CompiledRegex.Match(text2); if (!match.Success) { continue; } bool flag = false; for (int i = 1; i < match.Groups.Count; i++) { if (match.Groups[i].Value.Contains("{") || match.Groups[i].Value.Contains("}")) { flag = true; break; } } if (flag) { continue; } string text3 = template.TranslationTemplate; for (int j = 1; j < match.Groups.Count; j++) { text3 = text3.Replace("{" + template.GroupPlaceholders[j - 1] + "}", match.Groups[j].Value); } if (_fitKeys.Contains(template.OriginalPattern)) { _fitKeys.Add(exactKey); } return _cache[currentText] = text3.Replace("\\n", "\n"); } string workingText = exactKey; bool anyTranslated = false; foreach (SampleREntry conditionalTemplate in _conditionalTemplates) { bool flag2 = true; foreach (SampleTemplateEntry mandatoryRule in conditionalTemplate.MandatoryRules) { if (!(mandatoryRule.IsLiteral ? (workingText.IndexOf(mandatoryRule.OriginalKey, StringComparison.OrdinalIgnoreCase) != -1) : mandatoryRule.CompiledRegex.IsMatch(workingText))) { flag2 = false; break; } } if (!flag2) { continue; } int num = 0; List list = new List(); foreach (KeyValuePair> indexedRule in conditionalTemplate.IndexedRules) { bool flag3 = false; foreach (SampleTemplateEntry item in indexedRule.Value) { if (item.IsLiteral ? (workingText.IndexOf(item.OriginalKey, StringComparison.OrdinalIgnoreCase) != -1) : item.CompiledRegex.IsMatch(workingText)) { flag3 = true; list.Add(item); } } if (flag3) { num++; } } if (num < conditionalTemplate.RequiredGroupCount) { continue; } foreach (SampleTemplateEntry mandatoryRule2 in conditionalTemplate.MandatoryRules) { workingText = ApplySampleReplacement(workingText, mandatoryRule2, ref anyTranslated); } list.Sort((SampleTemplateEntry a, SampleTemplateEntry b) => b.OriginalKey.Length.CompareTo(a.OriginalKey.Length)); foreach (SampleTemplateEntry item2 in list) { workingText = ApplySampleReplacement(workingText, item2, ref anyTranslated); } } foreach (SampleGroupEntry groupTemplate in _groupTemplates) { if (groupTemplate.Headers.Count <= 0 || !groupTemplate.Headers.Exists((string h) => workingText.IndexOf(h, StringComparison.OrdinalIgnoreCase) != -1)) { continue; } foreach (SampleTemplateEntry rule in groupTemplate.Rules) { workingText = ApplySampleReplacement(workingText, rule, ref anyTranslated); } } foreach (SampleTemplateEntry sampleTemplate in _sampleTemplates) { if (string.IsNullOrEmpty(sampleTemplate.AnchorAnchorText) || workingText.IndexOf(sampleTemplate.AnchorAnchorText, StringComparison.OrdinalIgnoreCase) != -1) { workingText = ApplySampleReplacement(workingText, sampleTemplate, ref anyTranslated); } } if (anyTranslated) { return _cache[currentText] = workingText.Replace("\\n", "\n"); } return _cache[currentText] = currentText; } public static void SetAssignedTranslation(object ui, string translatedText) { if (ui != null) { _assignedTranslations.Remove(ui); if (!string.IsNullOrEmpty(translatedText)) { _assignedTranslations.Add(ui, translatedText); } } } private static string ApplySampleReplacement(string input, SampleTemplateEntry sample, ref bool anyTranslated) { if (sample.IsLiteral) { if (input.IndexOf(sample.OriginalKey, StringComparison.OrdinalIgnoreCase) != -1) { string text = Regex.Replace(input, Regex.Escape(sample.OriginalKey), sample.TranslationTemplate.Replace("$", "$$"), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); if (text != input) { anyTranslated = true; return text; } } return input; } if (sample.IsAtomic) { if (input.IndexOf(sample.OriginalKey, StringComparison.OrdinalIgnoreCase) != -1) { string text2 = sample.CompiledRegex.Replace(input, delegate(Match m) { int num = m.Index - 1; if (num >= 0 && num < input.Length) { int num2 = input.LastIndexOf('<', num); int num3 = input.LastIndexOf('>', num); if (num2 > num3) { return m.Value; } int num4 = input.LastIndexOf('{', num); int num5 = input.LastIndexOf('}', num); if (num4 > num5) { return m.Value; } } return sample.TranslationTemplate; }); if (text2 != input) { anyTranslated = true; return text2; } } } else if (sample.CompiledRegex.IsMatch(input)) { string result = sample.CompiledRegex.Replace(input, delegate(Match match) { string text3 = sample.TranslationTemplate; for (int i = 1; i < match.Groups.Count; i++) { string text4 = sample.Placeholders[i - 1]; string newValue = match.Groups[i].Value.Trim(); text3 = text3.Replace("{" + text4 + "}", newValue); } return text3; }); anyTranslated = true; return result; } return input; } private static string GetLongestStaticChunk(string input) { if (string.IsNullOrEmpty(input)) { return string.Empty; } string input2 = Regex.Replace(input, "<[^>]+>", " "); input2 = Regex.Replace(input2, "\\{\\#?\\d+\\}", " "); input2 = input2.Replace("\\n", " ").Replace("", "").Replace("", ""); string[] array = input2.Split(new char[6] { ' ', '\t', '.', ',', '!', '?' }, StringSplitOptions.RemoveEmptyEntries); string text = string.Empty; string[] array2 = array; foreach (string text2 in array2) { if (text2.Length > text.Length) { text = text2; } } return (text.Length > 3) ? text : string.Empty; } public static bool IsTranslated(string text) { if (string.IsNullOrEmpty(text)) { return false; } string exactKey = text.GetExactKey(); return _allKeys.Contains(exactKey) || _exact.ContainsKey(exactKey) || _allValues.Contains(exactKey); } public static bool IsTranslatedDeep(string text) { if (IsTranslated(text)) { return true; } string exactKey = text.GetExactKey(); foreach (TemplateEntry template in _templates) { if (template.CompiledRegex.IsMatch(text) || template.OriginalPattern == exactKey) { return true; } } return false; } public static bool IsCyrillic(string text) { if (string.IsNullOrEmpty(text)) { return false; } return Regex.IsMatch(text, "[а-яА-ЯёЁ]"); } public static bool IsAssignedTranslation(object ui, string text) { if (ui == null || string.IsNullOrEmpty(text)) { return false; } if (_assignedTranslations.TryGetValue(ui, out string value)) { return value == text || value.GetExactKey() == text.GetExactKey(); } return false; } public static bool RegisterOriginal(object ui, string text) { if (ui == null || string.IsNullOrEmpty(text)) { return true; } string exactKey = text.GetExactKey(); if (_assignedTranslations.TryGetValue(ui, out string value) && value.GetExactKey() == exactKey) { return false; } if (_originals.TryGetValue(ui, out string value2) && value2.GetExactKey() == exactKey) { return true; } if (_allValues.Contains(exactKey) && !_allKeys.Contains(exactKey)) { return false; } _originals.Remove(ui); _originals.Add(ui, text); return true; } public static string GetOriginal(object ui, string current) { string value; return (ui != null && _originals.TryGetValue(ui, out value)) ? value : current; } public static bool IsFitRequired(string key) { return _fitKeys.Contains(key.GetExactKey()); } } }